@zerc20/storage-sdk 0.0.0-stage → 0.2.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/src/formats.ts ADDED
@@ -0,0 +1,399 @@
1
+ // What every blob holds, and the ids and keys of the slots that hold them.
2
+ //
3
+ // A plaintext is `type(1) ‖ body ‖ zeros`, padded to the one length its blob has: every blob is `BLOB_LEN` bytes, so
4
+ // its kind does not show. Each type has one fixed body. A reader takes the body and ignores what follows it, so a
5
+ // later version can append a field that an older reader passes over; a record an older reader must not misread takes
6
+ // a new type, which that reader drops.
7
+
8
+ import { bech32m } from '@scure/base';
9
+ import { compare, concat, kdf, readU32, readU64, taggedHash, u32be, u64be, unhex } from './crypto.js';
10
+ import { BLOB_LEN } from './wire.js';
11
+
12
+ /** A sealed slot's plaintext: the blob less the nonce and the tag. */
13
+ export const SLOT_PLAIN = BLOB_LEN - 12 - 16;
14
+ /** An inbox announcement's plaintext: also less the ephemeral key. */
15
+ export const ANNOUNCE_PLAIN = BLOB_LEN - 32 - 12 - 16;
16
+ /** The most salts a burn block holds. */
17
+ export const BURN_BLOCK = 6;
18
+ /** The BN254 scalar field modulus `Fr`, big-endian. */
19
+ export const FR_BE = unhex('30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001');
20
+
21
+ /** Record types, the first byte of every plaintext. */
22
+ export const NOTE = 0x01;
23
+ export const CODE_NOTE = 0x02;
24
+ export const SELF_NOTE = 0x03;
25
+ export const DEPOSIT = 0x04;
26
+ export const BURN_BLOCK_RECORD = 0x05;
27
+ export const RETURNED = 0x06;
28
+ export const DEPOSIT_ADDRESS = 0x07;
29
+ export const POS_LANE = 0x08;
30
+ export const SENT_TO_ADDRESS = 0x10;
31
+ export const SENT_TO_CODE = 0x11;
32
+ export const CHANNEL = 0x20;
33
+ export const RECEIVE_CODE = 0x40;
34
+
35
+ // ---- ids and keys ----
36
+
37
+ /** The inbox announcements of channels to `address` go to, under its registration's notify key: another
38
+ * registration, another inbox, so payers then open fresh channels. */
39
+ export const addressInbox = (address: Uint8Array, notifyKey: Uint8Array): Uint8Array =>
40
+ taggedHash('zerc20/storage/inbox/v1', [address, notifyKey]);
41
+
42
+ /** The payer's seed for its channel to `inbox`, recomputable from the payer's view key alone. */
43
+ export const channelSeed = (seed: Uint8Array, payer: Uint8Array, inbox: Uint8Array): Uint8Array =>
44
+ kdf(seed, 'channel', [payer, inbox]);
45
+
46
+ /** A channel's lane key. Binding the inbox and the claimed payer means a seed learned from one channel opens
47
+ * nothing when replayed into another inbox or under another name. */
48
+ export const channelKey = (channelSeed: Uint8Array, inbox: Uint8Array, payer: Uint8Array): Uint8Array =>
49
+ kdf(channelSeed, 'channel-key', [inbox, payer]);
50
+
51
+ /** The lane key of receive code `index` (burn index `index` of `tokenId` on `chainId`). Every payer of the code
52
+ * writes its notes to this one lane, and the owner finds them through the log: no inbox, no announcement. */
53
+ export const codeKey = (seed: Uint8Array, chainId: bigint, tokenId: number, index: number): Uint8Array =>
54
+ kdf(seed, 'code', [u64be(chainId), u32be(tokenId), u32be(index)]);
55
+
56
+ /** The owner's own lane key: notes to self (merge outputs and change) and burn blocks, which only the owner's
57
+ * devices write and read. */
58
+ export const ownKey = (seed: Uint8Array): Uint8Array => kdf(seed, 'own');
59
+
60
+ /** The lane key of the owner's point of sale `n`, handed to that point of sale alone: it records there the burn
61
+ * addresses it makes for the owner, ahead of handing them out. */
62
+ export const posKey = (seed: Uint8Array, n: number): Uint8Array => kdf(seed, 'pos', [u32be(n)]);
63
+
64
+ /** The slot of record `slotIndex` on `chainId` in the lane of `laneKey`. */
65
+ export const slotId = (laneKey: Uint8Array, chainId: bigint, slotIndex: number): Uint8Array =>
66
+ kdf(laneKey, 'slot', [u64be(chainId), u32be(slotIndex)]);
67
+ export const sealKey = (laneKey: Uint8Array): Uint8Array => kdf(laneKey, 'seal');
68
+
69
+ export const outgoingId = (seed: Uint8Array, index: number): Uint8Array => kdf(seed, 'outgoing-id', [u32be(index)]);
70
+ export const outgoingKey = (seed: Uint8Array): Uint8Array => kdf(seed, 'outgoing');
71
+
72
+ // ---- records ----
73
+
74
+ /** What an on-chain note commits to besides its owner's `public_key`:
75
+ * `note_hash = Poseidon(chain_id, public_key, value, token_id, note_salt)`. */
76
+ export interface NoteOpening {
77
+ chainId: bigint;
78
+ tokenId: number;
79
+ /** Big-endian, below 2^248 (its first byte is 0). */
80
+ value: Uint8Array;
81
+ /** Big-endian canonical `Fr` element, fresh for every note. */
82
+ noteSalt: Uint8Array;
83
+ }
84
+
85
+ /** What a payment, withdrawal or merge was, kept on its note to self (its change, even of zero value, or a merge's
86
+ * output), so that every device of the owner shows it: that note on chain says it landed. */
87
+ export interface Receipt {
88
+ kind: 'sent' | 'sent-to-code' | 'withdrawn' | 'unwrapped' | 'merged';
89
+ /** Big-endian, below 2^248, as a note's value: what was paid or withdrawn, or what a merge's note holds. */
90
+ amount: Uint8Array;
91
+ /** The same: what the relayer was paid for it. */
92
+ fee: Uint8Array;
93
+ /** Whom it paid: an address, a receive code's burn address, or for a merge 20 zero bytes. */
94
+ counterparty: Uint8Array;
95
+ }
96
+
97
+ /** What the owner did that leaves no note to self, kept in the own lane so that every device of the owner shows it:
98
+ * a deposit into its burn address returned to its sender (a rage exit), or a deposit address it registered for one. */
99
+ export type OwnReceipt = { chainId: bigint; tokenId: number; burnAddress: Uint8Array } & (
100
+ | {
101
+ kind: 'returned';
102
+ /** The deposit's `commitment_index`. */
103
+ index: number;
104
+ recipient: Uint8Array;
105
+ /** Big-endian, below 2^248. */
106
+ amount: Uint8Array;
107
+ txHash: Uint8Array;
108
+ }
109
+ | { kind: 'deposit-address'; depositAddress: Uint8Array }
110
+ );
111
+
112
+ /** Where a record sat in its lane, and when the canister first stored it (kept in the clear). */
113
+ interface Stamped {
114
+ /** Position in the lane's sequence for its chain (0, 1, 2, …), which locates its slot. Not the on-chain
115
+ * `commitment_index`; slots that do not open leave gaps. */
116
+ slotIndex: number;
117
+ atNs: bigint;
118
+ }
119
+
120
+ export interface Note extends Stamped {
121
+ kind: 'note';
122
+ opening: NoteOpening;
123
+ /** In a receive code's lane, the payer it claims to be (unverified). */
124
+ payer?: Uint8Array;
125
+ /** In the own lane, what it is the change or the output of; missing for a kind this version does not know. */
126
+ receipt?: Receipt;
127
+ }
128
+
129
+ /** Not a note on chain: a deposit of `value` into the receiver's burn address of `burnSalt`, under the `public_key`
130
+ * the payer looked up (zERC20-v2's `burn_address`), paid by a plain transfer. */
131
+ export interface Deposit extends Stamped {
132
+ kind: 'deposit';
133
+ chainId: bigint;
134
+ tokenId: number;
135
+ /** Big-endian, below 2^248, as a note's. */
136
+ value: Uint8Array;
137
+ burnSalt: Uint8Array;
138
+ /** The burn address, which the receiver derives itself: for the payer's other devices, to find the transfer. */
139
+ burnAddress: Uint8Array;
140
+ }
141
+
142
+ /** Burn addresses `first, first + 1, ..` of `tokenId` on `chainId`, by their salts, which their owner may have handed
143
+ * out. Whoever hands one out records its block first, so every device of the owner finds the payments to it. In the
144
+ * own lane they are zERC20-v2's `keys::burn` indices; in a point of sale's lane, its `keys::pos_burn` counters. Salts,
145
+ * because deriving an address takes about 2^16 hashes, and checking one from its salt takes one. */
146
+ export interface BurnBlock {
147
+ chainId: bigint;
148
+ tokenId: number;
149
+ first: number;
150
+ /** 32 bytes each, 1 to `BURN_BLOCK` of them. */
151
+ salts: Uint8Array[];
152
+ }
153
+
154
+ /** In the own lane: the owner made point of sale `n`, whose lane (`posKey`) its devices then read. */
155
+ export interface PosLane {
156
+ n: number;
157
+ }
158
+
159
+ export type LaneRecord =
160
+ | Note
161
+ | Deposit
162
+ | (BurnBlock & Stamped & { kind: 'burn-block' })
163
+ | (OwnReceipt & Stamped)
164
+ | (PosLane & Stamped & { kind: 'pos-lane' });
165
+
166
+ /** Which lane a record was read from: the records each holds, and who writes it. */
167
+ export type LaneKind = 'channel' | 'code' | 'own' | 'pos';
168
+
169
+ /** The lanes each record type belongs in. A code's notes name their payer, and only the own lane's say what they are
170
+ * the change or the output of. */
171
+ const LANES: Record<number, LaneKind[]> = {
172
+ [NOTE]: ['channel'],
173
+ [CODE_NOTE]: ['code'],
174
+ [SELF_NOTE]: ['own'],
175
+ [DEPOSIT]: ['channel'],
176
+ [BURN_BLOCK_RECORD]: ['own', 'pos'],
177
+ [RETURNED]: ['own'],
178
+ [DEPOSIT_ADDRESS]: ['own'],
179
+ [POS_LANE]: ['own'],
180
+ };
181
+
182
+ /** `type ‖ body`, zero-padded to `size`. */
183
+ function record(type: number, body: Uint8Array, size = SLOT_PLAIN): Uint8Array {
184
+ const out = new Uint8Array(size);
185
+ out[0] = type;
186
+ out.set(body, 1);
187
+ return out;
188
+ }
189
+
190
+ /** A value's 31 bytes on the wire, or `null` if it is not below 2^248. */
191
+ function valueBytes(value: Uint8Array): Uint8Array | null {
192
+ return value.length === 32 && value[0] === 0 ? value.subarray(1) : null;
193
+ }
194
+
195
+ const isField = (x: Uint8Array): boolean => x.length === 32 && compare(x, FR_BE) < 0;
196
+
197
+ const RECEIPTS: Receipt['kind'][] = ['sent', 'sent-to-code', 'withdrawn', 'unwrapped', 'merged'];
198
+
199
+ /** A value back from its 31 bytes on the wire. */
200
+ const wideValue = (b: Uint8Array): Uint8Array => concat(Uint8Array.of(0), b);
201
+
202
+ /** `kind(1) ‖ amount(31) ‖ fee(31) ‖ counterparty(20)`, or `null` if a field is out of range. */
203
+ function receiptBytes(r: Receipt): Uint8Array | null {
204
+ const [amount, fee] = [valueBytes(r.amount), valueBytes(r.fee)];
205
+ if (!amount || !fee || !RECEIPTS.includes(r.kind) || r.counterparty.length !== 20) return null;
206
+ return concat(Uint8Array.of(RECEIPTS.indexOf(r.kind) + 1), amount, fee, r.counterparty);
207
+ }
208
+
209
+ /** The receipt in `b`, or `undefined` for a kind this version does not know: the note, which may be spendable change,
210
+ * is kept all the same. */
211
+ function readReceipt(b: Uint8Array): Receipt | undefined {
212
+ const kind = RECEIPTS[b[0] - 1];
213
+ return (
214
+ kind && {
215
+ kind,
216
+ amount: wideValue(b.subarray(1, 32)),
217
+ fee: wideValue(b.subarray(32, 63)),
218
+ counterparty: b.slice(63, 83),
219
+ }
220
+ );
221
+ }
222
+
223
+ /** A note's plaintext, `token_id(4) ‖ value(31) ‖ note_salt(32)`, or `null` if a field is out of the circuit's range
224
+ * (an out-of-range field can hash like a different value). With `payer` it is a note to a receive code, naming its
225
+ * payer; with `receipt`, a note to self, saying what it is the change or the output of. */
226
+ export function notePlaintext(
227
+ opening: NoteOpening,
228
+ extra: { payer: Uint8Array } | { receipt: Receipt } | null = null,
229
+ ): Uint8Array | null {
230
+ const value = valueBytes(opening.value);
231
+ if (!value || !isField(opening.noteSalt)) return null;
232
+ const body = concat(u32be(opening.tokenId), value, opening.noteSalt);
233
+ if (!extra) return record(NOTE, body);
234
+ if ('payer' in extra) return extra.payer.length === 20 ? record(CODE_NOTE, concat(body, extra.payer)) : null;
235
+ const receipt = receiptBytes(extra.receipt);
236
+ return receipt && record(SELF_NOTE, concat(body, receipt));
237
+ }
238
+
239
+ /** A deposit's announcement, `token_id(4) ‖ value(31) ‖ burn_salt(32) ‖ burn_address(20)`, or `null` if the value or
240
+ * the salt is out of range. */
241
+ export function depositPlaintext(
242
+ tokenId: number,
243
+ value: Uint8Array,
244
+ burnSalt: Uint8Array,
245
+ burnAddress: Uint8Array,
246
+ ): Uint8Array | null {
247
+ const v = valueBytes(value);
248
+ if (!v || !isField(burnSalt) || burnAddress.length !== 20) return null;
249
+ return record(DEPOSIT, concat(u32be(tokenId), v, burnSalt, burnAddress));
250
+ }
251
+
252
+ /** `token_id(4) ‖ burn_address(20) ‖` then a return's `index(8) ‖ recipient(20) ‖ amount(31) ‖ tx_hash(32)`, or a
253
+ * deposit address's `deposit_address(20)`. */
254
+ export function ownReceiptPlaintext(r: OwnReceipt): Uint8Array {
255
+ const amount = r.kind === 'returned' && valueBytes(r.amount);
256
+ const rest =
257
+ r.kind === 'returned'
258
+ ? amount &&
259
+ r.recipient.length === 20 &&
260
+ r.txHash.length === 32 &&
261
+ concat(u64be(BigInt(r.index)), r.recipient, amount, r.txHash)
262
+ : r.depositAddress.length === 20 && r.depositAddress;
263
+ if (!rest || r.burnAddress.length !== 20)
264
+ throw new Error('an own receipt holds 20-byte addresses, a 32-byte hash and a value below 2^248');
265
+ return record(r.kind === 'returned' ? RETURNED : DEPOSIT_ADDRESS, concat(u32be(r.tokenId), r.burnAddress, rest));
266
+ }
267
+
268
+ /** `token_id(4) ‖ first(4) ‖ count(1) ‖ salt(32) × count`. */
269
+ export function burnBlockPlaintext(b: BurnBlock): Uint8Array {
270
+ if (b.salts.length === 0 || b.salts.length > BURN_BLOCK || b.salts.some((s) => s.length !== 32)) {
271
+ throw new Error(`a burn block holds 1 to ${BURN_BLOCK} salts of 32 bytes`);
272
+ }
273
+ return record(BURN_BLOCK_RECORD, concat(u32be(b.tokenId), u32be(b.first), Uint8Array.of(b.salts.length), ...b.salts));
274
+ }
275
+
276
+ /** `n(4)`. */
277
+ export function posLanePlaintext(n: number): Uint8Array {
278
+ return record(POS_LANE, u32be(n));
279
+ }
280
+
281
+ /** The record a slot of a `lane` lane on `chainId` held, or `null` if it is malformed or not one such a lane holds. */
282
+ export function readLaneRecord(
283
+ lane: LaneKind,
284
+ chainId: bigint,
285
+ slotIndex: number,
286
+ atNs: bigint,
287
+ plain: Uint8Array,
288
+ ): LaneRecord | null {
289
+ const type = plain[0];
290
+ if (plain.length !== SLOT_PLAIN || !LANES[type]?.includes(lane)) return null;
291
+ const stamp = { slotIndex, atNs };
292
+ const tokenId = readU32(plain, 1);
293
+ if (type === BURN_BLOCK_RECORD) {
294
+ const count = plain[9];
295
+ if (count === 0 || count > BURN_BLOCK) return null;
296
+ const salts = Array.from({ length: count }, (_, i) => plain.slice(10 + 32 * i, 42 + 32 * i));
297
+ return { kind: 'burn-block', ...stamp, chainId, tokenId, first: readU32(plain, 5), salts };
298
+ }
299
+ if (type === POS_LANE) return { kind: 'pos-lane', ...stamp, n: readU32(plain, 1) };
300
+ if (type === RETURNED || type === DEPOSIT_ADDRESS) {
301
+ const head = { ...stamp, chainId, tokenId, burnAddress: plain.slice(5, 25) };
302
+ if (type === DEPOSIT_ADDRESS) return { ...head, kind: 'deposit-address', depositAddress: plain.slice(25, 45) };
303
+ const [recipient, amount, txHash] = [plain.slice(33, 53), wideValue(plain.subarray(53, 84)), plain.slice(84, 116)];
304
+ return { ...head, kind: 'returned', index: Number(readU64(plain, 25)), recipient, amount, txHash };
305
+ }
306
+ // a note or a deposit
307
+ const value = wideValue(plain.subarray(5, 36));
308
+ const salt = plain.slice(36, 68);
309
+ if (!isField(salt)) return null;
310
+ if (type === DEPOSIT)
311
+ return { kind: 'deposit', ...stamp, chainId, tokenId, value, burnSalt: salt, burnAddress: plain.slice(68, 88) };
312
+ const note: Note = { kind: 'note', ...stamp, opening: { chainId, tokenId, value, noteSalt: salt } };
313
+ if (type === CODE_NOTE) note.payer = plain.slice(68, 88);
314
+ const receipt = type === SELF_NOTE && readReceipt(plain.subarray(68, 151));
315
+ if (receipt) note.receipt = receipt;
316
+ return note;
317
+ }
318
+
319
+ /** Where to pay: handed from receiver to payer over a trusted channel. It does not name the receiver, but whoever
320
+ * swaps it in transit redirects the funds. Pay any amount, by a transfer to `burnAddress` (a deposit), or by a note
321
+ * owned by `publicKey` written to the lane of `key`. */
322
+ export interface ReceiveCode {
323
+ chainId: bigint;
324
+ tokenId: number;
325
+ /** zERC20-v2's `keys::burn` address of the code's index. */
326
+ burnAddress: Uint8Array;
327
+ /** The zERC20 public key the paying note commits to: the code's own (`keys::code_secret`), not the address's, so
328
+ * codes cannot be linked to each other or to the address. */
329
+ publicKey: Uint8Array;
330
+ key: Uint8Array;
331
+ }
332
+
333
+ const codeBody = (c: ReceiveCode): Uint8Array => {
334
+ if (c.burnAddress.length !== 20 || c.publicKey.length !== 32 || c.key.length !== 32)
335
+ throw new Error('not a receive code');
336
+ return concat(u64be(c.chainId), u32be(c.tokenId), c.burnAddress, c.publicKey, c.key);
337
+ };
338
+
339
+ const readCodeBody = (b: Uint8Array): ReceiveCode => ({
340
+ chainId: readU64(b, 0),
341
+ tokenId: readU32(b, 8),
342
+ burnAddress: b.slice(12, 32),
343
+ publicKey: b.slice(32, 64),
344
+ key: b.slice(64, 96),
345
+ });
346
+
347
+ const CODE_PREFIX = 'zerc20';
348
+
349
+ /** A receive code as text: bech32m of `RECEIVE_CODE ‖ chain_id(8) ‖ token_id(4) ‖ burn_address(20) ‖ public_key(32) ‖
350
+ * key(32)`, so a mistyped one fails its checksum instead of paying someone else. */
351
+ export function encodeCode(c: ReceiveCode): string {
352
+ return bech32m.encode(CODE_PREFIX, bech32m.toWords(concat(Uint8Array.of(RECEIVE_CODE), codeBody(c))), false);
353
+ }
354
+
355
+ /** The receive code `text` holds, or `null` for anything else. */
356
+ export function decodeCode(text: string): ReceiveCode | null {
357
+ try {
358
+ const { prefix, bytes } = bech32m.decodeToBytes(text.trim(), false);
359
+ if (prefix !== CODE_PREFIX || bytes.length !== 97 || bytes[0] !== RECEIVE_CODE) return null;
360
+ return readCodeBody(bytes.subarray(1));
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+
366
+ /** Where a payer paid, as its outgoing record keeps it: an address under one of its registrations (its notify key),
367
+ * or a receive code. */
368
+ export type Target = { address: Uint8Array; notifyKey: Uint8Array } | ReceiveCode;
369
+
370
+ /** `address(20) ‖ notify_key(32)`, or the code's body. */
371
+ export function targetPlaintext(t: Target): Uint8Array {
372
+ return 'address' in t ? record(SENT_TO_ADDRESS, concat(t.address, t.notifyKey)) : record(SENT_TO_CODE, codeBody(t));
373
+ }
374
+
375
+ export function readTarget(plain: Uint8Array): Target | null {
376
+ if (plain.length !== SLOT_PLAIN) return null;
377
+ if (plain[0] === SENT_TO_ADDRESS) return { address: plain.slice(1, 21), notifyKey: plain.slice(21, 53) };
378
+ if (plain[0] === SENT_TO_CODE) return readCodeBody(plain.subarray(1, 97));
379
+ return null;
380
+ }
381
+
382
+ /** A channel's announcement, `payer(20) ‖ channel_seed(32)`: who claims to open it, and the seed its lane key derives
383
+ * from. */
384
+ export function channelPlaintext(payer: Uint8Array, seed: Uint8Array): Uint8Array {
385
+ return record(CHANNEL, concat(payer, seed), ANNOUNCE_PLAIN);
386
+ }
387
+
388
+ export function readChannel(plain: Uint8Array): { payer: Uint8Array; seed: Uint8Array } | null {
389
+ if (plain.length !== ANNOUNCE_PLAIN || plain[0] !== CHANNEL) return null;
390
+ return { payer: plain.slice(1, 21), seed: plain.slice(21, 53) };
391
+ }
392
+
393
+ /** How records reach a receiver: through a channel to its address, a receive code's lane, or the lane of one of its
394
+ * points of sale. The code's owner knows it by its token and burn index; a payer by the code itself. */
395
+ export type Via =
396
+ | { type: 'address' }
397
+ | { type: 'code'; chainId: bigint; tokenId: number; index: number }
398
+ | { type: 'code'; code: ReceiveCode }
399
+ | { type: 'pos'; n: number };
package/src/index.ts ADDED
@@ -0,0 +1,86 @@
1
+ // zerc20-storage SDK: private note delivery for zERC20 on the Internet Computer.
2
+ //
3
+ // - View keys (`registeredViewKey`): an address's key, derived from its zERC20 root seed. Everything only the owner can
4
+ // compute derives from it. Registering (one wallet signature) publishes what payers need: the address's zERC20
5
+ // public key and the key announcements are encrypted to, which payers look up with `lookupRecipient`. It also
6
+ // escrows the view key to the auditor.
7
+ // - History (`refresh`): both sides recover their channels and records from the view key alone, and each refresh
8
+ // reads only what is new. Keep the state with `exportState`.
9
+ // - Payments (`planSend`, `planDeposit`, `planPayCode`, and `planSelf` for merge outputs and change, with what each
10
+ // was), submitted with `commit`; `planReceipt` for what leaves no note to self.
11
+ // - Burn blocks (`planBurnBlock`): which burn addresses the owner's devices hand out. Each of a wallet's own is also
12
+ // a receive code (`encodeCode`), paid by a deposit or by a note, any amount, any number of times.
13
+ // - Points of sale (`planPosLane`): a lane whose key (`posKey`) the owner hands to one point of sale, which records
14
+ // there the burn addresses it hands out (`writePosBlock`), for every device of the owner to find.
15
+ //
16
+ // Payers are not verified: a record's `peer` is only what was claimed.
17
+
18
+ export type { Action, AuditRecord, AuditRequest, CanisterError, Kind, Registration, Service, Slot } from './candid.js';
19
+ export { idlFactory } from './candid.js';
20
+ export { CanisterClient, StorageError } from './canister.js';
21
+ export {
22
+ BURN_BLOCK,
23
+ FR_BE,
24
+ addressInbox,
25
+ codeKey,
26
+ decodeCode,
27
+ encodeCode,
28
+ posKey,
29
+ type BurnBlock,
30
+ type Deposit,
31
+ type LaneRecord,
32
+ type Note,
33
+ type NoteOpening,
34
+ type OwnReceipt,
35
+ type PosLane,
36
+ type Receipt,
37
+ type ReceiveCode,
38
+ type Via,
39
+ } from './formats.js';
40
+ export {
41
+ commit,
42
+ nextPosLane,
43
+ planBurnBlock,
44
+ planDeposit,
45
+ planPayCode,
46
+ planPosLane,
47
+ planReceipt,
48
+ planSelf,
49
+ planSend,
50
+ writePosBlock,
51
+ } from './send.js';
52
+ export {
53
+ type ChainLane,
54
+ type NewRecord,
55
+ type StoredLane,
56
+ type SyncState,
57
+ emptyState,
58
+ exportState,
59
+ importState,
60
+ refresh,
61
+ } from './sync.js';
62
+ export {
63
+ type Recipient,
64
+ type Signer,
65
+ type ViewKey,
66
+ eip191Signer,
67
+ lookupRecipient,
68
+ notifyKey,
69
+ notifySecret,
70
+ openEscrow,
71
+ registeredViewKey,
72
+ register,
73
+ verifyRegistration,
74
+ viewKey,
75
+ } from './viewKey.js';
76
+ export {
77
+ REGISTER_POW_BITS,
78
+ actionsDigest,
79
+ auditMessage,
80
+ powOk,
81
+ registerDigest,
82
+ registerMessage,
83
+ requiredPowBits,
84
+ solvePow,
85
+ vetkdInput,
86
+ } from './wire.js';
@@ -0,0 +1,6 @@
1
+ // solvePow's search, in a Web Worker: off the page's thread, it never needs to yield.
2
+
3
+ import { findNonce } from './wire.js';
4
+
5
+ onmessage = async ({ data }: MessageEvent<{ digest: Uint8Array; bits: number }>) =>
6
+ postMessage(await findNonce(data.digest, data.bits, Infinity));