@zerc20/wallet-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +26 -2
- package/dist/burnWorker.d.ts +1 -0
- package/dist/burnWorker.js +21 -0
- package/dist/burnWorker.js.map +1 -0
- package/dist/chain.d.ts +345 -0
- package/dist/chain.js +149 -0
- package/dist/chain.js.map +1 -0
- package/dist/context.d.ts +48 -0
- package/dist/context.js +40 -0
- package/dist/context.js.map +1 -0
- package/dist/core.d.ts +43 -0
- package/dist/core.js +106 -0
- package/dist/core.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +66 -0
- package/dist/network.js +55 -0
- package/dist/network.js.map +1 -0
- package/dist/pos.d.ts +93 -0
- package/dist/pos.js +302 -0
- package/dist/pos.js.map +1 -0
- package/dist/prove.d.ts +13 -0
- package/dist/prove.js +53 -0
- package/dist/prove.js.map +1 -0
- package/dist/rageExit.d.ts +5 -0
- package/dist/rageExit.js +146 -0
- package/dist/rageExit.js.map +1 -0
- package/dist/scan.d.ts +17 -0
- package/dist/scan.js +47 -0
- package/dist/scan.js.map +1 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.js +35 -0
- package/dist/seed.js.map +1 -0
- package/dist/servers.d.ts +60 -0
- package/dist/servers.js +117 -0
- package/dist/servers.js.map +1 -0
- package/dist/services.d.ts +62 -0
- package/dist/services.js +128 -0
- package/dist/services.js.map +1 -0
- package/dist/spend.d.ts +151 -0
- package/dist/spend.js +745 -0
- package/dist/spend.js.map +1 -0
- package/dist/state.d.ts +174 -0
- package/dist/state.js +45 -0
- package/dist/state.js.map +1 -0
- package/dist/sync.d.ts +84 -0
- package/dist/sync.js +615 -0
- package/dist/sync.js.map +1 -0
- package/dist/tree.d.ts +19 -0
- package/dist/tree.js +63 -0
- package/dist/tree.js.map +1 -0
- package/dist/wallet.d.ts +301 -0
- package/dist/wallet.js +992 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +60 -4
- package/src/burnWorker.ts +25 -0
- package/src/chain.ts +205 -0
- package/src/context.ts +84 -0
- package/src/core.ts +147 -0
- package/src/errors.ts +61 -0
- package/src/index.ts +41 -0
- package/src/network.ts +94 -0
- package/src/pos.ts +415 -0
- package/src/prove.ts +67 -0
- package/src/rageExit.ts +168 -0
- package/src/scan.ts +62 -0
- package/src/seed.ts +46 -0
- package/src/servers.ts +172 -0
- package/src/services.ts +184 -0
- package/src/snarkjs.d.ts +11 -0
- package/src/spend.ts +912 -0
- package/src/state.ts +211 -0
- package/src/sync.ts +698 -0
- package/src/tree.ts +108 -0
- package/src/wallet.ts +1297 -0
- package/wasm/zerc20_wallet_core.d.ts +176 -0
- package/wasm/zerc20_wallet_core.js +783 -0
- package/wasm/zerc20_wallet_core_bg.wasm +0 -0
- package/wasm/zerc20_wallet_core_bg.wasm.d.ts +32 -0
package/dist/pos.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// A point of sale with no key: it makes burn addresses for a merchant's wallet in the lane the wallet gave it
|
|
2
|
+
// (`Wallet.posKey`), records them there ahead of need, hands out one per sale, and follows what reaches each from what
|
|
3
|
+
// every wallet downloads. Stolen, it gives away which addresses are the merchant's, and nothing else: the merchant's
|
|
4
|
+
// wallet alone spends what they receive, merges it and withdraws it.
|
|
5
|
+
import { BURN_BLOCK, CanisterClient, lookupRecipient, writePosBlock } from '@zerc20/storage-sdk';
|
|
6
|
+
import { bytesToHex, getAddress, hexToBytes, isAddress, isHex, numberToHex, zeroAddress, } from 'viem';
|
|
7
|
+
import { Chain } from './chain.js';
|
|
8
|
+
import { core, initCore, posBurnAddress } from './core.js';
|
|
9
|
+
import { fail } from './errors.js';
|
|
10
|
+
import { checkNetwork } from './network.js';
|
|
11
|
+
import { scan } from './scan.js';
|
|
12
|
+
import { Asp, Indexer } from './servers.js';
|
|
13
|
+
import { registerDeposit } from './services.js';
|
|
14
|
+
import { applyHits, commitmentFields, commitmentTx, dropReorged, readDecisions, readTransfers } from './sync.js';
|
|
15
|
+
/** The longest a refill waits once a token's addresses run low: a write at a random time after the sale that drew
|
|
16
|
+
* them down, not right after it, so that the write's time does not mark that sale. */
|
|
17
|
+
const REFILL_WITHIN_MS = 10 * 60_000;
|
|
18
|
+
/** JSON with bigints, which `JSON` itself has no form for. */
|
|
19
|
+
const encode = (v) => new TextEncoder().encode(JSON.stringify(v, (_, x) => (typeof x === 'bigint' ? { $n: `${x}` } : x)));
|
|
20
|
+
const decode = (b) => JSON.parse(new TextDecoder().decode(b), (_, x) => x && typeof x === 'object' && typeof x.$n === 'string' ? BigInt(x.$n) : x);
|
|
21
|
+
const same = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
22
|
+
const pairOf = (token) => `${token.chainId}:${token.tokenId}`;
|
|
23
|
+
export class PointOfSale {
|
|
24
|
+
#network;
|
|
25
|
+
#state;
|
|
26
|
+
#persist;
|
|
27
|
+
#refillWithinMs;
|
|
28
|
+
#chains = new Map();
|
|
29
|
+
/** The refill running for each token, and the one waiting to. */
|
|
30
|
+
#fills = new Map();
|
|
31
|
+
#timers = new Map();
|
|
32
|
+
#storage;
|
|
33
|
+
#queue = Promise.resolve();
|
|
34
|
+
#saving = Promise.resolve();
|
|
35
|
+
constructor(network, state, persist, refillWithinMs) {
|
|
36
|
+
this.#network = network;
|
|
37
|
+
this.#state = state;
|
|
38
|
+
this.#persist = persist;
|
|
39
|
+
this.#refillWithinMs = refillWithinMs;
|
|
40
|
+
}
|
|
41
|
+
/** Opens the point of sale's saved state. It keeps which addresses it handed out: lose that, and it may hand one out
|
|
42
|
+
* twice. One state, one point of sale: two open on it would hand out the same addresses. */
|
|
43
|
+
static async open(opts) {
|
|
44
|
+
checkNetwork(opts.network);
|
|
45
|
+
await initCore();
|
|
46
|
+
const saved = await opts.persist.load();
|
|
47
|
+
const state = saved?.length ? decode(saved) : { connection: null, sales: [], seen: {}, txs: {} };
|
|
48
|
+
return new PointOfSale(opts.network, state, opts.persist, opts.refillWithinMs ?? REFILL_WITHIN_MS);
|
|
49
|
+
}
|
|
50
|
+
get connected() {
|
|
51
|
+
return this.#state.connection !== null;
|
|
52
|
+
}
|
|
53
|
+
/** Takes the key `merchant`'s wallet made for it (`Wallet.posKey`), and the public key `merchant` registered in
|
|
54
|
+
* storage, which every address it makes is under: whoever hands it a key cannot point sales at a key of its own. The
|
|
55
|
+
* same key again changes nothing. Another one starts over in that lane; the sales made so far are still followed. */
|
|
56
|
+
connect(merchant, laneKey) {
|
|
57
|
+
return this.#exclusive(async () => {
|
|
58
|
+
if (!isAddress(merchant, { strict: false }))
|
|
59
|
+
fail('invalid', `${String(merchant)} is not an address`);
|
|
60
|
+
if (!isHex(laneKey) || laneKey.length !== 66)
|
|
61
|
+
fail('invalid', 'the key is 32 bytes in hex');
|
|
62
|
+
const c = this.#state.connection;
|
|
63
|
+
if (c && same(c.merchant, merchant) && same(c.laneKey, laneKey))
|
|
64
|
+
return;
|
|
65
|
+
const recipient = await lookupRecipient(await this.#client(), hexToBytes(merchant));
|
|
66
|
+
if (!recipient)
|
|
67
|
+
fail('invalid', `${merchant} has not registered for private payments: open its wallet once`);
|
|
68
|
+
for (const t of this.#timers.values())
|
|
69
|
+
clearTimeout(t);
|
|
70
|
+
this.#timers.clear();
|
|
71
|
+
this.#fills.clear();
|
|
72
|
+
this.#state.connection = {
|
|
73
|
+
merchant: getAddress(merchant),
|
|
74
|
+
publicKey: bytesToHex(recipient.publicKey),
|
|
75
|
+
laneKey: laneKey.toLowerCase(),
|
|
76
|
+
slots: {},
|
|
77
|
+
pools: {},
|
|
78
|
+
};
|
|
79
|
+
await this.#save();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Readies addresses of `token` ahead of its sales: an app calls it for each token it takes, as it starts and after
|
|
83
|
+
* sales. Nothing happens while enough are ready, or a refill is under way or due. */
|
|
84
|
+
prepare(token) {
|
|
85
|
+
chainOf(this.#network, token.chainId);
|
|
86
|
+
this.#topUp(token);
|
|
87
|
+
}
|
|
88
|
+
/** How many addresses of `token` are recorded and wait for a sale. */
|
|
89
|
+
ready(token) {
|
|
90
|
+
return this.#state.connection?.pools[pairOf(token)]?.ready.length ?? 0;
|
|
91
|
+
}
|
|
92
|
+
/** A new sale of `token`, at an address no sale has had: one recorded ahead, or, when none is left, one recorded
|
|
93
|
+
* now. With `via: 'deposit'` it also registers a deposit address in front of it (`registerDeposit`). */
|
|
94
|
+
async next(opts) {
|
|
95
|
+
const { token } = opts;
|
|
96
|
+
const c = chainOf(this.#network, token.chainId);
|
|
97
|
+
this.#connection();
|
|
98
|
+
const { commitmentsFinal: from } = await new Indexer(c.indexer).status();
|
|
99
|
+
let burnAddress;
|
|
100
|
+
while (!(burnAddress = this.#connection().pools[pairOf(token)]?.ready.shift()))
|
|
101
|
+
await this.#fill(token);
|
|
102
|
+
// saved as taken before the deposit relayer is asked: a sale that fails half-way never hands it out again
|
|
103
|
+
const sale = {
|
|
104
|
+
id: burnAddress,
|
|
105
|
+
chainId: token.chainId,
|
|
106
|
+
tokenId: token.tokenId,
|
|
107
|
+
token: token.address,
|
|
108
|
+
burnAddress,
|
|
109
|
+
depositAddress: null,
|
|
110
|
+
depositFee: null,
|
|
111
|
+
refundTo: null,
|
|
112
|
+
amount: opts.amount ?? null,
|
|
113
|
+
createdMs: Date.now(),
|
|
114
|
+
};
|
|
115
|
+
const watch = { key: tag(token.tokenId, burnAddress), from, scannedTo: from };
|
|
116
|
+
this.#state.sales.push(sale);
|
|
117
|
+
this.#state.seen[burnAddress] = { address: burnAddress, watches: [{ tokenId: token.tokenId, watch }], found: [] };
|
|
118
|
+
await this.#save();
|
|
119
|
+
this.#topUp(token);
|
|
120
|
+
return opts.via === 'deposit' ? this.#exclusive(() => this.#register(sale, opts.refundTo)) : sale;
|
|
121
|
+
}
|
|
122
|
+
/** Registers a deposit address in front of sale `id`, for the underlying asset; the deposit relayer watches it for 7
|
|
123
|
+
* days. What it cannot wrap (too little, another token) and a rage exit go back to `refundTo`, the merchant's
|
|
124
|
+
* account if absent: name the payer's, so that neither the merchant's account is on chain nor a payment the ASP
|
|
125
|
+
* rejects comes back to the merchant. A sale has one: asked again with the same `refundTo`, it is the same. */
|
|
126
|
+
registerDeposit(id, opts = {}) {
|
|
127
|
+
return this.#exclusive(() => this.#register(this.#sale(id), opts.refundTo));
|
|
128
|
+
}
|
|
129
|
+
async #register(sale, refundTo) {
|
|
130
|
+
const to = refundTo ?? this.#connection().merchant;
|
|
131
|
+
if (!isAddress(to, { strict: false }) || same(to, zeroAddress))
|
|
132
|
+
return fail('invalid', `${to} is not an address to refund to`);
|
|
133
|
+
if (sale.depositAddress) {
|
|
134
|
+
if (same(sale.refundTo, to))
|
|
135
|
+
return sale;
|
|
136
|
+
return fail('invalid', `sale ${sale.id} already has a deposit address, refunding to ${sale.refundTo}`);
|
|
137
|
+
}
|
|
138
|
+
const { depositAddress, fee } = await registerDeposit(this.#chain(sale.chainId), sale.token, sale.burnAddress, to);
|
|
139
|
+
Object.assign(sale, { depositAddress, depositFee: fee, refundTo: to });
|
|
140
|
+
await this.#save();
|
|
141
|
+
return sale;
|
|
142
|
+
}
|
|
143
|
+
sales() {
|
|
144
|
+
return [...this.#state.sales];
|
|
145
|
+
}
|
|
146
|
+
/** What reached each of sales `ids`: every transfer to its burn address, with where each stands. One pass per chain
|
|
147
|
+
* for them all, reading only what every wallet downloads, so no server learns which addresses it asks about. A chain
|
|
148
|
+
* that fails keeps what was read before, and its error is returned. */
|
|
149
|
+
refresh(ids) {
|
|
150
|
+
return this.#exclusive(async () => {
|
|
151
|
+
const sales = [...new Map(ids.map((id) => [id.toLowerCase(), this.#sale(id)])).values()];
|
|
152
|
+
const errors = [];
|
|
153
|
+
const chains = [...new Set(sales.map((s) => s.chainId))];
|
|
154
|
+
await Promise.all(chains.map((chainId) => this.#refreshChain(chainId, sales.filter((s) => s.chainId === chainId)).catch((e) => void errors.push(e))));
|
|
155
|
+
await this.#save();
|
|
156
|
+
return { sales: sales.map((s) => this.#statusOf(s)), errors };
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** What reached sale `id`, and how much is final. */
|
|
160
|
+
async status(id) {
|
|
161
|
+
const { sales, errors } = await this.refresh([id]);
|
|
162
|
+
if (errors.length > 0)
|
|
163
|
+
throw errors[0];
|
|
164
|
+
return sales[0];
|
|
165
|
+
}
|
|
166
|
+
/** What reached sale `id` as of the last `refresh`, asking no server. */
|
|
167
|
+
lastStatus(id) {
|
|
168
|
+
return this.#statusOf(this.#sale(id));
|
|
169
|
+
}
|
|
170
|
+
async #refreshChain(chainId, sales) {
|
|
171
|
+
const c = chainOf(this.#network, chainId);
|
|
172
|
+
const indexer = new Indexer(c.indexer);
|
|
173
|
+
const seen = sales.map((s) => this.#state.seen[s.id]);
|
|
174
|
+
const status = await indexer.status();
|
|
175
|
+
const watches = seen.flatMap((r) => r.watches.map((w) => w.watch));
|
|
176
|
+
const hits = await scan(watches, 4, (p) => indexer.page('tags', p), status.commitmentsFinal, status.commitments);
|
|
177
|
+
applyHits([], seen, hits);
|
|
178
|
+
dropReorged(seen, status.commitmentsFinal);
|
|
179
|
+
await readTransfers(commitmentFields(indexer), seen);
|
|
180
|
+
await readDecisions(new Asp(c.asp), seen);
|
|
181
|
+
// a final payment's transaction is kept; until then it is read again, as a reorg may move what sits at a position
|
|
182
|
+
const txs = this.#state.txs;
|
|
183
|
+
await Promise.all(seen
|
|
184
|
+
.flatMap((r) => r.found)
|
|
185
|
+
.map(async (f) => {
|
|
186
|
+
const at = `${chainId}:${f.index}`;
|
|
187
|
+
if (!f.tentative && txs[at])
|
|
188
|
+
return;
|
|
189
|
+
const tx = await commitmentTx(this.#chain(chainId), indexer, f.index).catch(() => undefined);
|
|
190
|
+
if (tx === undefined)
|
|
191
|
+
return;
|
|
192
|
+
if (tx?.hash)
|
|
193
|
+
txs[at] = tx.hash;
|
|
194
|
+
else
|
|
195
|
+
delete txs[at];
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
#statusOf(sale) {
|
|
199
|
+
const payments = this.#state.seen[sale.id].found
|
|
200
|
+
.filter((f) => f.amount !== null)
|
|
201
|
+
.map((f) => ({
|
|
202
|
+
amount: f.amount,
|
|
203
|
+
sender: f.sender,
|
|
204
|
+
state: f.tentative ? 'confirming' : f.asp === null ? 'screening' : f.asp,
|
|
205
|
+
txHash: this.#state.txs[`${sale.chainId}:${f.index}`] ?? null,
|
|
206
|
+
}));
|
|
207
|
+
const received = payments.filter((p) => p.state !== 'confirming').reduce((s, p) => s + p.amount, 0n);
|
|
208
|
+
return { sale, payments, received };
|
|
209
|
+
}
|
|
210
|
+
/** Refills `token`'s addresses once they run low: right away when none is left, otherwise at a random time within
|
|
211
|
+
* `refillWithinMs`. */
|
|
212
|
+
#topUp(token) {
|
|
213
|
+
const pair = pairOf(token);
|
|
214
|
+
const left = this.#state.connection?.pools[pair]?.ready.length ?? 0;
|
|
215
|
+
if (!this.#state.connection || left >= BURN_BLOCK || this.#fills.has(pair) || this.#timers.has(pair))
|
|
216
|
+
return;
|
|
217
|
+
// a refill that fails is tried again by the next sale, which tells why
|
|
218
|
+
const fill = () => void this.#fill(token).catch(() => undefined);
|
|
219
|
+
if (left === 0)
|
|
220
|
+
return fill();
|
|
221
|
+
const timer = setTimeout(() => {
|
|
222
|
+
this.#timers.delete(pair);
|
|
223
|
+
fill();
|
|
224
|
+
}, Math.random() * this.#refillWithinMs);
|
|
225
|
+
timer.unref?.(); // a process need not stay up for it
|
|
226
|
+
this.#timers.set(pair, timer);
|
|
227
|
+
}
|
|
228
|
+
/** Makes `BURN_BLOCK` more addresses of `token`, records them in the lane, then adds them to its pool: one refill at
|
|
229
|
+
* a time per token. One that fails changes nothing. */
|
|
230
|
+
#fill(token) {
|
|
231
|
+
const pair = pairOf(token);
|
|
232
|
+
const running = this.#fills.get(pair);
|
|
233
|
+
if (running)
|
|
234
|
+
return running;
|
|
235
|
+
clearTimeout(this.#timers.get(pair));
|
|
236
|
+
this.#timers.delete(pair);
|
|
237
|
+
const run = (async () => {
|
|
238
|
+
const c = this.#connection();
|
|
239
|
+
const pool = (c.pools[pair] ??= { next: 0, ready: [] });
|
|
240
|
+
const k = pool.next;
|
|
241
|
+
const laneKey = hexToBytes(c.laneKey);
|
|
242
|
+
const burns = await Promise.all(Array.from({ length: BURN_BLOCK }, (_, i) => posBurnAddress(laneKey, token.chainId, token.tokenId, k + i, c.publicKey)));
|
|
243
|
+
const block = {
|
|
244
|
+
chainId: token.chainId,
|
|
245
|
+
tokenId: token.tokenId,
|
|
246
|
+
first: k,
|
|
247
|
+
salts: burns.map((b) => hexToBytes(b.salt)),
|
|
248
|
+
};
|
|
249
|
+
const chain = `${token.chainId}`;
|
|
250
|
+
const slot = await writePosBlock(await this.#client(), laneKey, block, c.slots[chain] ?? 0);
|
|
251
|
+
if (this.#state.connection !== c)
|
|
252
|
+
return; // connected to another lane meanwhile
|
|
253
|
+
c.slots[chain] = Math.max(c.slots[chain] ?? 0, slot);
|
|
254
|
+
pool.ready.push(...burns.map((b) => b.address));
|
|
255
|
+
pool.next = k + BURN_BLOCK;
|
|
256
|
+
await this.#save();
|
|
257
|
+
})().finally(() => {
|
|
258
|
+
if (this.#fills.get(pair) === run)
|
|
259
|
+
this.#fills.delete(pair);
|
|
260
|
+
});
|
|
261
|
+
this.#fills.set(pair, run);
|
|
262
|
+
return run;
|
|
263
|
+
}
|
|
264
|
+
#connection() {
|
|
265
|
+
return this.#state.connection ?? fail('invalid', "not connected: connect the merchant's wallet first");
|
|
266
|
+
}
|
|
267
|
+
#sale(id) {
|
|
268
|
+
return this.#state.sales.find((s) => same(s.id, id)) ?? fail('invalid', `no sale ${id}`);
|
|
269
|
+
}
|
|
270
|
+
#chain(chainId) {
|
|
271
|
+
let c = this.#chains.get(chainId);
|
|
272
|
+
if (!c)
|
|
273
|
+
this.#chains.set(chainId, (c = new Chain(chainOf(this.#network, chainId))));
|
|
274
|
+
return c;
|
|
275
|
+
}
|
|
276
|
+
/** Storage, connected on first use: a point of sale that only follows its sales needs none. */
|
|
277
|
+
#client() {
|
|
278
|
+
const { host, canisterId } = this.#network.storage;
|
|
279
|
+
return (this.#storage ??= CanisterClient.connect(host, canisterId).catch((e) => {
|
|
280
|
+
this.#storage = undefined;
|
|
281
|
+
throw e;
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
/** Runs `f` after everything queued before it. */
|
|
285
|
+
#exclusive(f) {
|
|
286
|
+
const run = this.#queue.then(f);
|
|
287
|
+
this.#queue = run.catch(() => undefined);
|
|
288
|
+
return run;
|
|
289
|
+
}
|
|
290
|
+
/** Saves the state as it is when the save runs, after every save asked for before: an older state never lands last,
|
|
291
|
+
* which would hand out again an address taken since. */
|
|
292
|
+
#save() {
|
|
293
|
+
const run = this.#saving.then(() => this.#persist.save(encode(this.#state)));
|
|
294
|
+
this.#saving = run.catch(() => undefined);
|
|
295
|
+
return run;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function chainOf(network, chainId) {
|
|
299
|
+
return network.chains.find((c) => c.chainId === chainId) ?? fail('invalid', `the network has no chain ${chainId}`);
|
|
300
|
+
}
|
|
301
|
+
const tag = (tokenId, address) => numberToHex(core.transferTag(tokenId, address), { size: 4 });
|
|
302
|
+
//# sourceMappingURL=pos.js.map
|
package/dist/pos.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pos.js","sourceRoot":"","sources":["../src/pos.ts"],"names":[],"mappings":"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"}
|
package/dist/prove.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Circuit } from './network.js';
|
|
2
|
+
/** A URL's bytes; `file:` URLs from disk, for Node. */
|
|
3
|
+
export declare function readUrl(url: string): Promise<Uint8Array>;
|
|
4
|
+
/** `Verifier.Groth16Proof`, with pB in the order the Solidity verifier takes. */
|
|
5
|
+
export interface Groth16 {
|
|
6
|
+
pA: [bigint, bigint];
|
|
7
|
+
pB: [[bigint, bigint], [bigint, bigint]];
|
|
8
|
+
pC: [bigint, bigint];
|
|
9
|
+
}
|
|
10
|
+
export declare function prove(circuit: Circuit, input: object): Promise<{
|
|
11
|
+
proof: Groth16;
|
|
12
|
+
signals: bigint[];
|
|
13
|
+
}>;
|
package/dist/prove.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Groth16 proofs of JoinSplit and RageExit with snarkjs, on the user's device: the witness holds the private keys.
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
4
|
+
import { fail } from './errors.js';
|
|
5
|
+
/** A URL's bytes; `file:` URLs from disk, for Node. */
|
|
6
|
+
export async function readUrl(url) {
|
|
7
|
+
if (url.startsWith('file:')) {
|
|
8
|
+
const fs = 'node:fs/promises';
|
|
9
|
+
const { readFile } = (await import(/* @vite-ignore */ fs));
|
|
10
|
+
return readFile(new URL(url));
|
|
11
|
+
}
|
|
12
|
+
const res = await fetch(url);
|
|
13
|
+
if (!res.ok)
|
|
14
|
+
return fail('unavailable', `${url}: HTTP ${res.status}`);
|
|
15
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
16
|
+
}
|
|
17
|
+
const artifacts = new Map();
|
|
18
|
+
/** The artifact at `url`, checked against its pinned SHA-256: a swapped proving key could leak the witness. */
|
|
19
|
+
function artifact(url, sha) {
|
|
20
|
+
let p = artifacts.get(url);
|
|
21
|
+
if (!p) {
|
|
22
|
+
p = readUrl(url).then((bytes) => {
|
|
23
|
+
if (bytesToHex(sha256(bytes)) !== sha.toLowerCase())
|
|
24
|
+
fail('invalid', `${url} does not match its SHA-256`);
|
|
25
|
+
return bytes;
|
|
26
|
+
});
|
|
27
|
+
p.catch(() => artifacts.delete(url));
|
|
28
|
+
artifacts.set(url, p);
|
|
29
|
+
}
|
|
30
|
+
return p;
|
|
31
|
+
}
|
|
32
|
+
export async function prove(circuit, input) {
|
|
33
|
+
const [wasm, zkey] = await Promise.all([
|
|
34
|
+
artifact(circuit.wasm, circuit.wasmSha256),
|
|
35
|
+
artifact(circuit.zkey, circuit.zkeySha256),
|
|
36
|
+
]);
|
|
37
|
+
// loaded on the first proof: an app that only reads balances need not carry snarkjs
|
|
38
|
+
const { groth16 } = await import('snarkjs');
|
|
39
|
+
const { proof, publicSignals } = await groth16.fullProve(input, { type: 'mem', data: wasm }, { type: 'mem', data: zkey });
|
|
40
|
+
const b = (x) => BigInt(x);
|
|
41
|
+
return {
|
|
42
|
+
proof: {
|
|
43
|
+
pA: [b(proof.pi_a[0]), b(proof.pi_a[1])],
|
|
44
|
+
pB: [
|
|
45
|
+
[b(proof.pi_b[0][1]), b(proof.pi_b[0][0])],
|
|
46
|
+
[b(proof.pi_b[1][1]), b(proof.pi_b[1][0])],
|
|
47
|
+
],
|
|
48
|
+
pC: [b(proof.pi_c[0]), b(proof.pi_c[1])],
|
|
49
|
+
},
|
|
50
|
+
signals: publicSignals.map(b),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=prove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prove.js","sourceRoot":"","sources":["../src/prove.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
import { type Ctx } from './context.js';
|
|
3
|
+
import type { DepositRecord } from './state.js';
|
|
4
|
+
/** Refunds the deposit at `index` of `record` to its sender, from the account's own wallet (it pays the gas). */
|
|
5
|
+
export declare function rageExit(ctx: Ctx, record: DepositRecord, index: number): Promise<Hex[]>;
|
package/dist/rageExit.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Rage exit: a burn deposit refunded to its sender with nothing but the chain and the indexer's public pages, for
|
|
2
|
+
// when the ASP, the attester or root proving stop. The ledger authenticates the deposit itself, by re-hashing the
|
|
3
|
+
// commitments from it up to a checkpoint; a long way is walked back one transaction of commitments at a time.
|
|
4
|
+
import { decodeEventLog, numberToHex, parseAbi } from 'viem';
|
|
5
|
+
import { depositAddressAbi, ledgerAbi, verifierAbi } from './chain.js';
|
|
6
|
+
import { chainCtx, spendKey, walletSees } from './context.js';
|
|
7
|
+
import { core } from './core.js';
|
|
8
|
+
import { fail, guard } from './errors.js';
|
|
9
|
+
import { prove } from './prove.js';
|
|
10
|
+
import { PAGE } from './servers.js';
|
|
11
|
+
/** Commitments per transaction: about a thousand fit in the 128 KB transaction size limit. */
|
|
12
|
+
const SEGMENT = 900;
|
|
13
|
+
const checkpointEvent = parseAbi(['event CheckpointRecorded(uint64 indexed index, uint256 hashChain)']);
|
|
14
|
+
/** Commitments `[from, to)` and the hash chain before `from`, from the indexer's pages. */
|
|
15
|
+
async function commitments(c, from, to) {
|
|
16
|
+
const list = [];
|
|
17
|
+
let prev = null;
|
|
18
|
+
for (let p = Math.floor(from / PAGE); p * PAGE < to; p++) {
|
|
19
|
+
const { hashChainBefore, entries } = await c.indexer.commitments(p);
|
|
20
|
+
let chain = hashChainBefore;
|
|
21
|
+
for (let j = 0; j < entries.length / 128; j++) {
|
|
22
|
+
const i = p * PAGE + j;
|
|
23
|
+
if (i >= to)
|
|
24
|
+
break;
|
|
25
|
+
const fields = [0, 1, 2, 3].map((f) => BigInt(`0x${hexOf(entries.subarray(j * 128 + 32 * f, j * 128 + 32 * (f + 1)))}`));
|
|
26
|
+
if (i === from)
|
|
27
|
+
prev = chain;
|
|
28
|
+
if (i >= from)
|
|
29
|
+
list.push(fields);
|
|
30
|
+
chain = core.hashChain(chain, fields.map((x) => numberToHex(x, { size: 32 })));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (prev === null || list.length !== to - from)
|
|
34
|
+
return fail('unavailable', `the indexer does not serve commitments ${from}..${to} yet`);
|
|
35
|
+
return { prev, list };
|
|
36
|
+
}
|
|
37
|
+
const hexOf = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
|
|
38
|
+
/** The first index after `index` the ledger has a checkpoint at: a proved root's size, else a fresh reservation. */
|
|
39
|
+
async function checkpointAfter(ctx, c, index, send) {
|
|
40
|
+
const ledger = await c.chain.ledger();
|
|
41
|
+
for (let p = 0;; p++) {
|
|
42
|
+
const roots = await c.indexer.roots(p);
|
|
43
|
+
const after = roots.find((r) => r.size > index);
|
|
44
|
+
if (after) {
|
|
45
|
+
const [, exists] = await c.chain.client.readContract({
|
|
46
|
+
address: ledger,
|
|
47
|
+
abi: ledgerAbi,
|
|
48
|
+
functionName: 'hashChainAt',
|
|
49
|
+
args: [BigInt(after.size)],
|
|
50
|
+
});
|
|
51
|
+
if (exists)
|
|
52
|
+
return after.size;
|
|
53
|
+
}
|
|
54
|
+
if (roots.length < PAGE)
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const receipt = await send(ledger, ledgerAbi, 'reserveHashChain', []);
|
|
58
|
+
for (const log of receipt.logs) {
|
|
59
|
+
try {
|
|
60
|
+
const ev = decodeEventLog({ abi: checkpointEvent, data: log.data, topics: log.topics });
|
|
61
|
+
return Number(ev.args.index);
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
}
|
|
65
|
+
return fail('unavailable', 'reserveHashChain recorded no checkpoint');
|
|
66
|
+
}
|
|
67
|
+
/** Refunds the deposit at `index` of `record` to its sender, from the account's own wallet (it pays the gas). */
|
|
68
|
+
export async function rageExit(ctx, record, index) {
|
|
69
|
+
const c = chainCtx(ctx, record.chainId);
|
|
70
|
+
const found = record.found.find((f) => f.index === index) ?? fail('invalid', `no deposit at ${index}`);
|
|
71
|
+
if (found.spent)
|
|
72
|
+
fail('invalid', 'the deposit is already spent');
|
|
73
|
+
const wc = ctx.walletClient;
|
|
74
|
+
const account = wc.account;
|
|
75
|
+
if ((await wc.getChainId()) !== Number(record.chainId))
|
|
76
|
+
await guard(wc.switchChain({ id: Number(record.chainId) }));
|
|
77
|
+
const hashes = [];
|
|
78
|
+
const send = async (address, abi, functionName, args) => {
|
|
79
|
+
const hash = await guard(wc.writeContract({ account, chain: null, address, abi: abi, functionName: functionName, args: args }));
|
|
80
|
+
hashes.push(hash);
|
|
81
|
+
const receipt = await c.chain.client.waitForTransactionReceipt({ hash });
|
|
82
|
+
if (receipt.status !== 'success')
|
|
83
|
+
fail('unavailable', `${functionName} reverted`);
|
|
84
|
+
await walletSees(wc, hash);
|
|
85
|
+
return receipt;
|
|
86
|
+
};
|
|
87
|
+
let end = await checkpointAfter(ctx, c, index, send);
|
|
88
|
+
const { prev, list } = await commitments(c, index, end);
|
|
89
|
+
const ledger = await c.chain.ledger();
|
|
90
|
+
// walk a long way back first: each step records the chain before its segment as a new checkpoint
|
|
91
|
+
while (end - index > SEGMENT) {
|
|
92
|
+
const start = end - SEGMENT;
|
|
93
|
+
const before = list.slice(0, start - index).reduce((chain, f) => core.hashChain(chain, f.map((x) => numberToHex(x, { size: 32 }))), prev);
|
|
94
|
+
await send(ledger, ledgerAbi, 'checkpointBefore', [BigInt(end), BigInt(before), list.slice(start - index)]);
|
|
95
|
+
end = start;
|
|
96
|
+
}
|
|
97
|
+
const segment = list.slice(0, end - index);
|
|
98
|
+
// checked first, so a wrong page from the indexer costs no gas
|
|
99
|
+
await c.chain.client.readContract({
|
|
100
|
+
address: ledger,
|
|
101
|
+
abi: ledgerAbi,
|
|
102
|
+
functionName: 'verifySegment',
|
|
103
|
+
args: [BigInt(end), BigInt(prev), segment],
|
|
104
|
+
});
|
|
105
|
+
const key = spendKey(ctx);
|
|
106
|
+
const { proof, signals } = await prove(ctx.network.circuits.rageexit, {
|
|
107
|
+
chain_id: `${record.chainId}`,
|
|
108
|
+
commitment_index: `${index}`,
|
|
109
|
+
burn_address: BigInt(record.address).toString(),
|
|
110
|
+
private_key: key.secret,
|
|
111
|
+
auth_key_hash: key.authKeyHash,
|
|
112
|
+
burn_salt: record.salt,
|
|
113
|
+
});
|
|
114
|
+
const nullifier = signals[0];
|
|
115
|
+
await send(c.config.verifier, verifierAbi, 'rageExit', [
|
|
116
|
+
{ checkpointIndex: BigInt(end), prevHashChain: BigInt(prev), commitments: segment, nullifier, proof },
|
|
117
|
+
]);
|
|
118
|
+
found.spent = true;
|
|
119
|
+
// a deposit address's refund lands on the address itself; forward it to the address's refund address: this account,
|
|
120
|
+
// or for a point of sale's sale the payer's or the merchant's. A point of sale registers its deposit addresses without
|
|
121
|
+
// telling this wallet, and a record restored from storage no longer knows its own, so the sender is checked on chain.
|
|
122
|
+
const token = (await c.chain.tokens()).find((t) => t.tokenId === found.tokenId);
|
|
123
|
+
// the deposit's commitment, which the ledger just authenticated: `tag, sender, burn address, amount`
|
|
124
|
+
const [, from, , amount] = list[0];
|
|
125
|
+
const sender = numberToHex(from, { size: 20 });
|
|
126
|
+
const refundTo = await c.chain.depositAddressRefund(sender, token.address, record.address);
|
|
127
|
+
// for every device of the wallet, written by the next sync; kept now, before the forward, which may fail
|
|
128
|
+
ctx.state.receipts.push({
|
|
129
|
+
kind: 'returned',
|
|
130
|
+
chainId: record.chainId,
|
|
131
|
+
tokenId: found.tokenId,
|
|
132
|
+
burnAddress: record.address.toLowerCase(),
|
|
133
|
+
index,
|
|
134
|
+
recipient: (refundTo ?? sender).toLowerCase(),
|
|
135
|
+
amount,
|
|
136
|
+
txHash: hashes.at(-1),
|
|
137
|
+
atMs: Date.now(),
|
|
138
|
+
unwritten: true,
|
|
139
|
+
});
|
|
140
|
+
await ctx.save();
|
|
141
|
+
if (refundTo)
|
|
142
|
+
await send(sender, depositAddressAbi, 'refund', [token.address]);
|
|
143
|
+
await ctx.save();
|
|
144
|
+
return hashes;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=rageExit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rageExit.js","sourceRoot":"","sources":["../src/rageExit.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,kHAAkH;AAClH,8GAA8G;AAE9G,OAAO,EAA0B,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAA2B,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGpC,8FAA8F;AAC9F,MAAM,OAAO,GAAG,GAAG,CAAC;AAEpB,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,mEAAmE,CAAC,CAAC,CAAC;AAIxG,2FAA2F;AAC3F,KAAK,UAAU,WAAW,CAAC,CAAW,EAAE,IAAY,EAAE,EAAU;IAC9D,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,IAAI,GAAe,IAAI,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QACzD,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,KAAK,GAAG,eAAe,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,IAAI,EAAE;gBAAE,MAAM;YACnB,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,MAAM,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CACvE,CAAC;YACZ,IAAI,CAAC,KAAK,IAAI;gBAAE,IAAI,GAAG,KAAK,CAAC;YAC7B,IAAI,CAAC,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC,SAAS,CACpB,KAAK,EACL,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CACzC,CAAC;QACX,CAAC;IACH,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE,GAAG,IAAI;QAC5C,OAAO,IAAI,CAAC,aAAa,EAAE,0CAA0C,IAAI,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1F,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAa,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEhG,oHAAoH;AACpH,KAAK,UAAU,eAAe,CAAC,GAAQ,EAAE,CAAW,EAAE,KAAa,EAAE,IAAY;IAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAI,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;QAChD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC;gBACnD,OAAO,EAAE,MAAM;gBACf,GAAG,EAAE,SAAS;gBACd,YAAY,EAAE,aAAa;gBAC3B,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,MAAM;gBAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAChC,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI;YAAE,MAAM;IACjC,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACtE,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,cAAc,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxF,OAAO,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC,aAAa,EAAE,yCAAyC,CAAC,CAAC;AACxE,CAAC;AASD,iHAAiH;AACjH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAQ,EAAE,MAAqB,EAAE,KAAa;IAC3E,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,iBAAiB,KAAK,EAAE,CAAC,CAAC;IACvG,IAAI,KAAK,CAAC,KAAK;QAAE,IAAI,CAAC,SAAS,EAAE,8BAA8B,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC;IAC5B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAQ,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;QAAE,MAAM,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACpH,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,MAAM,IAAI,GAAW,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;QAC9D,MAAM,IAAI,GAAG,MAAM,KAAK,CACtB,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAY,EAAE,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAC/G,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,aAAa,EAAE,GAAG,YAAY,WAAW,CAAC,CAAC;QAClF,MAAM,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,OAAgB,CAAC;IAC1B,CAAC,CAAC;IAEF,IAAI,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACtC,iGAAiG;IACjG,OAAO,GAAG,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,GAAG,OAAO,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAChD,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CACX,IAAI,CAAC,SAAS,CACZ,KAAK,EACL,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CACpC,EACV,IAAI,CACL,CAAC;QACF,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5G,GAAG,GAAG,KAAK,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,CAAC;IAC3C,+DAA+D;IAC/D,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC;QAChC,OAAO,EAAE,MAAM;QACf,GAAG,EAAE,SAAS;QACd,YAAY,EAAE,eAAe;QAC7B,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;KAC3C,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE;QACpE,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE;QAC7B,gBAAgB,EAAE,GAAG,KAAK,EAAE;QAC5B,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE;QAC/C,WAAW,EAAE,GAAG,CAAC,MAAM;QACvB,aAAa,EAAE,GAAG,CAAC,WAAW;QAC9B,SAAS,EAAE,MAAM,CAAC,IAAI;KACvB,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE;QACrD,EAAE,eAAe,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;KACtG,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IACnB,oHAAoH;IACpH,uHAAuH;IACvH,sHAAsH;IACtH,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAE,CAAC;IACjF,qGAAqG;IACrG,MAAM,CAAC,EAAE,IAAI,EAAE,AAAD,EAAG,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3F,yGAAyG;IACzG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QACtB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,EAAS;QAChD,KAAK;QACL,SAAS,EAAE,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,WAAW,EAAS;QACpD,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE;QACtB,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAChB,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACjB,IAAI,QAAQ;QAAE,MAAM,IAAI,CAAC,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/E,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACjB,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/scan.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
/** A key to look for: entries at or after `from`, and not yet looked at before `scannedTo`. */
|
|
3
|
+
export interface Watch {
|
|
4
|
+
key: Hex;
|
|
5
|
+
from: number;
|
|
6
|
+
/** Everything in `[from, scannedTo)` has been looked at. */
|
|
7
|
+
scannedTo: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Hit {
|
|
10
|
+
key: Hex;
|
|
11
|
+
position: number;
|
|
12
|
+
/** Past the final count, so a reorg may still move or drop it. */
|
|
13
|
+
tentative: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Looks for `watches` (updated in place) in entries up to `final`, and, without recording that, in
|
|
16
|
+
* `[final, live)` too. `page(p)` returns page `p` of `width`-byte entries. */
|
|
17
|
+
export declare function scan(watches: Watch[], width: number, page: (p: number) => Promise<Uint8Array>, final: number, live?: number): Promise<Hit[]>;
|
package/dist/scan.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Finding a wallet's own entries in the indexer's append-only lists without naming them: download pages, match
|
|
2
|
+
// locally. One scanner serves both the 4-byte commitment tags and the 8-byte nullifier prefixes.
|
|
3
|
+
//
|
|
4
|
+
// Each watched key remembers how far it has been looked for, so a key learned late (a payment announced after
|
|
5
|
+
// its commitment landed) is looked for once over the range it missed, and everything else only over new
|
|
6
|
+
// entries. Pages below the final count never change, so re-reading them hits the HTTP cache or the CDN.
|
|
7
|
+
import { bytesToHex } from 'viem';
|
|
8
|
+
import { PAGE } from './servers.js';
|
|
9
|
+
/** Looks for `watches` (updated in place) in entries up to `final`, and, without recording that, in
|
|
10
|
+
* `[final, live)` too. `page(p)` returns page `p` of `width`-byte entries. */
|
|
11
|
+
export async function scan(watches, width, page, final, live = final) {
|
|
12
|
+
for (const w of watches)
|
|
13
|
+
w.scannedTo = Math.max(w.scannedTo, w.from);
|
|
14
|
+
const pending = watches.filter((w) => w.scannedTo < live);
|
|
15
|
+
if (pending.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
const byKey = new Map();
|
|
18
|
+
for (const w of pending)
|
|
19
|
+
byKey.set(w.key, [...(byKey.get(w.key) ?? []), w]);
|
|
20
|
+
// a loop, not Math.min(...): spreading 100k+ watches overflows the stack
|
|
21
|
+
let start = live;
|
|
22
|
+
for (const w of pending)
|
|
23
|
+
start = Math.min(start, w.scannedTo);
|
|
24
|
+
const hits = [];
|
|
25
|
+
for (let p = Math.floor(start / PAGE); p * PAGE < live; p++) {
|
|
26
|
+
const bytes = await page(p);
|
|
27
|
+
const first = p * PAGE;
|
|
28
|
+
const end = Math.min(first + bytes.length / width, live);
|
|
29
|
+
for (let i = Math.max(first, start); i < end; i++) {
|
|
30
|
+
const key = bytesToHex(bytes.subarray((i - first) * width, (i - first + 1) * width));
|
|
31
|
+
for (const w of byKey.get(key) ?? []) {
|
|
32
|
+
if (i >= w.scannedTo)
|
|
33
|
+
hits.push({ key, position: i, tentative: i >= final });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// a short page means the indexer has less than `live` (a stale copy): stop, and look again next time
|
|
37
|
+
if (first + bytes.length / width < Math.min(first + PAGE, live)) {
|
|
38
|
+
for (const w of pending)
|
|
39
|
+
w.scannedTo = Math.max(w.scannedTo, Math.min(final, first + bytes.length / width));
|
|
40
|
+
return hits;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const w of pending)
|
|
44
|
+
w.scannedTo = Math.max(w.scannedTo, final);
|
|
45
|
+
return hits;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=scan.js.map
|
package/dist/scan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
|