@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/LICENSE +24 -0
- package/README.md +14 -2
- package/dist/candid.d.ts +73 -0
- package/dist/candid.js +51 -0
- package/dist/candid.js.map +1 -0
- package/dist/canister.d.ts +33 -0
- package/dist/canister.js +76 -0
- package/dist/canister.js.map +1 -0
- package/dist/crypto.d.ts +27 -0
- package/dist/crypto.js +99 -0
- package/dist/crypto.js.map +1 -0
- package/dist/formats.d.ts +202 -0
- package/dist/formats.js +238 -0
- package/dist/formats.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/powWorker.d.ts +1 -0
- package/dist/powWorker.js +4 -0
- package/dist/powWorker.js.map +1 -0
- package/dist/send.d.ts +51 -0
- package/dist/send.js +162 -0
- package/dist/send.js.map +1 -0
- package/dist/sync.d.ts +82 -0
- package/dist/sync.js +263 -0
- package/dist/sync.js.map +1 -0
- package/dist/viewKey.d.ts +45 -0
- package/dist/viewKey.js +106 -0
- package/dist/viewKey.js.map +1 -0
- package/dist/wire.d.ts +33 -0
- package/dist/wire.js +112 -0
- package/dist/wire.js.map +1 -0
- package/package.json +51 -4
- package/src/candid.ts +116 -0
- package/src/canister.ts +85 -0
- package/src/crypto.ts +113 -0
- package/src/formats.ts +399 -0
- package/src/index.ts +86 -0
- package/src/powWorker.ts +6 -0
- package/src/send.ts +224 -0
- package/src/sync.ts +376 -0
- package/src/viewKey.ts +154 -0
- package/src/wire.ts +136 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org/>
|
package/README.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zerc20/storage-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Private note delivery for zERC20 on the Internet Computer. A payer leaves a note's opening, encrypted, in the storage
|
|
4
|
+
canister, and the receiver finds it with its view key. The canister holds only ciphertext under unlinkable ids, so it
|
|
5
|
+
cannot tell who paid whom. Every secret stays in this SDK.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @zerc20/storage-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Node 22.12 or later. Apps that hold a wallet use it through `@zerc20/wallet-sdk`.
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
[Unlicense](LICENSE)
|
package/dist/candid.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ActorMethod } from '@icp-sdk/core/agent';
|
|
2
|
+
import type { IDL } from '@icp-sdk/core/candid';
|
|
3
|
+
export type KindName = 'Outgoing' | 'Lane';
|
|
4
|
+
export type Kind = {
|
|
5
|
+
[K in KindName]: {
|
|
6
|
+
[P in K]: null;
|
|
7
|
+
};
|
|
8
|
+
}[KindName];
|
|
9
|
+
export type Action = {
|
|
10
|
+
OpenChannel: {
|
|
11
|
+
inbox: Uint8Array;
|
|
12
|
+
enc_announcement: Uint8Array;
|
|
13
|
+
outgoing_id: Uint8Array;
|
|
14
|
+
enc_outgoing: Uint8Array;
|
|
15
|
+
};
|
|
16
|
+
} | {
|
|
17
|
+
Put: {
|
|
18
|
+
kind: Kind;
|
|
19
|
+
id: Uint8Array;
|
|
20
|
+
data: Uint8Array;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export interface AuditRequest {
|
|
24
|
+
target: Uint8Array;
|
|
25
|
+
notify_key: Uint8Array;
|
|
26
|
+
transport_public_key: Uint8Array;
|
|
27
|
+
expiry_ns: bigint;
|
|
28
|
+
signature: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
export interface Registration {
|
|
31
|
+
address: Uint8Array;
|
|
32
|
+
public_key: Uint8Array;
|
|
33
|
+
notify_key: Uint8Array;
|
|
34
|
+
escrow: Uint8Array;
|
|
35
|
+
expiry_ns: bigint;
|
|
36
|
+
signature: Uint8Array;
|
|
37
|
+
}
|
|
38
|
+
export interface Slot {
|
|
39
|
+
data: Uint8Array;
|
|
40
|
+
at_ns: bigint;
|
|
41
|
+
}
|
|
42
|
+
export interface AuditRecord {
|
|
43
|
+
auditor: Uint8Array;
|
|
44
|
+
target: Uint8Array;
|
|
45
|
+
notify_key: Uint8Array;
|
|
46
|
+
transport_public_key: Uint8Array;
|
|
47
|
+
expiry_ns: bigint;
|
|
48
|
+
signature: Uint8Array;
|
|
49
|
+
at_ns: bigint;
|
|
50
|
+
}
|
|
51
|
+
export type CanisterError = {
|
|
52
|
+
Occupied: Uint8Array;
|
|
53
|
+
} | {
|
|
54
|
+
Rejected: string;
|
|
55
|
+
};
|
|
56
|
+
export type Result<T> = {
|
|
57
|
+
Ok: T;
|
|
58
|
+
} | {
|
|
59
|
+
Err: CanisterError;
|
|
60
|
+
};
|
|
61
|
+
export interface Service {
|
|
62
|
+
escrow_public_key: ActorMethod<[], Result<Uint8Array>>;
|
|
63
|
+
register: ActorMethod<[Registration, bigint], Result<null>>;
|
|
64
|
+
registration: ActorMethod<[Uint8Array, [] | [Uint8Array]], [] | [Registration]>;
|
|
65
|
+
audit_escrow_key: ActorMethod<[AuditRequest], Result<Uint8Array>>;
|
|
66
|
+
get_audits: ActorMethod<[bigint, number], AuditRecord[]>;
|
|
67
|
+
submit: ActorMethod<[Action[], bigint], Result<null>>;
|
|
68
|
+
get_announcements: ActorMethod<[Uint8Array, number, number], Uint8Array[]>;
|
|
69
|
+
get_slots: ActorMethod<[Kind, Uint8Array[]], ([] | [Slot])[]>;
|
|
70
|
+
get_log: ActorMethod<[bigint, number], Uint8Array[]>;
|
|
71
|
+
log_length: ActorMethod<[], bigint>;
|
|
72
|
+
}
|
|
73
|
+
export declare const idlFactory: IDL.InterfaceFactory;
|
package/dist/candid.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The zerc20_storage Candid interface (canister/zerc20_storage.did), by hand: generated bindings
|
|
2
|
+
// target the older @dfinity/* packages.
|
|
3
|
+
export const idlFactory = ({ IDL }) => {
|
|
4
|
+
const Blob = IDL.Vec(IDL.Nat8);
|
|
5
|
+
const Error = IDL.Variant({ Occupied: Blob, Rejected: IDL.Text });
|
|
6
|
+
const BlobResult = IDL.Variant({ Ok: Blob, Err: Error });
|
|
7
|
+
const UnitResult = IDL.Variant({ Ok: IDL.Null, Err: Error });
|
|
8
|
+
const Kind = IDL.Variant({ Outgoing: IDL.Null, Lane: IDL.Null });
|
|
9
|
+
const Action = IDL.Variant({
|
|
10
|
+
OpenChannel: IDL.Record({ inbox: Blob, enc_announcement: Blob, outgoing_id: Blob, enc_outgoing: Blob }),
|
|
11
|
+
Put: IDL.Record({ kind: Kind, id: Blob, data: Blob }),
|
|
12
|
+
});
|
|
13
|
+
const AuditRequest = IDL.Record({
|
|
14
|
+
target: Blob,
|
|
15
|
+
notify_key: Blob,
|
|
16
|
+
transport_public_key: Blob,
|
|
17
|
+
expiry_ns: IDL.Nat64,
|
|
18
|
+
signature: Blob,
|
|
19
|
+
});
|
|
20
|
+
const Registration = IDL.Record({
|
|
21
|
+
address: Blob,
|
|
22
|
+
public_key: Blob,
|
|
23
|
+
notify_key: Blob,
|
|
24
|
+
escrow: Blob,
|
|
25
|
+
expiry_ns: IDL.Nat64,
|
|
26
|
+
signature: Blob,
|
|
27
|
+
});
|
|
28
|
+
const Slot = IDL.Record({ data: Blob, at_ns: IDL.Nat64 });
|
|
29
|
+
const AuditRecord = IDL.Record({
|
|
30
|
+
auditor: Blob,
|
|
31
|
+
target: Blob,
|
|
32
|
+
notify_key: Blob,
|
|
33
|
+
transport_public_key: Blob,
|
|
34
|
+
expiry_ns: IDL.Nat64,
|
|
35
|
+
signature: Blob,
|
|
36
|
+
at_ns: IDL.Nat64,
|
|
37
|
+
});
|
|
38
|
+
return IDL.Service({
|
|
39
|
+
escrow_public_key: IDL.Func([], [BlobResult], []),
|
|
40
|
+
register: IDL.Func([Registration, IDL.Nat64], [UnitResult], []),
|
|
41
|
+
registration: IDL.Func([Blob, IDL.Opt(Blob)], [IDL.Opt(Registration)], ['query']),
|
|
42
|
+
audit_escrow_key: IDL.Func([AuditRequest], [BlobResult], []),
|
|
43
|
+
get_audits: IDL.Func([IDL.Nat64, IDL.Nat32], [IDL.Vec(AuditRecord)], ['query']),
|
|
44
|
+
submit: IDL.Func([IDL.Vec(Action), IDL.Nat64], [UnitResult], []),
|
|
45
|
+
get_announcements: IDL.Func([Blob, IDL.Nat32, IDL.Nat32], [IDL.Vec(Blob)], ['query']),
|
|
46
|
+
get_slots: IDL.Func([Kind, IDL.Vec(Blob)], [IDL.Vec(IDL.Opt(Slot))], ['query']),
|
|
47
|
+
get_log: IDL.Func([IDL.Nat64, IDL.Nat32], [IDL.Vec(Blob)], ['query']),
|
|
48
|
+
log_length: IDL.Func([], [IDL.Nat64], ['query']),
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=candid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candid.js","sourceRoot":"","sources":["../src/candid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ActorSubclass, type Agent } from '@icp-sdk/core/agent';
|
|
2
|
+
import { Principal } from '@icp-sdk/core/principal';
|
|
3
|
+
import { DerivedPublicKey } from '@dfinity/vetkeys';
|
|
4
|
+
import { type Action, type CanisterError, type Registration, type Result, type Service } from './candid.js';
|
|
5
|
+
/** A definite answer from the canister: `Occupied` (the slot holds other data) or `Rejected`. */
|
|
6
|
+
export declare class StorageError extends Error {
|
|
7
|
+
readonly error: CanisterError;
|
|
8
|
+
constructor(error: CanisterError);
|
|
9
|
+
get occupied(): boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function unwrap<T>(result: Result<T>): T;
|
|
12
|
+
export declare class CanisterClient {
|
|
13
|
+
#private;
|
|
14
|
+
readonly canisterId: Principal;
|
|
15
|
+
/** Queries answered by a single replica: cheap, but only good for spotting news. */
|
|
16
|
+
readonly actor: ActorSubclass<Service>;
|
|
17
|
+
/** Every method as an update call, so the subnet agrees on the answer. */
|
|
18
|
+
readonly replicated: ActorSubclass<Service>;
|
|
19
|
+
/** A client over HTTP. A local replica (an `http://` host on this machine) has its root key fetched; the IC's is
|
|
20
|
+
* built in. A root key fetched from anywhere else would let whoever is on the path forge every certified answer. */
|
|
21
|
+
static connect(host: string, canisterId: Principal | string): Promise<CanisterClient>;
|
|
22
|
+
constructor(agent: Agent, canisterId: Principal | string);
|
|
23
|
+
/** The key owners IBE-encrypt their escrows with. Fixed for the canister's lifetime. */
|
|
24
|
+
escrowPublicKey(): Promise<DerivedPublicKey>;
|
|
25
|
+
/** `address`'s registration with `notifyKey`, by default its current one: a query, which one replica answers.
|
|
26
|
+
* Unverified: check it with `verifyRegistration`. Call `replicated.registration` where a stale answer matters. */
|
|
27
|
+
registration(address: Uint8Array, notifyKey?: Uint8Array): Promise<Registration | null>;
|
|
28
|
+
/** Solves the proof of work and registers. An identical registration that is already stored is a
|
|
29
|
+
* no-op, so a call whose outcome is unknown can be repeated. */
|
|
30
|
+
register(registration: Registration): Promise<void>;
|
|
31
|
+
/** Solves the proof of work and submits the batch. Throws `StorageError` on a definite failure. */
|
|
32
|
+
submit(actions: Action[]): Promise<void>;
|
|
33
|
+
}
|
package/dist/canister.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// A zerc20_storage canister, called anonymously.
|
|
2
|
+
import { Actor, HttpAgent } from '@icp-sdk/core/agent';
|
|
3
|
+
import { Principal } from '@icp-sdk/core/principal';
|
|
4
|
+
import { DerivedPublicKey } from '@dfinity/vetkeys';
|
|
5
|
+
import { idlFactory } from './candid.js';
|
|
6
|
+
import { hex } from './crypto.js';
|
|
7
|
+
import { REGISTER_POW_BITS, actionsDigest, registerDigest, requiredPowBits, solvePow } from './wire.js';
|
|
8
|
+
/** A definite answer from the canister: `Occupied` (the slot holds other data) or `Rejected`. */
|
|
9
|
+
export class StorageError extends Error {
|
|
10
|
+
error;
|
|
11
|
+
constructor(error) {
|
|
12
|
+
super('Occupied' in error ? `occupied: 0x${hex(error.Occupied)}` : `rejected: ${error.Rejected}`);
|
|
13
|
+
this.error = error;
|
|
14
|
+
}
|
|
15
|
+
get occupied() {
|
|
16
|
+
return 'Occupied' in this.error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Each request gives up after this, so that one that hangs does not hold its caller forever. A call cut off may still
|
|
20
|
+
* have landed, as one whose connection drops may. */
|
|
21
|
+
const TIMEOUT_MS = 30_000;
|
|
22
|
+
const fetchWithTimeout = (input, init) => {
|
|
23
|
+
const timeout = AbortSignal.timeout(TIMEOUT_MS);
|
|
24
|
+
return fetch(input, { ...init, signal: init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout });
|
|
25
|
+
};
|
|
26
|
+
export function unwrap(result) {
|
|
27
|
+
if ('Err' in result)
|
|
28
|
+
throw new StorageError(result.Err);
|
|
29
|
+
return result.Ok;
|
|
30
|
+
}
|
|
31
|
+
export class CanisterClient {
|
|
32
|
+
canisterId;
|
|
33
|
+
/** Queries answered by a single replica: cheap, but only good for spotting news. */
|
|
34
|
+
actor;
|
|
35
|
+
/** Every method as an update call, so the subnet agrees on the answer. */
|
|
36
|
+
replicated;
|
|
37
|
+
#escrowPublicKey;
|
|
38
|
+
/** A client over HTTP. A local replica (an `http://` host on this machine) has its root key fetched; the IC's is
|
|
39
|
+
* built in. A root key fetched from anywhere else would let whoever is on the path forge every certified answer. */
|
|
40
|
+
static async connect(host, canisterId) {
|
|
41
|
+
const url = new URL(host);
|
|
42
|
+
const local = url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
43
|
+
const agent = await HttpAgent.create({ host, shouldFetchRootKey: local, fetch: fetchWithTimeout });
|
|
44
|
+
return new CanisterClient(agent, canisterId);
|
|
45
|
+
}
|
|
46
|
+
constructor(agent, canisterId) {
|
|
47
|
+
this.canisterId = Principal.from(canisterId);
|
|
48
|
+
const config = { agent, canisterId: this.canisterId };
|
|
49
|
+
this.actor = Actor.createActor(idlFactory, config);
|
|
50
|
+
this.replicated = Actor.createActor(idlFactory, { ...config, queryStrategy: 'update' });
|
|
51
|
+
}
|
|
52
|
+
/** The key owners IBE-encrypt their escrows with. Fixed for the canister's lifetime. */
|
|
53
|
+
escrowPublicKey() {
|
|
54
|
+
this.#escrowPublicKey ??= this.actor.escrow_public_key().then((r) => DerivedPublicKey.deserialize(unwrap(r)));
|
|
55
|
+
this.#escrowPublicKey.catch(() => (this.#escrowPublicKey = undefined));
|
|
56
|
+
return this.#escrowPublicKey;
|
|
57
|
+
}
|
|
58
|
+
/** `address`'s registration with `notifyKey`, by default its current one: a query, which one replica answers.
|
|
59
|
+
* Unverified: check it with `verifyRegistration`. Call `replicated.registration` where a stale answer matters. */
|
|
60
|
+
async registration(address, notifyKey) {
|
|
61
|
+
const r = await this.actor.registration(address, notifyKey ? [notifyKey] : []);
|
|
62
|
+
return r[0] ?? null;
|
|
63
|
+
}
|
|
64
|
+
/** Solves the proof of work and registers. An identical registration that is already stored is a
|
|
65
|
+
* no-op, so a call whose outcome is unknown can be repeated. */
|
|
66
|
+
async register(registration) {
|
|
67
|
+
const nonce = await solvePow(registerDigest(registration), REGISTER_POW_BITS);
|
|
68
|
+
unwrap(await this.actor.register(registration, nonce));
|
|
69
|
+
}
|
|
70
|
+
/** Solves the proof of work and submits the batch. Throws `StorageError` on a definite failure. */
|
|
71
|
+
async submit(actions) {
|
|
72
|
+
const nonce = await solvePow(actionsDigest(actions), requiredPowBits(actions));
|
|
73
|
+
unwrap(await this.actor.submit(actions, nonce));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=canister.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canister.js","sourceRoot":"","sources":["../src/canister.ts"],"names":[],"mappings":"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"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils.js';
|
|
2
|
+
export { bytesToHex as hex, hexToBytes as unhex, randomBytes };
|
|
3
|
+
export declare const utf8: (text: string) => Uint8Array;
|
|
4
|
+
export declare function concat(...parts: Uint8Array[]): Uint8Array;
|
|
5
|
+
export declare function u32be(n: number): Uint8Array;
|
|
6
|
+
export declare function u64be(n: bigint): Uint8Array;
|
|
7
|
+
export declare const readU32: (bytes: Uint8Array, at: number) => number;
|
|
8
|
+
export declare const readU64: (bytes: Uint8Array, at: number) => bigint;
|
|
9
|
+
export declare function equal(a: Uint8Array, b: Uint8Array): boolean;
|
|
10
|
+
/** Lexicographic (= big-endian numeric, for equal lengths) comparison. */
|
|
11
|
+
export declare function compare(a: Uint8Array, b: Uint8Array): number;
|
|
12
|
+
/** `u32_be(len) ‖ part` for every part, so fields can never run into each other. */
|
|
13
|
+
export declare function lengthPrefixed(parts: Uint8Array[]): Uint8Array;
|
|
14
|
+
/** Unix time in ns from the local clock. */
|
|
15
|
+
export declare const nowNs: () => bigint;
|
|
16
|
+
/** HKDF-SHA256 of `key`, domain-separated by `label` and the length-prefixed `parts`. */
|
|
17
|
+
export declare function kdf(key: Uint8Array, label: string, parts?: Uint8Array[]): Uint8Array;
|
|
18
|
+
/** SHA-256 of `tag` and `parts`, each length-prefixed: what anyone can compute, where `kdf` needs a key. */
|
|
19
|
+
export declare function taggedHash(tag: string, parts: Uint8Array[]): Uint8Array;
|
|
20
|
+
/** `nonce(12) ‖ ChaCha20-Poly1305`. The nonce is random so that a retry after a failed but
|
|
21
|
+
* already-visible call never reuses a (key, nonce) pair. */
|
|
22
|
+
export declare function seal(key: Uint8Array, aad: Uint8Array, msg: Uint8Array): Uint8Array;
|
|
23
|
+
export declare function open(key: Uint8Array, aad: Uint8Array, blob: Uint8Array): Uint8Array | null;
|
|
24
|
+
/** ECIES to an X25519 public key: `ephemeral_pk(32) ‖ seal(..)`. `null` for a low-order key, whose
|
|
25
|
+
* shared secret anyone could compute. */
|
|
26
|
+
export declare function sealTo(publicKey: Uint8Array, label: string, aad: Uint8Array, msg: Uint8Array): Uint8Array | null;
|
|
27
|
+
export declare function openFrom(secret: Uint8Array, label: string, aad: Uint8Array, blob: Uint8Array): Uint8Array | null;
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Primitives: HKDF-SHA256, ChaCha20-Poly1305 with random nonces, X25519 ECIES, and byte helpers.
|
|
2
|
+
import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
3
|
+
import { x25519 } from '@noble/curves/ed25519.js';
|
|
4
|
+
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
5
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
6
|
+
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils.js';
|
|
7
|
+
export { bytesToHex as hex, hexToBytes as unhex, randomBytes };
|
|
8
|
+
export const utf8 = (text) => new TextEncoder().encode(text);
|
|
9
|
+
export function concat(...parts) {
|
|
10
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
11
|
+
let at = 0;
|
|
12
|
+
for (const part of parts) {
|
|
13
|
+
out.set(part, at);
|
|
14
|
+
at += part.length;
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
export function u32be(n) {
|
|
19
|
+
const out = new Uint8Array(4);
|
|
20
|
+
new DataView(out.buffer).setUint32(0, n, false);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
export function u64be(n) {
|
|
24
|
+
const out = new Uint8Array(8);
|
|
25
|
+
new DataView(out.buffer).setBigUint64(0, n, false);
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
export const readU32 = (bytes, at) => new DataView(bytes.buffer, bytes.byteOffset).getUint32(at, false);
|
|
29
|
+
export const readU64 = (bytes, at) => new DataView(bytes.buffer, bytes.byteOffset).getBigUint64(at, false);
|
|
30
|
+
export function equal(a, b) {
|
|
31
|
+
return a.length === b.length && a.every((byte, i) => byte === b[i]);
|
|
32
|
+
}
|
|
33
|
+
/** Lexicographic (= big-endian numeric, for equal lengths) comparison. */
|
|
34
|
+
export function compare(a, b) {
|
|
35
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
36
|
+
if (a[i] !== b[i])
|
|
37
|
+
return a[i] - b[i];
|
|
38
|
+
}
|
|
39
|
+
return a.length - b.length;
|
|
40
|
+
}
|
|
41
|
+
/** `u32_be(len) ‖ part` for every part, so fields can never run into each other. */
|
|
42
|
+
export function lengthPrefixed(parts) {
|
|
43
|
+
return concat(...parts.flatMap((part) => [u32be(part.length), part]));
|
|
44
|
+
}
|
|
45
|
+
/** Unix time in ns from the local clock. */
|
|
46
|
+
export const nowNs = () => BigInt(Date.now()) * 1000000n;
|
|
47
|
+
const KDF_SALT = utf8('zerc20/storage/kdf/v1');
|
|
48
|
+
/** HKDF-SHA256 of `key`, domain-separated by `label` and the length-prefixed `parts`. */
|
|
49
|
+
export function kdf(key, label, parts = []) {
|
|
50
|
+
return hkdf(sha256, key, KDF_SALT, lengthPrefixed([utf8(label), ...parts]), 32);
|
|
51
|
+
}
|
|
52
|
+
/** SHA-256 of `tag` and `parts`, each length-prefixed: what anyone can compute, where `kdf` needs a key. */
|
|
53
|
+
export function taggedHash(tag, parts) {
|
|
54
|
+
return sha256(lengthPrefixed([utf8(tag), ...parts]));
|
|
55
|
+
}
|
|
56
|
+
/** `nonce(12) ‖ ChaCha20-Poly1305`. The nonce is random so that a retry after a failed but
|
|
57
|
+
* already-visible call never reuses a (key, nonce) pair. */
|
|
58
|
+
export function seal(key, aad, msg) {
|
|
59
|
+
const nonce = randomBytes(12);
|
|
60
|
+
return concat(nonce, chacha20poly1305(key, nonce, aad).encrypt(msg));
|
|
61
|
+
}
|
|
62
|
+
export function open(key, aad, blob) {
|
|
63
|
+
if (blob.length < 12)
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
return chacha20poly1305(key, blob.subarray(0, 12), aad).decrypt(blob.subarray(12));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** ECIES to an X25519 public key: `ephemeral_pk(32) ‖ seal(..)`. `null` for a low-order key, whose
|
|
73
|
+
* shared secret anyone could compute. */
|
|
74
|
+
export function sealTo(publicKey, label, aad, msg) {
|
|
75
|
+
const ephemeral = x25519.utils.randomSecretKey();
|
|
76
|
+
const ephemeralPk = x25519.getPublicKey(ephemeral);
|
|
77
|
+
let shared;
|
|
78
|
+
try {
|
|
79
|
+
shared = x25519.getSharedSecret(ephemeral, publicKey);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return concat(ephemeralPk, seal(kdf(shared, label, [ephemeralPk, publicKey]), aad, msg));
|
|
85
|
+
}
|
|
86
|
+
export function openFrom(secret, label, aad, blob) {
|
|
87
|
+
if (blob.length < 32)
|
|
88
|
+
return null;
|
|
89
|
+
const ephemeralPk = blob.subarray(0, 32);
|
|
90
|
+
try {
|
|
91
|
+
const shared = x25519.getSharedSecret(secret, ephemeralPk);
|
|
92
|
+
const key = kdf(shared, label, [ephemeralPk, x25519.getPublicKey(secret)]);
|
|
93
|
+
return open(key, aad, blob.subarray(32));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/** A sealed slot's plaintext: the blob less the nonce and the tag. */
|
|
2
|
+
export declare const SLOT_PLAIN: number;
|
|
3
|
+
/** An inbox announcement's plaintext: also less the ephemeral key. */
|
|
4
|
+
export declare const ANNOUNCE_PLAIN: number;
|
|
5
|
+
/** The most salts a burn block holds. */
|
|
6
|
+
export declare const BURN_BLOCK = 6;
|
|
7
|
+
/** The BN254 scalar field modulus `Fr`, big-endian. */
|
|
8
|
+
export declare const FR_BE: Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>;
|
|
9
|
+
/** Record types, the first byte of every plaintext. */
|
|
10
|
+
export declare const NOTE = 1;
|
|
11
|
+
export declare const CODE_NOTE = 2;
|
|
12
|
+
export declare const SELF_NOTE = 3;
|
|
13
|
+
export declare const DEPOSIT = 4;
|
|
14
|
+
export declare const BURN_BLOCK_RECORD = 5;
|
|
15
|
+
export declare const RETURNED = 6;
|
|
16
|
+
export declare const DEPOSIT_ADDRESS = 7;
|
|
17
|
+
export declare const POS_LANE = 8;
|
|
18
|
+
export declare const SENT_TO_ADDRESS = 16;
|
|
19
|
+
export declare const SENT_TO_CODE = 17;
|
|
20
|
+
export declare const CHANNEL = 32;
|
|
21
|
+
export declare const RECEIVE_CODE = 64;
|
|
22
|
+
/** The inbox announcements of channels to `address` go to, under its registration's notify key: another
|
|
23
|
+
* registration, another inbox, so payers then open fresh channels. */
|
|
24
|
+
export declare const addressInbox: (address: Uint8Array, notifyKey: Uint8Array) => Uint8Array;
|
|
25
|
+
/** The payer's seed for its channel to `inbox`, recomputable from the payer's view key alone. */
|
|
26
|
+
export declare const channelSeed: (seed: Uint8Array, payer: Uint8Array, inbox: Uint8Array) => Uint8Array;
|
|
27
|
+
/** A channel's lane key. Binding the inbox and the claimed payer means a seed learned from one channel opens
|
|
28
|
+
* nothing when replayed into another inbox or under another name. */
|
|
29
|
+
export declare const channelKey: (channelSeed: Uint8Array, inbox: Uint8Array, payer: Uint8Array) => Uint8Array;
|
|
30
|
+
/** The lane key of receive code `index` (burn index `index` of `tokenId` on `chainId`). Every payer of the code
|
|
31
|
+
* writes its notes to this one lane, and the owner finds them through the log: no inbox, no announcement. */
|
|
32
|
+
export declare const codeKey: (seed: Uint8Array, chainId: bigint, tokenId: number, index: number) => Uint8Array;
|
|
33
|
+
/** The owner's own lane key: notes to self (merge outputs and change) and burn blocks, which only the owner's
|
|
34
|
+
* devices write and read. */
|
|
35
|
+
export declare const ownKey: (seed: Uint8Array) => Uint8Array;
|
|
36
|
+
/** The lane key of the owner's point of sale `n`, handed to that point of sale alone: it records there the burn
|
|
37
|
+
* addresses it makes for the owner, ahead of handing them out. */
|
|
38
|
+
export declare const posKey: (seed: Uint8Array, n: number) => Uint8Array;
|
|
39
|
+
/** The slot of record `slotIndex` on `chainId` in the lane of `laneKey`. */
|
|
40
|
+
export declare const slotId: (laneKey: Uint8Array, chainId: bigint, slotIndex: number) => Uint8Array;
|
|
41
|
+
export declare const sealKey: (laneKey: Uint8Array) => Uint8Array;
|
|
42
|
+
export declare const outgoingId: (seed: Uint8Array, index: number) => Uint8Array;
|
|
43
|
+
export declare const outgoingKey: (seed: Uint8Array) => Uint8Array;
|
|
44
|
+
/** What an on-chain note commits to besides its owner's `public_key`:
|
|
45
|
+
* `note_hash = Poseidon(chain_id, public_key, value, token_id, note_salt)`. */
|
|
46
|
+
export interface NoteOpening {
|
|
47
|
+
chainId: bigint;
|
|
48
|
+
tokenId: number;
|
|
49
|
+
/** Big-endian, below 2^248 (its first byte is 0). */
|
|
50
|
+
value: Uint8Array;
|
|
51
|
+
/** Big-endian canonical `Fr` element, fresh for every note. */
|
|
52
|
+
noteSalt: Uint8Array;
|
|
53
|
+
}
|
|
54
|
+
/** What a payment, withdrawal or merge was, kept on its note to self (its change, even of zero value, or a merge's
|
|
55
|
+
* output), so that every device of the owner shows it: that note on chain says it landed. */
|
|
56
|
+
export interface Receipt {
|
|
57
|
+
kind: 'sent' | 'sent-to-code' | 'withdrawn' | 'unwrapped' | 'merged';
|
|
58
|
+
/** Big-endian, below 2^248, as a note's value: what was paid or withdrawn, or what a merge's note holds. */
|
|
59
|
+
amount: Uint8Array;
|
|
60
|
+
/** The same: what the relayer was paid for it. */
|
|
61
|
+
fee: Uint8Array;
|
|
62
|
+
/** Whom it paid: an address, a receive code's burn address, or for a merge 20 zero bytes. */
|
|
63
|
+
counterparty: Uint8Array;
|
|
64
|
+
}
|
|
65
|
+
/** What the owner did that leaves no note to self, kept in the own lane so that every device of the owner shows it:
|
|
66
|
+
* a deposit into its burn address returned to its sender (a rage exit), or a deposit address it registered for one. */
|
|
67
|
+
export type OwnReceipt = {
|
|
68
|
+
chainId: bigint;
|
|
69
|
+
tokenId: number;
|
|
70
|
+
burnAddress: Uint8Array;
|
|
71
|
+
} & ({
|
|
72
|
+
kind: 'returned';
|
|
73
|
+
/** The deposit's `commitment_index`. */
|
|
74
|
+
index: number;
|
|
75
|
+
recipient: Uint8Array;
|
|
76
|
+
/** Big-endian, below 2^248. */
|
|
77
|
+
amount: Uint8Array;
|
|
78
|
+
txHash: Uint8Array;
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'deposit-address';
|
|
81
|
+
depositAddress: Uint8Array;
|
|
82
|
+
});
|
|
83
|
+
/** Where a record sat in its lane, and when the canister first stored it (kept in the clear). */
|
|
84
|
+
interface Stamped {
|
|
85
|
+
/** Position in the lane's sequence for its chain (0, 1, 2, …), which locates its slot. Not the on-chain
|
|
86
|
+
* `commitment_index`; slots that do not open leave gaps. */
|
|
87
|
+
slotIndex: number;
|
|
88
|
+
atNs: bigint;
|
|
89
|
+
}
|
|
90
|
+
export interface Note extends Stamped {
|
|
91
|
+
kind: 'note';
|
|
92
|
+
opening: NoteOpening;
|
|
93
|
+
/** In a receive code's lane, the payer it claims to be (unverified). */
|
|
94
|
+
payer?: Uint8Array;
|
|
95
|
+
/** In the own lane, what it is the change or the output of; missing for a kind this version does not know. */
|
|
96
|
+
receipt?: Receipt;
|
|
97
|
+
}
|
|
98
|
+
/** Not a note on chain: a deposit of `value` into the receiver's burn address of `burnSalt`, under the `public_key`
|
|
99
|
+
* the payer looked up (zERC20-v2's `burn_address`), paid by a plain transfer. */
|
|
100
|
+
export interface Deposit extends Stamped {
|
|
101
|
+
kind: 'deposit';
|
|
102
|
+
chainId: bigint;
|
|
103
|
+
tokenId: number;
|
|
104
|
+
/** Big-endian, below 2^248, as a note's. */
|
|
105
|
+
value: Uint8Array;
|
|
106
|
+
burnSalt: Uint8Array;
|
|
107
|
+
/** The burn address, which the receiver derives itself: for the payer's other devices, to find the transfer. */
|
|
108
|
+
burnAddress: Uint8Array;
|
|
109
|
+
}
|
|
110
|
+
/** Burn addresses `first, first + 1, ..` of `tokenId` on `chainId`, by their salts, which their owner may have handed
|
|
111
|
+
* out. Whoever hands one out records its block first, so every device of the owner finds the payments to it. In the
|
|
112
|
+
* own lane they are zERC20-v2's `keys::burn` indices; in a point of sale's lane, its `keys::pos_burn` counters. Salts,
|
|
113
|
+
* because deriving an address takes about 2^16 hashes, and checking one from its salt takes one. */
|
|
114
|
+
export interface BurnBlock {
|
|
115
|
+
chainId: bigint;
|
|
116
|
+
tokenId: number;
|
|
117
|
+
first: number;
|
|
118
|
+
/** 32 bytes each, 1 to `BURN_BLOCK` of them. */
|
|
119
|
+
salts: Uint8Array[];
|
|
120
|
+
}
|
|
121
|
+
/** In the own lane: the owner made point of sale `n`, whose lane (`posKey`) its devices then read. */
|
|
122
|
+
export interface PosLane {
|
|
123
|
+
n: number;
|
|
124
|
+
}
|
|
125
|
+
export type LaneRecord = Note | Deposit | (BurnBlock & Stamped & {
|
|
126
|
+
kind: 'burn-block';
|
|
127
|
+
}) | (OwnReceipt & Stamped) | (PosLane & Stamped & {
|
|
128
|
+
kind: 'pos-lane';
|
|
129
|
+
});
|
|
130
|
+
/** Which lane a record was read from: the records each holds, and who writes it. */
|
|
131
|
+
export type LaneKind = 'channel' | 'code' | 'own' | 'pos';
|
|
132
|
+
/** A note's plaintext, `token_id(4) ‖ value(31) ‖ note_salt(32)`, or `null` if a field is out of the circuit's range
|
|
133
|
+
* (an out-of-range field can hash like a different value). With `payer` it is a note to a receive code, naming its
|
|
134
|
+
* payer; with `receipt`, a note to self, saying what it is the change or the output of. */
|
|
135
|
+
export declare function notePlaintext(opening: NoteOpening, extra?: {
|
|
136
|
+
payer: Uint8Array;
|
|
137
|
+
} | {
|
|
138
|
+
receipt: Receipt;
|
|
139
|
+
} | null): Uint8Array | null;
|
|
140
|
+
/** A deposit's announcement, `token_id(4) ‖ value(31) ‖ burn_salt(32) ‖ burn_address(20)`, or `null` if the value or
|
|
141
|
+
* the salt is out of range. */
|
|
142
|
+
export declare function depositPlaintext(tokenId: number, value: Uint8Array, burnSalt: Uint8Array, burnAddress: Uint8Array): Uint8Array | null;
|
|
143
|
+
/** `token_id(4) ‖ burn_address(20) ‖` then a return's `index(8) ‖ recipient(20) ‖ amount(31) ‖ tx_hash(32)`, or a
|
|
144
|
+
* deposit address's `deposit_address(20)`. */
|
|
145
|
+
export declare function ownReceiptPlaintext(r: OwnReceipt): Uint8Array;
|
|
146
|
+
/** `token_id(4) ‖ first(4) ‖ count(1) ‖ salt(32) × count`. */
|
|
147
|
+
export declare function burnBlockPlaintext(b: BurnBlock): Uint8Array;
|
|
148
|
+
/** `n(4)`. */
|
|
149
|
+
export declare function posLanePlaintext(n: number): Uint8Array;
|
|
150
|
+
/** The record a slot of a `lane` lane on `chainId` held, or `null` if it is malformed or not one such a lane holds. */
|
|
151
|
+
export declare function readLaneRecord(lane: LaneKind, chainId: bigint, slotIndex: number, atNs: bigint, plain: Uint8Array): LaneRecord | null;
|
|
152
|
+
/** Where to pay: handed from receiver to payer over a trusted channel. It does not name the receiver, but whoever
|
|
153
|
+
* swaps it in transit redirects the funds. Pay any amount, by a transfer to `burnAddress` (a deposit), or by a note
|
|
154
|
+
* owned by `publicKey` written to the lane of `key`. */
|
|
155
|
+
export interface ReceiveCode {
|
|
156
|
+
chainId: bigint;
|
|
157
|
+
tokenId: number;
|
|
158
|
+
/** zERC20-v2's `keys::burn` address of the code's index. */
|
|
159
|
+
burnAddress: Uint8Array;
|
|
160
|
+
/** The zERC20 public key the paying note commits to: the code's own (`keys::code_secret`), not the address's, so
|
|
161
|
+
* codes cannot be linked to each other or to the address. */
|
|
162
|
+
publicKey: Uint8Array;
|
|
163
|
+
key: Uint8Array;
|
|
164
|
+
}
|
|
165
|
+
/** A receive code as text: bech32m of `RECEIVE_CODE ‖ chain_id(8) ‖ token_id(4) ‖ burn_address(20) ‖ public_key(32) ‖
|
|
166
|
+
* key(32)`, so a mistyped one fails its checksum instead of paying someone else. */
|
|
167
|
+
export declare function encodeCode(c: ReceiveCode): string;
|
|
168
|
+
/** The receive code `text` holds, or `null` for anything else. */
|
|
169
|
+
export declare function decodeCode(text: string): ReceiveCode | null;
|
|
170
|
+
/** Where a payer paid, as its outgoing record keeps it: an address under one of its registrations (its notify key),
|
|
171
|
+
* or a receive code. */
|
|
172
|
+
export type Target = {
|
|
173
|
+
address: Uint8Array;
|
|
174
|
+
notifyKey: Uint8Array;
|
|
175
|
+
} | ReceiveCode;
|
|
176
|
+
/** `address(20) ‖ notify_key(32)`, or the code's body. */
|
|
177
|
+
export declare function targetPlaintext(t: Target): Uint8Array;
|
|
178
|
+
export declare function readTarget(plain: Uint8Array): Target | null;
|
|
179
|
+
/** A channel's announcement, `payer(20) ‖ channel_seed(32)`: who claims to open it, and the seed its lane key derives
|
|
180
|
+
* from. */
|
|
181
|
+
export declare function channelPlaintext(payer: Uint8Array, seed: Uint8Array): Uint8Array;
|
|
182
|
+
export declare function readChannel(plain: Uint8Array): {
|
|
183
|
+
payer: Uint8Array;
|
|
184
|
+
seed: Uint8Array;
|
|
185
|
+
} | null;
|
|
186
|
+
/** How records reach a receiver: through a channel to its address, a receive code's lane, or the lane of one of its
|
|
187
|
+
* points of sale. The code's owner knows it by its token and burn index; a payer by the code itself. */
|
|
188
|
+
export type Via = {
|
|
189
|
+
type: 'address';
|
|
190
|
+
} | {
|
|
191
|
+
type: 'code';
|
|
192
|
+
chainId: bigint;
|
|
193
|
+
tokenId: number;
|
|
194
|
+
index: number;
|
|
195
|
+
} | {
|
|
196
|
+
type: 'code';
|
|
197
|
+
code: ReceiveCode;
|
|
198
|
+
} | {
|
|
199
|
+
type: 'pos';
|
|
200
|
+
n: number;
|
|
201
|
+
};
|
|
202
|
+
export {};
|