@veilo/sdk-core 0.3.3 → 0.4.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/README.md +42 -3
- package/SHIELD_INTEGRATION.md +143 -0
- package/config.d.ts +2 -0
- package/config.js +4 -0
- package/dist/cjs/client.d.ts +5 -5
- package/dist/cjs/client.js +79 -42
- package/dist/cjs/compactNote.d.ts +107 -0
- package/dist/cjs/compactNote.js +167 -0
- package/dist/cjs/events.d.ts +1 -1
- package/dist/cjs/events.js +2 -2
- package/dist/cjs/idl/privacy_pool.d.ts +5 -0
- package/dist/cjs/idl/privacy_pool.js +15218 -0
- package/dist/cjs/index.d.ts +16 -13
- package/dist/cjs/index.js +40 -30
- package/dist/cjs/merkle.d.ts +13 -0
- package/dist/cjs/merkle.js +31 -8
- package/dist/cjs/program.d.ts +11 -0
- package/dist/cjs/program.js +26 -3
- package/dist/cjs/proof.d.ts +1 -1
- package/dist/cjs/proof.js +37 -37
- package/dist/cjs/prover.d.ts +1 -1
- package/dist/cjs/prover.js +2 -2
- package/dist/cjs/random.d.ts +16 -0
- package/dist/cjs/random.js +28 -0
- package/dist/cjs/relayer.d.ts +27 -4
- package/dist/cjs/relayer.js +16 -5
- package/dist/cjs/shield/alt.d.ts +87 -0
- package/dist/cjs/shield/alt.js +194 -0
- package/dist/cjs/shield/computeBudget.d.ts +61 -0
- package/dist/cjs/shield/computeBudget.js +64 -0
- package/dist/cjs/shield/errors.d.ts +58 -0
- package/dist/cjs/shield/errors.js +121 -0
- package/dist/cjs/shield/finalize.d.ts +45 -0
- package/dist/cjs/shield/finalize.js +119 -0
- package/dist/cjs/shield/index.d.ts +35 -0
- package/dist/cjs/shield/index.js +68 -0
- package/dist/cjs/shield/ix.d.ts +54 -0
- package/dist/cjs/shield/ix.js +119 -0
- package/dist/cjs/shield/owner.d.ts +36 -0
- package/dist/cjs/shield/owner.js +126 -0
- package/dist/cjs/shield/ports.d.ts +43 -0
- package/dist/cjs/shield/ports.js +153 -0
- package/dist/cjs/shield/preflight.d.ts +30 -0
- package/dist/cjs/shield/preflight.js +154 -0
- package/dist/cjs/shield/shield.d.ts +68 -0
- package/dist/cjs/shield/shield.js +499 -0
- package/dist/cjs/shield/types.d.ts +202 -0
- package/dist/cjs/shield/types.js +2 -0
- package/dist/cjs/utxo.d.ts +20 -0
- package/dist/cjs/utxo.js +41 -28
- package/dist/esm/client.d.ts +407 -0
- package/dist/esm/client.js +9 -5
- package/dist/esm/compactNote.d.ts +107 -0
- package/dist/esm/compactNote.js +156 -0
- package/dist/esm/config.d.ts +82 -0
- package/dist/esm/events.d.ts +77 -0
- package/dist/esm/events.js +1 -1
- package/dist/esm/idl/privacy_pool.d.ts +5 -0
- package/dist/esm/idl/privacy_pool.js +15216 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +18 -11
- package/dist/esm/merkle.d.ts +77 -0
- package/dist/esm/merkle.js +23 -1
- package/dist/esm/package.json +1 -0
- package/dist/esm/poseidon.d.ts +29 -0
- package/dist/esm/program.d.ts +37 -0
- package/dist/esm/program.js +23 -1
- package/dist/esm/proof.d.ts +183 -0
- package/dist/esm/proof.js +2 -2
- package/dist/esm/prover.d.ts +54 -0
- package/dist/esm/prover.js +1 -1
- package/dist/esm/random.d.ts +16 -0
- package/dist/esm/random.js +21 -0
- package/dist/esm/relayer.d.ts +318 -0
- package/dist/esm/relayer.js +16 -5
- package/dist/esm/retry.d.ts +32 -0
- package/dist/esm/shield/alt.d.ts +87 -0
- package/dist/esm/shield/alt.js +186 -0
- package/dist/esm/shield/computeBudget.d.ts +61 -0
- package/dist/esm/shield/computeBudget.js +61 -0
- package/dist/esm/shield/errors.d.ts +58 -0
- package/dist/esm/shield/errors.js +115 -0
- package/dist/esm/shield/finalize.d.ts +45 -0
- package/dist/esm/shield/finalize.js +83 -0
- package/dist/esm/shield/index.d.ts +35 -0
- package/dist/esm/shield/index.js +32 -0
- package/dist/esm/shield/ix.d.ts +54 -0
- package/dist/esm/shield/ix.js +82 -0
- package/dist/esm/shield/owner.d.ts +36 -0
- package/dist/esm/shield/owner.js +122 -0
- package/dist/esm/shield/ports.d.ts +43 -0
- package/dist/esm/shield/ports.js +147 -0
- package/dist/esm/shield/preflight.d.ts +30 -0
- package/dist/esm/shield/preflight.js +151 -0
- package/dist/esm/shield/shield.d.ts +68 -0
- package/dist/esm/shield/shield.js +492 -0
- package/dist/esm/shield/types.d.ts +202 -0
- package/dist/esm/shield/types.js +1 -0
- package/dist/esm/utxo.d.ts +235 -0
- package/dist/esm/utxo.js +18 -8
- package/dist/src/client.d.ts +407 -0
- package/dist/src/client.js +951 -0
- package/dist/src/compactNote.d.ts +107 -0
- package/dist/src/compactNote.js +167 -0
- package/dist/src/config.d.ts +82 -0
- package/dist/src/config.js +57 -0
- package/dist/src/events.d.ts +77 -0
- package/dist/src/events.js +167 -0
- package/dist/src/idl/privacy_pool.d.ts +5 -0
- package/dist/src/idl/privacy_pool.js +15218 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +67 -0
- package/dist/src/merkle.d.ts +77 -0
- package/dist/src/merkle.js +156 -0
- package/dist/src/poseidon.d.ts +29 -0
- package/dist/src/poseidon.js +100 -0
- package/dist/src/program.d.ts +37 -0
- package/dist/src/program.js +61 -0
- package/dist/src/proof.d.ts +183 -0
- package/dist/src/proof.js +292 -0
- package/dist/src/prover.d.ts +54 -0
- package/dist/src/prover.js +112 -0
- package/dist/src/random.d.ts +16 -0
- package/dist/src/random.js +28 -0
- package/dist/src/relayer.d.ts +318 -0
- package/dist/src/relayer.js +257 -0
- package/dist/src/retry.d.ts +32 -0
- package/dist/src/retry.js +75 -0
- package/dist/src/shield/alt.d.ts +87 -0
- package/dist/src/shield/alt.js +194 -0
- package/dist/src/shield/computeBudget.d.ts +61 -0
- package/dist/src/shield/computeBudget.js +64 -0
- package/dist/src/shield/errors.d.ts +58 -0
- package/dist/src/shield/errors.js +121 -0
- package/dist/src/shield/finalize.d.ts +45 -0
- package/dist/src/shield/finalize.js +119 -0
- package/dist/src/shield/index.d.ts +35 -0
- package/dist/src/shield/index.js +68 -0
- package/dist/src/shield/ix.d.ts +54 -0
- package/dist/src/shield/ix.js +119 -0
- package/dist/src/shield/owner.d.ts +36 -0
- package/dist/src/shield/owner.js +126 -0
- package/dist/src/shield/ports.d.ts +43 -0
- package/dist/src/shield/ports.js +153 -0
- package/dist/src/shield/preflight.d.ts +30 -0
- package/dist/src/shield/preflight.js +154 -0
- package/dist/src/shield/shield.d.ts +68 -0
- package/dist/src/shield/shield.js +499 -0
- package/dist/src/shield/types.d.ts +202 -0
- package/dist/src/shield/types.js +2 -0
- package/dist/src/utxo.d.ts +235 -0
- package/dist/src/utxo.js +407 -0
- package/dist/tests/compact-note.test.d.ts +1 -0
- package/dist/tests/compact-note.test.js +173 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +102 -0
- package/dist/tests/edge-cases.test.d.ts +1 -0
- package/dist/tests/edge-cases.test.js +220 -0
- package/dist/tests/encryption.test.d.ts +1 -0
- package/dist/tests/encryption.test.js +215 -0
- package/dist/tests/events.test.d.ts +1 -0
- package/dist/tests/events.test.js +78 -0
- package/dist/tests/multi-tree.test.d.ts +1 -0
- package/dist/tests/multi-tree.test.js +405 -0
- package/dist/tests/pda.test.d.ts +1 -0
- package/dist/tests/pda.test.js +229 -0
- package/dist/tests/poseidon-builder-parity.test.d.ts +1 -0
- package/dist/tests/poseidon-builder-parity.test.js +72 -0
- package/dist/tests/poseidon.test.d.ts +1 -0
- package/dist/tests/poseidon.test.js +142 -0
- package/dist/tests/proof.test.d.ts +1 -0
- package/dist/tests/proof.test.js +296 -0
- package/dist/tests/relayer.test.d.ts +1 -0
- package/dist/tests/relayer.test.js +271 -0
- package/dist/tests/sdk.integration.test.d.ts +1 -0
- package/dist/tests/sdk.integration.test.js +330 -0
- package/dist/tests/shield-owner.test.d.ts +1 -0
- package/dist/tests/shield-owner.test.js +89 -0
- package/dist/tests/shield-preflight.test.d.ts +1 -0
- package/dist/tests/shield-preflight.test.js +87 -0
- package/dist/tests/shield-realproof.test.d.ts +1 -0
- package/dist/tests/shield-realproof.test.js +272 -0
- package/dist/tests/shield.test.d.ts +1 -0
- package/dist/tests/shield.test.js +403 -0
- package/dist/tests/utxo.test.d.ts +1 -0
- package/dist/tests/utxo.test.js +140 -0
- package/package.json +69 -11
- package/poseidon.d.ts +2 -0
- package/poseidon.js +4 -0
- package/proof.d.ts +2 -0
- package/proof.js +4 -0
- package/prover.d.ts +2 -0
- package/prover.js +4 -0
- package/shield.d.ts +2 -0
- package/shield.js +4 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { AddressLookupTableAccount, Connection, PublicKey, VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
import type { TransactionProofBuilder, TransactionProofStruct } from "../proof.js";
|
|
3
|
+
import type { CompactNoteCipher } from "../compactNote.js";
|
|
4
|
+
/**
|
|
5
|
+
* Who owns the resulting note. Both halves are required and neither is derived
|
|
6
|
+
* from the other:
|
|
7
|
+
*
|
|
8
|
+
* - `veiloPublicKey` is the Poseidon field element bound into the output
|
|
9
|
+
* commitment. It is the **spending** authority.
|
|
10
|
+
* - `noteViewingKey` is the Ed25519 wallet key the compact cipher is encrypted
|
|
11
|
+
* to. It is the **detection** authority.
|
|
12
|
+
*
|
|
13
|
+
* If these two belong to different people, the note is *spendable but
|
|
14
|
+
* invisible*: the owner can never find it, so the funds are effectively gone.
|
|
15
|
+
* Nothing on-chain and nothing in this type can detect that — which is why
|
|
16
|
+
* `resolveShieldOwner()` exists and why hand-constructing this requires an
|
|
17
|
+
* explicit acknowledgement.
|
|
18
|
+
*
|
|
19
|
+
* See veilo-browser-extension/.../privateStealthWallet.ts:403-415 for the
|
|
20
|
+
* production comment describing this failure mode.
|
|
21
|
+
*/
|
|
22
|
+
export type ShieldOwner = {
|
|
23
|
+
veiloPublicKey: bigint;
|
|
24
|
+
noteViewingKey: PublicKey;
|
|
25
|
+
/**
|
|
26
|
+
* Set automatically by {@link resolveShieldOwner}, which fetches both halves
|
|
27
|
+
* from a single relayer response and can therefore guarantee they match.
|
|
28
|
+
*
|
|
29
|
+
* Hand-built owners must set this explicitly — it is the only mechanism
|
|
30
|
+
* standing between an integration mistake and silent fund loss.
|
|
31
|
+
*/
|
|
32
|
+
acknowledgeOwnerInvariant?: true;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Who signs and pays. Public key only — the SDK never receives a secret key.
|
|
36
|
+
*
|
|
37
|
+
* The signer is unavoidably the fee payer, the funding source, and the payer of
|
|
38
|
+
* ~0.00192 SOL of nullifier-marker rent (the program hardcodes
|
|
39
|
+
* `payer = relayer` on both marker accounts). A wallet with no SOL cannot
|
|
40
|
+
* shield through this path regardless of who submits the transaction.
|
|
41
|
+
*/
|
|
42
|
+
export type ShieldSigner = {
|
|
43
|
+
publicKey: PublicKey;
|
|
44
|
+
/**
|
|
45
|
+
* Source token account for SPL shields. Defaults to the signer's canonical
|
|
46
|
+
* ATA. Must be owned by `publicKey` with no delegate set.
|
|
47
|
+
*/
|
|
48
|
+
tokenAccount?: PublicKey;
|
|
49
|
+
};
|
|
50
|
+
/** Where the target tree and its root come from. */
|
|
51
|
+
export interface TreeSource {
|
|
52
|
+
getDepositTarget(mint: PublicKey): Promise<DepositTarget>;
|
|
53
|
+
}
|
|
54
|
+
export type DepositTarget = {
|
|
55
|
+
/** Always 0 — the tree whose root the dummy inputs nominally prove against. */
|
|
56
|
+
inputTreeId: number;
|
|
57
|
+
/** 32-byte root, read from that tree's on-chain root history. */
|
|
58
|
+
inputRoot: Uint8Array;
|
|
59
|
+
/** Tree the new commitments are appended to. */
|
|
60
|
+
outputTreeId: number;
|
|
61
|
+
/** `nextIndex` at build time. A prediction — see `predictedLeafIndex`. */
|
|
62
|
+
outputNextIndex: number;
|
|
63
|
+
};
|
|
64
|
+
/** Where the address lookup table comes from. */
|
|
65
|
+
export interface AltSource {
|
|
66
|
+
resolve(ctx: {
|
|
67
|
+
connection: Connection;
|
|
68
|
+
mint: PublicKey;
|
|
69
|
+
outputTreeId: number;
|
|
70
|
+
programId: PublicKey;
|
|
71
|
+
}): Promise<AddressLookupTableAccount>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Caching for values that are stable across builds (the ALT account, rent
|
|
75
|
+
* exemption minimums, pool config).
|
|
76
|
+
*
|
|
77
|
+
* Deliberately an interface: the three production deposit implementations
|
|
78
|
+
* differ on exactly this axis — `localStorage`, `chrome.storage`, and
|
|
79
|
+
* `AsyncStorage` — and none of them belongs in a published SDK.
|
|
80
|
+
*/
|
|
81
|
+
export interface ShieldCachePort {
|
|
82
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
83
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
export type PreflightOptions = {
|
|
86
|
+
/** Skip the SPL token-account inspection (owner, delegate, balance). */
|
|
87
|
+
skipTokenAccount?: boolean;
|
|
88
|
+
/** Skip the signer balance check. */
|
|
89
|
+
skipBalance?: boolean;
|
|
90
|
+
/** Skip pool state checks (paused, min/max, tree capacity). */
|
|
91
|
+
skipPoolState?: boolean;
|
|
92
|
+
};
|
|
93
|
+
export type ShieldParams = {
|
|
94
|
+
connection: Connection;
|
|
95
|
+
/** Net amount to shield, in base units. No fee is taken on deposits. */
|
|
96
|
+
amount: bigint;
|
|
97
|
+
/** Defaults to native SOL. */
|
|
98
|
+
mint?: PublicKey;
|
|
99
|
+
owner: ShieldOwner;
|
|
100
|
+
signer: ShieldSigner;
|
|
101
|
+
/** Proof generation. `createTransactionProver(artifacts)` satisfies this. */
|
|
102
|
+
prover: TransactionProofBuilder;
|
|
103
|
+
treeSource?: TreeSource;
|
|
104
|
+
alt?: AltSource;
|
|
105
|
+
cache?: ShieldCachePort;
|
|
106
|
+
programId?: PublicKey;
|
|
107
|
+
computeUnitLimit?: number;
|
|
108
|
+
/** Set to 0 to omit the priority-fee instruction entirely. */
|
|
109
|
+
computeUnitPriceMicroLamports?: number;
|
|
110
|
+
deadlineSeconds?: number;
|
|
111
|
+
/** Supply to avoid an RPC round-trip; otherwise fetched just before compiling. */
|
|
112
|
+
blockhash?: {
|
|
113
|
+
blockhash: string;
|
|
114
|
+
lastValidBlockHeight: number;
|
|
115
|
+
};
|
|
116
|
+
preflight?: boolean | PreflightOptions;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The recoverable secret material for the note this shield creates.
|
|
120
|
+
*
|
|
121
|
+
* SECRET. Anyone holding this can locate the note. It must never be persisted
|
|
122
|
+
* server-side in the clear or transmitted alongside {@link ShieldProofCache},
|
|
123
|
+
* which is deliberately public.
|
|
124
|
+
*/
|
|
125
|
+
export type ShieldNote = {
|
|
126
|
+
commitment: Uint8Array;
|
|
127
|
+
blinding: bigint;
|
|
128
|
+
amount: bigint;
|
|
129
|
+
ownerVeiloPublicKey: bigint;
|
|
130
|
+
mint: PublicKey;
|
|
131
|
+
treeId: number;
|
|
132
|
+
compact: CompactNoteCipher;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Everything needed to re-assemble the transaction with a fresh blockhash.
|
|
136
|
+
*
|
|
137
|
+
* PUBLIC and JSON-serializable by construction: it carries no blinding, no
|
|
138
|
+
* private key, and no note plaintext. A platform can build server-side, ship
|
|
139
|
+
* this to a client for signature, and rebuild in a different process without
|
|
140
|
+
* ever moving a secret.
|
|
141
|
+
*/
|
|
142
|
+
export type ShieldProofCache = {
|
|
143
|
+
version: 1;
|
|
144
|
+
programId: string;
|
|
145
|
+
mint: string;
|
|
146
|
+
signer: string;
|
|
147
|
+
altAddress: string;
|
|
148
|
+
inputTreeId: number;
|
|
149
|
+
outputTreeId: number;
|
|
150
|
+
root: string;
|
|
151
|
+
publicAmount: string;
|
|
152
|
+
extData: {
|
|
153
|
+
recipient: string;
|
|
154
|
+
relayer: string;
|
|
155
|
+
fee: string;
|
|
156
|
+
refund: string;
|
|
157
|
+
claimant: string;
|
|
158
|
+
};
|
|
159
|
+
extDataHash: string;
|
|
160
|
+
inputNullifiers: [string, string];
|
|
161
|
+
outputCommitments: [string, string];
|
|
162
|
+
proof: TransactionProofStruct;
|
|
163
|
+
noteCiphers: {
|
|
164
|
+
note0EphemeralKey: string;
|
|
165
|
+
note0Encrypted: string;
|
|
166
|
+
note0ViewTag: number;
|
|
167
|
+
note1EphemeralKey: string;
|
|
168
|
+
note1Encrypted: string;
|
|
169
|
+
note1ViewTag: number;
|
|
170
|
+
};
|
|
171
|
+
/** Resolved instruction accounts, keyed by IDL account name. */
|
|
172
|
+
accounts: Record<string, string>;
|
|
173
|
+
computeUnitLimit: number;
|
|
174
|
+
};
|
|
175
|
+
export type ShieldQuote = {
|
|
176
|
+
/** Serialized size including the signature. Must be <= 1232. */
|
|
177
|
+
serializedSize: number;
|
|
178
|
+
computeUnitLimit: number;
|
|
179
|
+
computeUnitPriceMicroLamports: number;
|
|
180
|
+
estimatedFeeLamports: bigint;
|
|
181
|
+
/** Nullifier-marker rent, permanently burned by the signer. */
|
|
182
|
+
estimatedRentLamports: bigint;
|
|
183
|
+
};
|
|
184
|
+
export type ShieldResult = {
|
|
185
|
+
/** UNSIGNED. The caller signs and submits — this SDK never broadcasts. */
|
|
186
|
+
transaction: VersionedTransaction;
|
|
187
|
+
blockhash: string;
|
|
188
|
+
lastValidBlockHeight: number;
|
|
189
|
+
deadline: bigint;
|
|
190
|
+
note: ShieldNote;
|
|
191
|
+
/**
|
|
192
|
+
* Leaf index the output commitment is *expected* to land at.
|
|
193
|
+
*
|
|
194
|
+
* A prediction, read before submission. Under concurrent deposits into the
|
|
195
|
+
* same tree it will be wrong. Use `finalizeShield()` to read the authoritative
|
|
196
|
+
* index from the confirmed transaction's `CommitmentEvent` before persisting
|
|
197
|
+
* anything that depends on it.
|
|
198
|
+
*/
|
|
199
|
+
predictedLeafIndex: number;
|
|
200
|
+
quote: ShieldQuote;
|
|
201
|
+
proofCache: ShieldProofCache;
|
|
202
|
+
};
|
package/dist/cjs/utxo.d.ts
CHANGED
|
@@ -182,6 +182,26 @@ export type DecryptedNote = {
|
|
|
182
182
|
mintAddress?: string;
|
|
183
183
|
treeId: number;
|
|
184
184
|
};
|
|
185
|
+
/** Derive 32-byte encryption key from a shared secret using SHA-512 truncated.
|
|
186
|
+
* Must match the production "sha256" helper which uses nacl.hash (SHA-512) [0:32].
|
|
187
|
+
*
|
|
188
|
+
* DO NOT "fix" this to real SHA-256. The name in the sibling implementations
|
|
189
|
+
* (`sha256ForNotes`) is a misnomer, but the bytes are load-bearing: changing the
|
|
190
|
+
* KDF makes every historical note undecryptable.
|
|
191
|
+
*
|
|
192
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
193
|
+
export declare function kdfSHA512_256(secret: Uint8Array): Uint8Array;
|
|
194
|
+
/** Convert an Ed25519 private seed (32 bytes) to an X25519 scalar.
|
|
195
|
+
* Equivalent to ed2curve.convertSecretKey(seed).
|
|
196
|
+
*
|
|
197
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
198
|
+
export declare function ed25519SeedToX25519Private(seed: Uint8Array): Uint8Array;
|
|
199
|
+
/** Convert an Ed25519 compressed public key (32 bytes) to X25519.
|
|
200
|
+
* Equivalent to ed2curve.convertPublicKey(pubkey).
|
|
201
|
+
* Edwards y-coord → Montgomery u via: u = (1 + y) / (1 - y) (mod p).
|
|
202
|
+
*
|
|
203
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
204
|
+
export declare function ed25519PublicKeyToX25519(pubkey: Uint8Array): Uint8Array;
|
|
185
205
|
/**
|
|
186
206
|
* Encrypt a UTXO note for a recipient identified by their Solana wallet public key.
|
|
187
207
|
* Produces a blob the relayer will store and serve via POST /notes/save.
|
package/dist/cjs/utxo.js
CHANGED
|
@@ -17,13 +17,16 @@ exports.inputUTXOToCircuitFormat = inputUTXOToCircuitFormat;
|
|
|
17
17
|
exports.deriveEncryptionKeypair = deriveEncryptionKeypair;
|
|
18
18
|
exports.encryptUTXONote = encryptUTXONote;
|
|
19
19
|
exports.decryptUTXONote = decryptUTXONote;
|
|
20
|
+
exports.kdfSHA512_256 = kdfSHA512_256;
|
|
21
|
+
exports.ed25519SeedToX25519Private = ed25519SeedToX25519Private;
|
|
22
|
+
exports.ed25519PublicKeyToX25519 = ed25519PublicKeyToX25519;
|
|
20
23
|
exports.encryptBlindMailboxNote = encryptBlindMailboxNote;
|
|
21
24
|
exports.decryptBlindMailboxNote = decryptBlindMailboxNote;
|
|
22
25
|
exports.fetchAndDecryptNotes = fetchAndDecryptNotes;
|
|
23
|
-
const
|
|
26
|
+
const random_js_1 = require("./random.js");
|
|
24
27
|
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
25
28
|
const sha256_1 = require("@noble/hashes/sha256");
|
|
26
|
-
const
|
|
29
|
+
const poseidon_js_1 = require("./poseidon.js");
|
|
27
30
|
// -----------------------------------------------------------------------------
|
|
28
31
|
// Keypair Functions
|
|
29
32
|
// -----------------------------------------------------------------------------
|
|
@@ -33,8 +36,8 @@ const poseidon_1 = require("./poseidon");
|
|
|
33
36
|
* Public key is derived via Poseidon: pubkey = Poseidon(privateKey)
|
|
34
37
|
*/
|
|
35
38
|
function generateKeypair() {
|
|
36
|
-
const privateKeyBytes = new Uint8Array((0,
|
|
37
|
-
const privateKey = (0,
|
|
39
|
+
const privateKeyBytes = new Uint8Array((0, random_js_1.randomBytes)(32));
|
|
40
|
+
const privateKey = (0, poseidon_js_1.bytesToBigIntBE)(privateKeyBytes) % poseidon_js_1.BN254_FR_MODULUS;
|
|
38
41
|
const publicKey = derivePublicKey(privateKey);
|
|
39
42
|
return { privateKey, publicKey };
|
|
40
43
|
}
|
|
@@ -43,15 +46,15 @@ function generateKeypair() {
|
|
|
43
46
|
* Matches circuit's Keypair template: publicKey = Poseidon(privateKey)
|
|
44
47
|
*/
|
|
45
48
|
function derivePublicKey(privateKey) {
|
|
46
|
-
return (0,
|
|
49
|
+
return (0, poseidon_js_1.poseidon1)(privateKey);
|
|
47
50
|
}
|
|
48
51
|
/**
|
|
49
52
|
* Create keypair from a known private key.
|
|
50
53
|
*/
|
|
51
54
|
function keypairFromPrivateKey(privateKey) {
|
|
52
55
|
return {
|
|
53
|
-
privateKey: privateKey %
|
|
54
|
-
publicKey: derivePublicKey(privateKey %
|
|
56
|
+
privateKey: privateKey % poseidon_js_1.BN254_FR_MODULUS,
|
|
57
|
+
publicKey: derivePublicKey(privateKey % poseidon_js_1.BN254_FR_MODULUS),
|
|
55
58
|
};
|
|
56
59
|
}
|
|
57
60
|
// -----------------------------------------------------------------------------
|
|
@@ -63,20 +66,20 @@ function keypairFromPrivateKey(privateKey) {
|
|
|
63
66
|
* commitment = Poseidon(amount, pubkey, blinding, mintAddress)
|
|
64
67
|
*/
|
|
65
68
|
function commitUTXO(utxo) {
|
|
66
|
-
const commitment = (0,
|
|
67
|
-
return (0,
|
|
69
|
+
const commitment = (0, poseidon_js_1.poseidon4)(utxo.amount, utxo.pubkey, utxo.blinding, utxo.mintAddress);
|
|
70
|
+
return (0, poseidon_js_1.bigIntToBytesBE)(commitment);
|
|
68
71
|
}
|
|
69
72
|
/**
|
|
70
73
|
* Create a random UTXO with the given parameters.
|
|
71
74
|
*/
|
|
72
75
|
function createUTXO(params) {
|
|
73
|
-
const blindingBytes = new Uint8Array((0,
|
|
74
|
-
const blinding = (0,
|
|
76
|
+
const blindingBytes = new Uint8Array((0, random_js_1.randomBytes)(32));
|
|
77
|
+
const blinding = (0, poseidon_js_1.bytesToBigIntBE)(blindingBytes) % poseidon_js_1.BN254_FR_MODULUS;
|
|
75
78
|
const utxo = {
|
|
76
79
|
amount: params.amount,
|
|
77
80
|
pubkey: params.pubkey,
|
|
78
81
|
blinding,
|
|
79
|
-
mintAddress: (0,
|
|
82
|
+
mintAddress: (0, poseidon_js_1.pubkeyToField)(params.mintAddress),
|
|
80
83
|
};
|
|
81
84
|
const commitment = commitUTXO(utxo);
|
|
82
85
|
return {
|
|
@@ -130,7 +133,7 @@ function createOwnedZeroUTXO(privateKey, mintAddress) {
|
|
|
130
133
|
* signature = Poseidon(privateKey, commitment, pathIndex)
|
|
131
134
|
*/
|
|
132
135
|
function computeSignature(privateKey, commitment, pathIndex) {
|
|
133
|
-
return (0,
|
|
136
|
+
return (0, poseidon_js_1.poseidon3)(privateKey, commitment, pathIndex);
|
|
134
137
|
}
|
|
135
138
|
/**
|
|
136
139
|
* Derive nullifier for a UTXO.
|
|
@@ -141,11 +144,11 @@ function computeSignature(privateKey, commitment, pathIndex) {
|
|
|
141
144
|
* The signature-based nullifier ensures only the private key holder can spend.
|
|
142
145
|
*/
|
|
143
146
|
function deriveNullifier(utxo, pathIndex, privateKey) {
|
|
144
|
-
const commitment = (0,
|
|
145
|
-
const pathIndexField = BigInt(pathIndex) %
|
|
147
|
+
const commitment = (0, poseidon_js_1.bytesToBigIntBE)(utxo.commitment) % poseidon_js_1.BN254_FR_MODULUS;
|
|
148
|
+
const pathIndexField = BigInt(pathIndex) % poseidon_js_1.BN254_FR_MODULUS;
|
|
146
149
|
const signature = computeSignature(privateKey, commitment, pathIndexField);
|
|
147
|
-
const nullifier = (0,
|
|
148
|
-
return (0,
|
|
150
|
+
const nullifier = (0, poseidon_js_1.poseidon3)(commitment, pathIndexField, signature);
|
|
151
|
+
return (0, poseidon_js_1.bigIntToBytesBE)(nullifier);
|
|
149
152
|
}
|
|
150
153
|
/**
|
|
151
154
|
* Convert InputUTXO to the format expected by the circuit.
|
|
@@ -155,7 +158,7 @@ function inputUTXOToCircuitFormat(input) {
|
|
|
155
158
|
amount: input.amount,
|
|
156
159
|
pubkey: input.pubkey,
|
|
157
160
|
blinding: input.blinding,
|
|
158
|
-
pathElements: input.pathElements.map((e) => (0,
|
|
161
|
+
pathElements: input.pathElements.map((e) => (0, poseidon_js_1.bytesToBigIntBE)(e) % poseidon_js_1.BN254_FR_MODULUS),
|
|
159
162
|
pathIndex: input.pathIndex,
|
|
160
163
|
privateKey: input.privateKey,
|
|
161
164
|
};
|
|
@@ -165,7 +168,7 @@ function inputUTXOToCircuitFormat(input) {
|
|
|
165
168
|
* The keypair is deterministic: same privateKey always yields the same pair.
|
|
166
169
|
*/
|
|
167
170
|
function deriveEncryptionKeypair(privateKey) {
|
|
168
|
-
const seed = (0, sha256_1.sha256)((0,
|
|
171
|
+
const seed = (0, sha256_1.sha256)((0, poseidon_js_1.bigIntToBytesBE)(privateKey));
|
|
169
172
|
return tweetnacl_1.default.box.keyPair.fromSecretKey(seed);
|
|
170
173
|
}
|
|
171
174
|
/**
|
|
@@ -189,8 +192,8 @@ function encryptUTXONote(utxo, recipientEncPubkey) {
|
|
|
189
192
|
}
|
|
190
193
|
const plaintext = new Uint8Array(72);
|
|
191
194
|
plaintext.set(amountBuf, 0);
|
|
192
|
-
plaintext.set((0,
|
|
193
|
-
plaintext.set((0,
|
|
195
|
+
plaintext.set((0, poseidon_js_1.bigIntToBytesBE)(utxo.blinding), 8);
|
|
196
|
+
plaintext.set((0, poseidon_js_1.bigIntToBytesBE)(utxo.privateKey), 40);
|
|
194
197
|
const sharedKey = tweetnacl_1.default.box.before(recipientEncPubkey, ephemeral.secretKey);
|
|
195
198
|
const ciphertext = tweetnacl_1.default.box.after(plaintext, nonce, sharedKey);
|
|
196
199
|
return { nonce, ciphertext, senderEphemeralPubkey: ephemeral.publicKey };
|
|
@@ -209,14 +212,14 @@ function decryptUTXONote(note, recipientPrivateKey, commitment, mintAddress) {
|
|
|
209
212
|
let amount = 0n;
|
|
210
213
|
for (let i = 7; i >= 0; i--)
|
|
211
214
|
amount = (amount << 8n) | BigInt(plaintext[i]);
|
|
212
|
-
const blinding = (0,
|
|
213
|
-
const privateKey = (0,
|
|
215
|
+
const blinding = (0, poseidon_js_1.bytesToBigIntBE)(plaintext.slice(8, 40)) % poseidon_js_1.BN254_FR_MODULUS;
|
|
216
|
+
const privateKey = (0, poseidon_js_1.bytesToBigIntBE)(plaintext.slice(40, 72)) % poseidon_js_1.BN254_FR_MODULUS;
|
|
214
217
|
const pubkey = derivePublicKey(privateKey);
|
|
215
218
|
const utxo = {
|
|
216
219
|
amount,
|
|
217
220
|
pubkey,
|
|
218
221
|
blinding,
|
|
219
|
-
mintAddress: (0,
|
|
222
|
+
mintAddress: (0, poseidon_js_1.pubkeyToField)(mintAddress),
|
|
220
223
|
};
|
|
221
224
|
const expectedCommitment = commitUTXO(utxo);
|
|
222
225
|
if (!expectedCommitment.every((b, i) => b === commitment[i]))
|
|
@@ -225,12 +228,20 @@ function decryptUTXONote(note, recipientPrivateKey, commitment, mintAddress) {
|
|
|
225
228
|
}
|
|
226
229
|
// -- Internal crypto helpers (no extra dependencies) --------------------------
|
|
227
230
|
/** Derive 32-byte encryption key from a shared secret using SHA-512 truncated.
|
|
228
|
-
* Must match the production "sha256" helper which uses nacl.hash (SHA-512) [0:32].
|
|
231
|
+
* Must match the production "sha256" helper which uses nacl.hash (SHA-512) [0:32].
|
|
232
|
+
*
|
|
233
|
+
* DO NOT "fix" this to real SHA-256. The name in the sibling implementations
|
|
234
|
+
* (`sha256ForNotes`) is a misnomer, but the bytes are load-bearing: changing the
|
|
235
|
+
* KDF makes every historical note undecryptable.
|
|
236
|
+
*
|
|
237
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
229
238
|
function kdfSHA512_256(secret) {
|
|
230
239
|
return tweetnacl_1.default.hash(secret).slice(0, 32);
|
|
231
240
|
}
|
|
232
241
|
/** Convert an Ed25519 private seed (32 bytes) to an X25519 scalar.
|
|
233
|
-
* Equivalent to ed2curve.convertSecretKey(seed).
|
|
242
|
+
* Equivalent to ed2curve.convertSecretKey(seed).
|
|
243
|
+
*
|
|
244
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
234
245
|
function ed25519SeedToX25519Private(seed) {
|
|
235
246
|
const h = tweetnacl_1.default.hash(seed); // SHA-512
|
|
236
247
|
const scalar = h.slice(0, 32);
|
|
@@ -253,7 +264,9 @@ function modpow25519(b, e) {
|
|
|
253
264
|
}
|
|
254
265
|
/** Convert an Ed25519 compressed public key (32 bytes) to X25519.
|
|
255
266
|
* Equivalent to ed2curve.convertPublicKey(pubkey).
|
|
256
|
-
* Edwards y-coord → Montgomery u via: u = (1 + y) / (1 - y) (mod p).
|
|
267
|
+
* Edwards y-coord → Montgomery u via: u = (1 + y) / (1 - y) (mod p).
|
|
268
|
+
*
|
|
269
|
+
* @internal Exported for `compactNote.ts`. Not part of the stable public API. */
|
|
257
270
|
function ed25519PublicKeyToX25519(pubkey) {
|
|
258
271
|
const pk = new Uint8Array(pubkey);
|
|
259
272
|
pk[31] &= 0x7f; // clear sign bit to get y as LE integer
|
|
@@ -284,7 +297,7 @@ function ed25519PublicKeyToX25519(pubkey) {
|
|
|
284
297
|
*/
|
|
285
298
|
function encryptBlindMailboxNote(recipientWalletPubkey, noteData) {
|
|
286
299
|
// 1. Generate ephemeral Ed25519 keypair seed
|
|
287
|
-
const ephemeralSeed = (0,
|
|
300
|
+
const ephemeralSeed = (0, random_js_1.randomBytes)(32);
|
|
288
301
|
// Ed25519 public key stored by relayer; dapp recovers X25519 via ed2curve.convertPublicKey()
|
|
289
302
|
const ephemeralEdPublicKey = tweetnacl_1.default.sign.keyPair.fromSeed(new Uint8Array(ephemeralSeed)).publicKey;
|
|
290
303
|
// X25519 scalar (ed2curve.convertSecretKey equivalent)
|