@auditable/privacy-pool-zk-sdk 0.7.0 → 0.9.1-rc.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/dist/ciphertext-blob.d.ts +17 -0
- package/dist/circuit-artifacts.d.ts +9 -0
- package/dist/cli.js +1366 -1017
- package/dist/cli.js.map +1 -1
- package/dist/derived-escrow-key.d.ts +10 -0
- package/dist/domain-separators.d.ts +16 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.mjs +1601 -1145
- package/dist/index.mjs.map +1 -1
- package/dist/kyt-flow.d.ts +2 -3
- package/dist/kyt-passage.d.ts +23 -12
- package/dist/output-note-encryption.d.ts +32 -2
- package/dist/prover.d.ts +3 -3
- package/dist/sdk.d.ts +44 -34
- package/dist/stealth-sign-message.d.ts +15 -2
- package/dist/stealth-signature.d.ts +13 -5
- package/dist/transaction-audit.d.ts +13 -12
- package/dist/types.d.ts +14 -4
- package/dist/withdrawal-transaction-input.d.ts +60 -45
- package/dist/witness.d.ts +2 -1
- package/dist/zk-artifact-url.d.ts +4 -0
- package/dist/zk-layout.d.ts +84 -0
- package/package.json +4 -9
- package/pkg/client_sdk_wasm.d.ts +29 -15
- package/pkg/client_sdk_wasm.js +129 -48
- package/pkg/client_sdk_wasm_bg.wasm +0 -0
- package/pkg/client_sdk_wasm_bg.wasm.d.ts +7 -5
- package/assets/main.wasm +0 -0
- package/assets/main_final.zkey +0 -0
- package/assets/witness_calculator.js +0 -383
- package/dist/kyt-onboarding.d.ts +0 -36
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { StrKey, xdr, nativeToScVal, Address, Keypair, rpc, TransactionBuilder, Operation, authorizeEntry } from '@stellar/stellar-sdk';
|
|
1
|
+
import { StrKey, Address, Keypair, rpc, TransactionBuilder, Operation, xdr, nativeToScVal, authorizeEntry } from '@stellar/stellar-sdk';
|
|
3
2
|
import { buildBabyjub, buildMimc7, buildPoseidon } from 'circomlibjs';
|
|
4
3
|
|
|
5
4
|
/* @ts-self-types="./client_sdk_wasm.d.ts" */
|
|
@@ -40,35 +39,69 @@ function buildWithdrawMerkleWitness(coin_json, state_json) {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
|
-
* Calculate nullifier hash from nullifier decimal
|
|
42
|
+
* Calculate owner-bound nullifier hash from nullifier and spend-scalar decimal strings.
|
|
44
43
|
* Returns hex string (0x...)
|
|
45
44
|
* @param {string} nullifier_decimal
|
|
45
|
+
* @param {string} priv_key_scalar_decimal
|
|
46
46
|
* @returns {string}
|
|
47
47
|
*/
|
|
48
|
-
function calculateNullifierHash(nullifier_decimal) {
|
|
49
|
-
let
|
|
50
|
-
let
|
|
48
|
+
function calculateNullifierHash(nullifier_decimal, priv_key_scalar_decimal) {
|
|
49
|
+
let deferred4_0;
|
|
50
|
+
let deferred4_1;
|
|
51
51
|
try {
|
|
52
52
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
53
53
|
const ptr0 = passStringToWasm0(nullifier_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
54
54
|
const len0 = WASM_VECTOR_LEN;
|
|
55
|
-
|
|
55
|
+
const ptr1 = passStringToWasm0(priv_key_scalar_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
56
|
+
const len1 = WASM_VECTOR_LEN;
|
|
57
|
+
wasm.calculateNullifierHash(retptr, ptr0, len0, ptr1, len1);
|
|
56
58
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
57
59
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
58
60
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
59
61
|
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
60
|
-
var
|
|
61
|
-
var
|
|
62
|
+
var ptr3 = r0;
|
|
63
|
+
var len3 = r1;
|
|
62
64
|
if (r3) {
|
|
63
|
-
|
|
65
|
+
ptr3 = 0; len3 = 0;
|
|
64
66
|
throw takeObject(r2);
|
|
65
67
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return getStringFromWasm0(
|
|
68
|
+
deferred4_0 = ptr3;
|
|
69
|
+
deferred4_1 = len3;
|
|
70
|
+
return getStringFromWasm0(ptr3, len3);
|
|
71
|
+
} finally {
|
|
72
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
73
|
+
wasm.__wbindgen_export4(deferred4_0, deferred4_1, 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `R = Poseidon255(2)(hi, lo)`; `scalar` is the raw Poseidon hash when it is
|
|
79
|
+
* already in `(0, BabyJub subgroup order)`. Out-of-range hashes fail so the
|
|
80
|
+
* caller can rejection-sample a new nonce.
|
|
81
|
+
* @param {string} nonce_decimal
|
|
82
|
+
* @param {string} recipient_hi_decimal
|
|
83
|
+
* @param {string} recipient_lo_decimal
|
|
84
|
+
* @returns {any}
|
|
85
|
+
*/
|
|
86
|
+
function derivedEscrowKey$1(nonce_decimal, recipient_hi_decimal, recipient_lo_decimal) {
|
|
87
|
+
try {
|
|
88
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
89
|
+
const ptr0 = passStringToWasm0(nonce_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
90
|
+
const len0 = WASM_VECTOR_LEN;
|
|
91
|
+
const ptr1 = passStringToWasm0(recipient_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
92
|
+
const len1 = WASM_VECTOR_LEN;
|
|
93
|
+
const ptr2 = passStringToWasm0(recipient_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
94
|
+
const len2 = WASM_VECTOR_LEN;
|
|
95
|
+
wasm.derivedEscrowKey(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
|
96
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
97
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
98
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
99
|
+
if (r2) {
|
|
100
|
+
throw takeObject(r1);
|
|
101
|
+
}
|
|
102
|
+
return takeObject(r0);
|
|
69
103
|
} finally {
|
|
70
104
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
71
|
-
wasm.__wbindgen_export4(deferred3_0, deferred3_1, 1);
|
|
72
105
|
}
|
|
73
106
|
}
|
|
74
107
|
|
|
@@ -149,26 +182,28 @@ function ecdhSharedKey(priv_hex, pub_x_hex, pub_y_hex) {
|
|
|
149
182
|
}
|
|
150
183
|
|
|
151
184
|
/**
|
|
152
|
-
* Generate a new coin with random nullifier, secret, and
|
|
153
|
-
* `
|
|
185
|
+
* Generate a new coin with random nullifier, secret, and owner public-key field elements.
|
|
186
|
+
* `amount_decimal` is an arbitrary-precision decimal field element (never JS `number`).
|
|
154
187
|
* `asset_hi_decimal` / `asset_lo_decimal` are decimal Fr strings for the Stellar asset contract id (two limbs).
|
|
155
188
|
* Returns JSON: { coin: { value, nullifier, secret, commitment, asset_hi, asset_lo }, commitment_hex, precommitement_hex }
|
|
156
|
-
* @param {
|
|
189
|
+
* @param {string} amount_decimal
|
|
157
190
|
* @param {string} asset_hi_decimal
|
|
158
191
|
* @param {string} asset_lo_decimal
|
|
159
192
|
* @param {string} application_id_decimal
|
|
160
193
|
* @returns {any}
|
|
161
194
|
*/
|
|
162
|
-
function generateCoin(
|
|
195
|
+
function generateCoin(amount_decimal, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
163
196
|
try {
|
|
164
197
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
165
|
-
const ptr0 = passStringToWasm0(
|
|
198
|
+
const ptr0 = passStringToWasm0(amount_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
166
199
|
const len0 = WASM_VECTOR_LEN;
|
|
167
|
-
const ptr1 = passStringToWasm0(
|
|
200
|
+
const ptr1 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
168
201
|
const len1 = WASM_VECTOR_LEN;
|
|
169
|
-
const ptr2 = passStringToWasm0(
|
|
202
|
+
const ptr2 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
170
203
|
const len2 = WASM_VECTOR_LEN;
|
|
171
|
-
|
|
204
|
+
const ptr3 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
205
|
+
const len3 = WASM_VECTOR_LEN;
|
|
206
|
+
wasm.generateCoin(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
172
207
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
173
208
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
174
209
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -182,32 +217,34 @@ function generateCoin(amount, asset_hi_decimal, asset_lo_decimal, application_id
|
|
|
182
217
|
}
|
|
183
218
|
|
|
184
219
|
/**
|
|
185
|
-
* `Poseidon₁(scalar)` secret +
|
|
220
|
+
* `Poseidon₁(scalar)` secret + recipient owner public key (hex), matching an aligned deposit witness.
|
|
186
221
|
* @param {string} scalar_hex
|
|
187
|
-
* @param {string}
|
|
188
|
-
* @param {string}
|
|
189
|
-
* @param {
|
|
222
|
+
* @param {string} owner_x_hex
|
|
223
|
+
* @param {string} owner_y_hex
|
|
224
|
+
* @param {string} amount_decimal
|
|
190
225
|
* @param {string} asset_hi_decimal
|
|
191
226
|
* @param {string} asset_lo_decimal
|
|
192
227
|
* @param {string} application_id_decimal
|
|
193
228
|
* @returns {any}
|
|
194
229
|
*/
|
|
195
|
-
function
|
|
230
|
+
function generateCoinForDepositWithOwnerPubHex(scalar_hex, owner_x_hex, owner_y_hex, amount_decimal, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
196
231
|
try {
|
|
197
232
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
198
233
|
const ptr0 = passStringToWasm0(scalar_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
199
234
|
const len0 = WASM_VECTOR_LEN;
|
|
200
|
-
const ptr1 = passStringToWasm0(
|
|
235
|
+
const ptr1 = passStringToWasm0(owner_x_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
201
236
|
const len1 = WASM_VECTOR_LEN;
|
|
202
|
-
const ptr2 = passStringToWasm0(
|
|
237
|
+
const ptr2 = passStringToWasm0(owner_y_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
203
238
|
const len2 = WASM_VECTOR_LEN;
|
|
204
|
-
const ptr3 = passStringToWasm0(
|
|
239
|
+
const ptr3 = passStringToWasm0(amount_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
205
240
|
const len3 = WASM_VECTOR_LEN;
|
|
206
|
-
const ptr4 = passStringToWasm0(
|
|
241
|
+
const ptr4 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
207
242
|
const len4 = WASM_VECTOR_LEN;
|
|
208
|
-
const ptr5 = passStringToWasm0(
|
|
243
|
+
const ptr5 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
209
244
|
const len5 = WASM_VECTOR_LEN;
|
|
210
|
-
|
|
245
|
+
const ptr6 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
246
|
+
const len6 = WASM_VECTOR_LEN;
|
|
247
|
+
wasm.generateCoinForDepositWithOwnerPubHex(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6);
|
|
211
248
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
212
249
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
213
250
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -223,24 +260,26 @@ function generateCoinForDepositWithSharedHex(scalar_hex, shared_x_hex, shared_y_
|
|
|
223
260
|
/**
|
|
224
261
|
* `secret` in coin = `Poseidon255(1)(scalar)` per `deposit.circom`; scalar is 32-byte hex (64 chars, optional `0x`).
|
|
225
262
|
* @param {string} scalar_hex
|
|
226
|
-
* @param {
|
|
263
|
+
* @param {string} amount_decimal
|
|
227
264
|
* @param {string} asset_hi_decimal
|
|
228
265
|
* @param {string} asset_lo_decimal
|
|
229
266
|
* @param {string} application_id_decimal
|
|
230
267
|
* @returns {any}
|
|
231
268
|
*/
|
|
232
|
-
function generateCoinFromDepositEphemeralScalarHex(scalar_hex,
|
|
269
|
+
function generateCoinFromDepositEphemeralScalarHex(scalar_hex, amount_decimal, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
233
270
|
try {
|
|
234
271
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
235
272
|
const ptr0 = passStringToWasm0(scalar_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
236
273
|
const len0 = WASM_VECTOR_LEN;
|
|
237
|
-
const ptr1 = passStringToWasm0(
|
|
274
|
+
const ptr1 = passStringToWasm0(amount_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
238
275
|
const len1 = WASM_VECTOR_LEN;
|
|
239
|
-
const ptr2 = passStringToWasm0(
|
|
276
|
+
const ptr2 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
240
277
|
const len2 = WASM_VECTOR_LEN;
|
|
241
|
-
const ptr3 = passStringToWasm0(
|
|
278
|
+
const ptr3 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
242
279
|
const len3 = WASM_VECTOR_LEN;
|
|
243
|
-
|
|
280
|
+
const ptr4 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
281
|
+
const len4 = WASM_VECTOR_LEN;
|
|
282
|
+
wasm.generateCoinFromDepositEphemeralScalarHex(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4);
|
|
244
283
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
245
284
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
246
285
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -254,29 +293,31 @@ function generateCoinFromDepositEphemeralScalarHex(scalar_hex, amount, asset_hi_
|
|
|
254
293
|
}
|
|
255
294
|
|
|
256
295
|
/**
|
|
257
|
-
* Same as `generateCoin`, but commitment uses the given
|
|
258
|
-
* @param {string}
|
|
259
|
-
* @param {string}
|
|
260
|
-
* @param {
|
|
296
|
+
* Same as `generateCoin`, but commitment uses the given owner public key (64-char hex coords).
|
|
297
|
+
* @param {string} owner_x_hex
|
|
298
|
+
* @param {string} owner_y_hex
|
|
299
|
+
* @param {string} amount_decimal
|
|
261
300
|
* @param {string} asset_hi_decimal
|
|
262
301
|
* @param {string} asset_lo_decimal
|
|
263
302
|
* @param {string} application_id_decimal
|
|
264
303
|
* @returns {any}
|
|
265
304
|
*/
|
|
266
|
-
function
|
|
305
|
+
function generateCoinWithOwnerPubHex(owner_x_hex, owner_y_hex, amount_decimal, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
267
306
|
try {
|
|
268
307
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
269
|
-
const ptr0 = passStringToWasm0(
|
|
308
|
+
const ptr0 = passStringToWasm0(owner_x_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
270
309
|
const len0 = WASM_VECTOR_LEN;
|
|
271
|
-
const ptr1 = passStringToWasm0(
|
|
310
|
+
const ptr1 = passStringToWasm0(owner_y_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
272
311
|
const len1 = WASM_VECTOR_LEN;
|
|
273
|
-
const ptr2 = passStringToWasm0(
|
|
312
|
+
const ptr2 = passStringToWasm0(amount_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
274
313
|
const len2 = WASM_VECTOR_LEN;
|
|
275
|
-
const ptr3 = passStringToWasm0(
|
|
314
|
+
const ptr3 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
276
315
|
const len3 = WASM_VECTOR_LEN;
|
|
277
|
-
const ptr4 = passStringToWasm0(
|
|
316
|
+
const ptr4 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
278
317
|
const len4 = WASM_VECTOR_LEN;
|
|
279
|
-
|
|
318
|
+
const ptr5 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
319
|
+
const len5 = WASM_VECTOR_LEN;
|
|
320
|
+
wasm.generateCoinWithOwnerPubHex(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5);
|
|
280
321
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
281
322
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
282
323
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -313,6 +354,38 @@ function proofToHex(proof_json) {
|
|
|
313
354
|
}
|
|
314
355
|
}
|
|
315
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Graph witness + arkworks Groth16 prove. Returns `{ proof_hex, public_hex }`.
|
|
359
|
+
* @param {Uint8Array} graph
|
|
360
|
+
* @param {Uint8Array} proving_key
|
|
361
|
+
* @param {Uint8Array} r1cs
|
|
362
|
+
* @param {string} inputs_json
|
|
363
|
+
* @returns {any}
|
|
364
|
+
*/
|
|
365
|
+
function proveGroth16(graph, proving_key, r1cs, inputs_json) {
|
|
366
|
+
try {
|
|
367
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
368
|
+
const ptr0 = passArray8ToWasm0(graph, wasm.__wbindgen_export);
|
|
369
|
+
const len0 = WASM_VECTOR_LEN;
|
|
370
|
+
const ptr1 = passArray8ToWasm0(proving_key, wasm.__wbindgen_export);
|
|
371
|
+
const len1 = WASM_VECTOR_LEN;
|
|
372
|
+
const ptr2 = passArray8ToWasm0(r1cs, wasm.__wbindgen_export);
|
|
373
|
+
const len2 = WASM_VECTOR_LEN;
|
|
374
|
+
const ptr3 = passStringToWasm0(inputs_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
375
|
+
const len3 = WASM_VECTOR_LEN;
|
|
376
|
+
wasm.proveGroth16(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
377
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
378
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
379
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
380
|
+
if (r2) {
|
|
381
|
+
throw takeObject(r1);
|
|
382
|
+
}
|
|
383
|
+
return takeObject(r0);
|
|
384
|
+
} finally {
|
|
385
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
316
389
|
/**
|
|
317
390
|
* Convert snarkjs public signals JSON to hex bytes for Soroban contract.
|
|
318
391
|
* @param {string} public_json
|
|
@@ -523,6 +596,13 @@ function isLikeNone(x) {
|
|
|
523
596
|
return x === undefined || x === null;
|
|
524
597
|
}
|
|
525
598
|
|
|
599
|
+
function passArray8ToWasm0(arg, malloc) {
|
|
600
|
+
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
|
601
|
+
getUint8ArrayMemory0().set(arg, ptr / 1);
|
|
602
|
+
WASM_VECTOR_LEN = arg.length;
|
|
603
|
+
return ptr;
|
|
604
|
+
}
|
|
605
|
+
|
|
526
606
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
527
607
|
if (realloc === undefined) {
|
|
528
608
|
const buf = cachedTextEncoder.encode(arg);
|
|
@@ -689,24 +769,26 @@ var wasmBindings = /*#__PURE__*/Object.freeze({
|
|
|
689
769
|
buildWithdrawMerkleWitness: buildWithdrawMerkleWitness,
|
|
690
770
|
calculateNullifierHash: calculateNullifierHash,
|
|
691
771
|
default: __wbg_init,
|
|
772
|
+
derivedEscrowKey: derivedEscrowKey$1,
|
|
692
773
|
ecdhEphemeralPublicKey: ecdhEphemeralPublicKey,
|
|
693
774
|
ecdhEphemeralPublicKeyFromScalarHex: ecdhEphemeralPublicKeyFromScalarHex,
|
|
694
775
|
ecdhSharedKey: ecdhSharedKey,
|
|
695
776
|
generateCoin: generateCoin,
|
|
696
|
-
|
|
777
|
+
generateCoinForDepositWithOwnerPubHex: generateCoinForDepositWithOwnerPubHex,
|
|
697
778
|
generateCoinFromDepositEphemeralScalarHex: generateCoinFromDepositEphemeralScalarHex,
|
|
698
|
-
|
|
779
|
+
generateCoinWithOwnerPubHex: generateCoinWithOwnerPubHex,
|
|
699
780
|
initSync: initSync,
|
|
700
781
|
proofToHex: proofToHex,
|
|
782
|
+
proveGroth16: proveGroth16,
|
|
701
783
|
publicToHex: publicToHex
|
|
702
784
|
});
|
|
703
785
|
|
|
704
|
-
const isNode$
|
|
786
|
+
const isNode$1 = typeof process !== 'undefined' && !!process.versions?.node;
|
|
705
787
|
let wasmModule = null;
|
|
706
788
|
async function loadWasm(wasmBinary) {
|
|
707
789
|
if (wasmModule)
|
|
708
790
|
return wasmModule;
|
|
709
|
-
if (isNode$
|
|
791
|
+
if (isNode$1) {
|
|
710
792
|
if (wasmBinary) {
|
|
711
793
|
initSync({ module: wasmBinary });
|
|
712
794
|
}
|
|
@@ -733,468 +815,301 @@ async function loadWasm(wasmBinary) {
|
|
|
733
815
|
return wasmModule;
|
|
734
816
|
}
|
|
735
817
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
} else if (code == 5) {
|
|
771
|
-
err = "Not enough memory.\n";
|
|
772
|
-
} else if (code == 6) {
|
|
773
|
-
err = "Input signal array access exceeds the size.\n";
|
|
774
|
-
} else if (code == 7) {
|
|
775
|
-
err = "Out of bounds array access.\n";
|
|
776
|
-
} else {
|
|
777
|
-
err = "Unknown error.\n";
|
|
778
|
-
}
|
|
779
|
-
throw new Error(err + errStr);
|
|
780
|
-
},
|
|
781
|
-
printErrorMessage : function() {
|
|
782
|
-
errStr += getMessage() + "\n";
|
|
783
|
-
// console.error(getMessage());
|
|
784
|
-
},
|
|
785
|
-
writeBufferMessage : function() {
|
|
786
|
-
const msg = getMessage();
|
|
787
|
-
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
|
788
|
-
if (msg === "\n") {
|
|
789
|
-
console.log(msgStr);
|
|
790
|
-
msgStr = "";
|
|
791
|
-
} else {
|
|
792
|
-
// If we've buffered other content, put a space in between the items
|
|
793
|
-
if (msgStr !== "") {
|
|
794
|
-
msgStr += " ";
|
|
795
|
-
}
|
|
796
|
-
// Then append the message to the message we are creating
|
|
797
|
-
msgStr += msg;
|
|
798
|
-
}
|
|
799
|
-
},
|
|
800
|
-
showSharedRWMemory : function() {
|
|
801
|
-
printSharedRWMemory ();
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
const sanityCheck =
|
|
808
|
-
options;
|
|
809
|
-
// options &&
|
|
810
|
-
// (
|
|
811
|
-
// options.sanityCheck ||
|
|
812
|
-
// options.logGetSignal ||
|
|
813
|
-
// options.logSetSignal ||
|
|
814
|
-
// options.logStartComponent ||
|
|
815
|
-
// options.logFinishComponent
|
|
816
|
-
// );
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
wc = new WitnessCalculator(instance, sanityCheck);
|
|
820
|
-
return wc;
|
|
821
|
-
|
|
822
|
-
function getMessage() {
|
|
823
|
-
var message = "";
|
|
824
|
-
var c = instance.exports.getMessageChar();
|
|
825
|
-
while ( c != 0 ) {
|
|
826
|
-
message += String.fromCharCode(c);
|
|
827
|
-
c = instance.exports.getMessageChar();
|
|
828
|
-
}
|
|
829
|
-
return message;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
function printSharedRWMemory () {
|
|
833
|
-
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
|
|
834
|
-
const arr = new Uint32Array(shared_rw_memory_size);
|
|
835
|
-
for (let j=0; j<shared_rw_memory_size; j++) {
|
|
836
|
-
arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// If we've buffered other content, put a space in between the items
|
|
840
|
-
if (msgStr !== "") {
|
|
841
|
-
msgStr += " ";
|
|
842
|
-
}
|
|
843
|
-
// Then append the value to the message we are creating
|
|
844
|
-
msgStr += (fromArray32(arr).toString());
|
|
845
|
-
}
|
|
846
|
-
|
|
818
|
+
/** Off-chain ZK layout table. Must stay byte-identical to `libs/zk::ZkLayoutParams`. */
|
|
819
|
+
const LEGACY_ZK_NONCE = 0n;
|
|
820
|
+
const COMMITMENT_V2_ZK_NONCE = 2n;
|
|
821
|
+
const BINDING_ZK_NONCE = 3n;
|
|
822
|
+
const SIX_BY_SIX_ZK_NONCE = 6n;
|
|
823
|
+
const SIX_BY_SIX_BINDING_ZK_NONCE = 7n;
|
|
824
|
+
const LEGACY_PUBLIC_INPUT_PREFIX_LEN = 5;
|
|
825
|
+
const V2_PUBLIC_INPUT_PREFIX_LEN = 7;
|
|
826
|
+
const NOTE_AUDIT_LEN$3 = 12;
|
|
827
|
+
const NOTE_OUTPUT_LEN$2 = 6;
|
|
828
|
+
var BundledZkCircuit;
|
|
829
|
+
(function (BundledZkCircuit) {
|
|
830
|
+
BundledZkCircuit["TwoByTwo"] = "2x2";
|
|
831
|
+
BundledZkCircuit["SixBySix"] = "6x6";
|
|
832
|
+
})(BundledZkCircuit || (BundledZkCircuit = {}));
|
|
833
|
+
function isDevZkCircuitConfig(config) {
|
|
834
|
+
return !('circuit' in config);
|
|
835
|
+
}
|
|
836
|
+
const DEFAULT_ZK_CONFIG_NONCE = BINDING_ZK_NONCE;
|
|
837
|
+
const DEFAULT_ZK_CIRCUITS = {
|
|
838
|
+
[BINDING_ZK_NONCE.toString()]: {
|
|
839
|
+
nIns: 2,
|
|
840
|
+
nOuts: 2,
|
|
841
|
+
publicNInputs: 1,
|
|
842
|
+
publicNOutputs: 1,
|
|
843
|
+
circuit: BundledZkCircuit.TwoByTwo,
|
|
844
|
+
},
|
|
845
|
+
[SIX_BY_SIX_BINDING_ZK_NONCE.toString()]: {
|
|
846
|
+
nIns: 6,
|
|
847
|
+
nOuts: 6,
|
|
848
|
+
publicNInputs: 1,
|
|
849
|
+
publicNOutputs: 1,
|
|
850
|
+
circuit: BundledZkCircuit.SixBySix,
|
|
851
|
+
},
|
|
847
852
|
};
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const arr = new Uint32Array(this.n32);
|
|
858
|
-
for (let i=0; i<this.n32; i++) {
|
|
859
|
-
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
|
860
|
-
}
|
|
861
|
-
this.prime = fromArray32(arr);
|
|
862
|
-
|
|
863
|
-
this.witnessSize = this.instance.exports.getWitnessSize();
|
|
864
|
-
|
|
865
|
-
this.sanityCheck = sanityCheck;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
circom_version() {
|
|
869
|
-
return this.instance.exports.getVersion();
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
async _doCalculateWitness(input_orig, sanityCheck) {
|
|
873
|
-
//input is assumed to be a map from signals to arrays of bigints
|
|
874
|
-
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
|
875
|
-
let prefix = "";
|
|
876
|
-
var input = new Object();
|
|
877
|
-
//console.log("Input: ", input_orig);
|
|
878
|
-
qualify_input(prefix,input_orig,input);
|
|
879
|
-
//console.log("Input after: ",input);
|
|
880
|
-
const keys = Object.keys(input);
|
|
881
|
-
var input_counter = 0;
|
|
882
|
-
keys.forEach( (k) => {
|
|
883
|
-
const h = fnvHash(k);
|
|
884
|
-
const hMSB = parseInt(h.slice(0,8), 16);
|
|
885
|
-
const hLSB = parseInt(h.slice(8,16), 16);
|
|
886
|
-
const fArr = flatArray(input[k]);
|
|
887
|
-
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
|
888
|
-
if (signalSize < 0){
|
|
889
|
-
throw new Error(`Signal ${k} not found\n`);
|
|
890
|
-
}
|
|
891
|
-
if (fArr.length < signalSize) {
|
|
892
|
-
throw new Error(`Not enough values for input signal ${k}\n`);
|
|
893
|
-
}
|
|
894
|
-
if (fArr.length > signalSize) {
|
|
895
|
-
throw new Error(`Too many values for input signal ${k}\n`);
|
|
896
|
-
}
|
|
897
|
-
for (let i=0; i<fArr.length; i++) {
|
|
898
|
-
const arrFr = toArray32(normalize(fArr[i],this.prime),this.n32);
|
|
899
|
-
for (let j=0; j<this.n32; j++) {
|
|
900
|
-
this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
|
|
901
|
-
}
|
|
902
|
-
try {
|
|
903
|
-
this.instance.exports.setInputSignal(hMSB, hLSB,i);
|
|
904
|
-
input_counter++;
|
|
905
|
-
} catch (err) {
|
|
906
|
-
// console.log(`After adding signal ${i} of ${k}`)
|
|
907
|
-
throw new Error(err);
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
});
|
|
912
|
-
if (input_counter < this.instance.exports.getInputSize()) {
|
|
913
|
-
throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
|
|
914
|
-
}
|
|
853
|
+
function layoutFromCircuitConfig(config) {
|
|
854
|
+
return zkLayoutFromShapeBinding(config.nIns, config.nOuts, config.publicNInputs, config.publicNOutputs, config.nIns + config.nOuts, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2);
|
|
855
|
+
}
|
|
856
|
+
function resolveZkCircuitConfig(zkCircuits, zkConfigNonce) {
|
|
857
|
+
const nonce = zkConfigNonce ?? DEFAULT_ZK_CONFIG_NONCE;
|
|
858
|
+
const circuits = zkCircuits ?? DEFAULT_ZK_CIRCUITS;
|
|
859
|
+
const config = circuits[nonce.toString()];
|
|
860
|
+
if (!config) {
|
|
861
|
+
throw new Error(`no zk circuit configured for nonce ${nonce.toString()}`);
|
|
915
862
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
}
|
|
928
|
-
w.push(fromArray32(arr));
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
return w;
|
|
863
|
+
return { nonce, config };
|
|
864
|
+
}
|
|
865
|
+
function bundledCircuitFileNames(circuit) {
|
|
866
|
+
if (circuit === BundledZkCircuit.SixBySix) {
|
|
867
|
+
return {
|
|
868
|
+
graphFileName: 'main_6x6.graph.bin',
|
|
869
|
+
r1csFileName: 'main_6x6.r1cs.gz',
|
|
870
|
+
provingKeyFileName: 'main_6x6_proving_key.bin',
|
|
871
|
+
verificationKeyFileName: 'main_6x6_verification_key.json',
|
|
872
|
+
zkeyFileName: 'main_6x6_final.zkey',
|
|
873
|
+
};
|
|
932
874
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
875
|
+
return {
|
|
876
|
+
graphFileName: 'main.graph.bin',
|
|
877
|
+
r1csFileName: 'main.r1cs.gz',
|
|
878
|
+
provingKeyFileName: 'main_proving_key.bin',
|
|
879
|
+
verificationKeyFileName: 'main_verification_key.json',
|
|
880
|
+
zkeyFileName: 'main_final.zkey',
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function zkLayoutFromShape(nIns, nOuts, publicNInputs, publicNOutputs, nAuditSlots, noteAuditLen, noteOutputLen, publicInputPrefixLen = LEGACY_PUBLIC_INPUT_PREFIX_LEN) {
|
|
884
|
+
const auditOffset = nIns + nOuts + nOuts * 2;
|
|
885
|
+
const outputNoteOffset = auditOffset + nAuditSlots * 2 + nAuditSlots * noteAuditLen + nAuditSlots;
|
|
886
|
+
return {
|
|
887
|
+
nIns,
|
|
888
|
+
nOuts,
|
|
889
|
+
publicNInputs,
|
|
890
|
+
publicNOutputs,
|
|
891
|
+
nAuditSlots,
|
|
892
|
+
noteAuditLen,
|
|
893
|
+
noteOutputLen,
|
|
894
|
+
publicInputPrefixLen,
|
|
895
|
+
auditOffset,
|
|
896
|
+
outputNoteOffset,
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function zkLayoutFromShapeBinding(nIns, nOuts, publicNInputs, publicNOutputs, nAuditSlots, noteAuditLen, noteOutputLen) {
|
|
900
|
+
const auditOffset = nIns + nOuts + nOuts * 2;
|
|
901
|
+
const outputNoteOffset = auditOffset + 2 + nAuditSlots;
|
|
902
|
+
return {
|
|
903
|
+
nIns,
|
|
904
|
+
nOuts,
|
|
905
|
+
publicNInputs,
|
|
906
|
+
publicNOutputs,
|
|
907
|
+
nAuditSlots,
|
|
908
|
+
noteAuditLen,
|
|
909
|
+
noteOutputLen,
|
|
910
|
+
publicInputPrefixLen: V2_PUBLIC_INPUT_PREFIX_LEN,
|
|
911
|
+
auditOffset,
|
|
912
|
+
outputNoteOffset,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function ciphertextsInPublicSignals(layout) {
|
|
916
|
+
const withCiphertext = layout.auditOffset +
|
|
917
|
+
layout.nAuditSlots * 2 +
|
|
918
|
+
layout.nAuditSlots * layout.noteAuditLen +
|
|
919
|
+
layout.nAuditSlots;
|
|
920
|
+
return layout.outputNoteOffset === withCiphertext;
|
|
921
|
+
}
|
|
922
|
+
function ciphertextFieldCount(layout) {
|
|
923
|
+
return layout.nAuditSlots * layout.noteAuditLen + layout.nOuts * layout.noteOutputLen;
|
|
924
|
+
}
|
|
925
|
+
function auditEphemeralPublicKeyCount(layout) {
|
|
926
|
+
return ciphertextsInPublicSignals(layout) ? layout.nAuditSlots : 1;
|
|
927
|
+
}
|
|
928
|
+
function indexAuditTags(layout) {
|
|
929
|
+
if (ciphertextsInPublicSignals(layout)) {
|
|
930
|
+
return (layout.auditOffset +
|
|
931
|
+
layout.nAuditSlots * 2 +
|
|
932
|
+
layout.nAuditSlots * layout.noteAuditLen);
|
|
950
933
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const buff = new Uint8Array( buff32.buffer);
|
|
957
|
-
await this._doCalculateWitness(input, sanityCheck);
|
|
958
|
-
|
|
959
|
-
//"wtns"
|
|
960
|
-
buff[0] = "w".charCodeAt(0);
|
|
961
|
-
buff[1] = "t".charCodeAt(0);
|
|
962
|
-
buff[2] = "n".charCodeAt(0);
|
|
963
|
-
buff[3] = "s".charCodeAt(0);
|
|
964
|
-
|
|
965
|
-
//version 2
|
|
966
|
-
buff32[1] = 2;
|
|
967
|
-
|
|
968
|
-
//number of sections: 2
|
|
969
|
-
buff32[2] = 2;
|
|
970
|
-
|
|
971
|
-
//id section 1
|
|
972
|
-
buff32[3] = 1;
|
|
973
|
-
|
|
974
|
-
const n8 = this.n32*4;
|
|
975
|
-
//id section 1 length in 64bytes
|
|
976
|
-
const idSection1length = 8 + n8;
|
|
977
|
-
const idSection1lengthHex = idSection1length.toString(16);
|
|
978
|
-
buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
|
|
979
|
-
buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
|
|
980
|
-
|
|
981
|
-
//this.n32
|
|
982
|
-
buff32[6] = n8;
|
|
983
|
-
|
|
984
|
-
//prime number
|
|
985
|
-
this.instance.exports.getRawPrime();
|
|
986
|
-
|
|
987
|
-
var pos = 7;
|
|
988
|
-
for (let j=0; j<this.n32; j++) {
|
|
989
|
-
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
|
990
|
-
}
|
|
991
|
-
pos += this.n32;
|
|
992
|
-
|
|
993
|
-
// witness size
|
|
994
|
-
buff32[pos] = this.witnessSize;
|
|
995
|
-
pos++;
|
|
996
|
-
|
|
997
|
-
//id section 2
|
|
998
|
-
buff32[pos] = 2;
|
|
999
|
-
pos++;
|
|
1000
|
-
|
|
1001
|
-
// section 2 length
|
|
1002
|
-
const idSection2length = n8*this.witnessSize;
|
|
1003
|
-
const idSection2lengthHex = idSection2length.toString(16);
|
|
1004
|
-
buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
|
|
1005
|
-
buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
|
|
1006
|
-
|
|
1007
|
-
pos += 2;
|
|
1008
|
-
for (let i=0; i<this.witnessSize; i++) {
|
|
1009
|
-
this.instance.exports.getWitness(i);
|
|
1010
|
-
for (let j=0; j<this.n32; j++) {
|
|
1011
|
-
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
|
1012
|
-
}
|
|
1013
|
-
pos += this.n32;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
return buff;
|
|
934
|
+
return layout.auditOffset + auditEphemeralPublicKeyCount(layout) * 2;
|
|
935
|
+
}
|
|
936
|
+
function indexOutputNoteTags(layout) {
|
|
937
|
+
if (ciphertextsInPublicSignals(layout)) {
|
|
938
|
+
return layout.outputNoteOffset + layout.nOuts * layout.noteOutputLen;
|
|
1017
939
|
}
|
|
1018
|
-
|
|
940
|
+
return layout.outputNoteOffset;
|
|
1019
941
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
if (Array.isArray(input)) {
|
|
1024
|
-
for (let i = 0; i<input.length; i++) {
|
|
1025
|
-
let new_prefix = prefix + "[" + i + "]";
|
|
1026
|
-
qualify_input_list(new_prefix,input[i],input1);
|
|
1027
|
-
}
|
|
1028
|
-
} else {
|
|
1029
|
-
qualify_input(prefix,input,input1);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
function qualify_input(prefix,input,input1) {
|
|
1034
|
-
if (Array.isArray(input)) {
|
|
1035
|
-
let a = flatArray(input);
|
|
1036
|
-
if (a.length > 0) {
|
|
1037
|
-
let t = typeof a[0];
|
|
1038
|
-
for (let i = 1; i<a.length; i++) {
|
|
1039
|
-
if (typeof a[i] != t){
|
|
1040
|
-
throw new Error(`Types are not the same in the key ${prefix}`);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
if (t == "object") {
|
|
1044
|
-
qualify_input_list(prefix,input,input1);
|
|
1045
|
-
} else {
|
|
1046
|
-
input1[prefix] = input;
|
|
1047
|
-
}
|
|
1048
|
-
} else {
|
|
1049
|
-
input1[prefix] = input;
|
|
1050
|
-
}
|
|
1051
|
-
} else if (typeof input == "object") {
|
|
1052
|
-
const keys = Object.keys(input);
|
|
1053
|
-
keys.forEach( (k) => {
|
|
1054
|
-
let new_prefix = prefix == ""? k : prefix + "." + k;
|
|
1055
|
-
qualify_input(new_prefix,input[k],input1);
|
|
1056
|
-
});
|
|
1057
|
-
} else {
|
|
1058
|
-
input1[prefix] = input;
|
|
942
|
+
function publicOutputsLen(layout) {
|
|
943
|
+
if (ciphertextsInPublicSignals(layout)) {
|
|
944
|
+
return layout.outputNoteOffset + layout.nOuts * layout.noteOutputLen + layout.nOuts;
|
|
1059
945
|
}
|
|
946
|
+
return layout.outputNoteOffset + layout.nOuts;
|
|
1060
947
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
948
|
+
function totalPublicSignals(layout) {
|
|
949
|
+
return (publicOutputsLen(layout) +
|
|
950
|
+
layout.publicInputPrefixLen +
|
|
951
|
+
2 * layout.publicNInputs +
|
|
952
|
+
2 * layout.publicNOutputs +
|
|
953
|
+
layout.publicNInputs +
|
|
954
|
+
layout.publicNOutputs);
|
|
955
|
+
}
|
|
956
|
+
function stateRootIndex(layout) {
|
|
957
|
+
return publicOutputsLen(layout);
|
|
958
|
+
}
|
|
959
|
+
function legacyOwnerBoundLayout() {
|
|
960
|
+
return zkLayoutFromShape(2, 2, 1, 1, 4, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2);
|
|
961
|
+
}
|
|
962
|
+
function standardLayout() {
|
|
963
|
+
return zkLayoutFromShape(2, 2, 1, 1, 4, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2, V2_PUBLIC_INPUT_PREFIX_LEN);
|
|
964
|
+
}
|
|
965
|
+
function sixBySixLayout() {
|
|
966
|
+
return zkLayoutFromShape(6, 6, 1, 1, 12, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2, V2_PUBLIC_INPUT_PREFIX_LEN);
|
|
967
|
+
}
|
|
968
|
+
function standardBindingLayout() {
|
|
969
|
+
return zkLayoutFromShapeBinding(2, 2, 1, 1, 4, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2);
|
|
970
|
+
}
|
|
971
|
+
function sixBySixBindingLayout() {
|
|
972
|
+
return zkLayoutFromShapeBinding(6, 6, 1, 1, 12, NOTE_AUDIT_LEN$3, NOTE_OUTPUT_LEN$2);
|
|
973
|
+
}
|
|
974
|
+
function layoutForKnownNonce(nonce) {
|
|
975
|
+
if (nonce === LEGACY_ZK_NONCE) {
|
|
976
|
+
return legacyOwnerBoundLayout();
|
|
1068
977
|
}
|
|
1069
|
-
if (
|
|
1070
|
-
|
|
1071
|
-
while (i>0) {
|
|
1072
|
-
res.unshift(0);
|
|
1073
|
-
i--;
|
|
1074
|
-
}
|
|
978
|
+
if (nonce === COMMITMENT_V2_ZK_NONCE) {
|
|
979
|
+
return standardLayout();
|
|
1075
980
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
function fromArray32(arr) { //returns a BigInt
|
|
1080
|
-
var res = BigInt(0);
|
|
1081
|
-
const radix = BigInt(0x100000000);
|
|
1082
|
-
for (let i = 0; i<arr.length; i++) {
|
|
1083
|
-
res = res*radix + BigInt(arr[i]);
|
|
981
|
+
if (nonce === BINDING_ZK_NONCE) {
|
|
982
|
+
return standardBindingLayout();
|
|
1084
983
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
fillArray(res, a);
|
|
1091
|
-
return res;
|
|
1092
|
-
|
|
1093
|
-
function fillArray(res, a) {
|
|
1094
|
-
if (Array.isArray(a)) {
|
|
1095
|
-
for (let i=0; i<a.length; i++) {
|
|
1096
|
-
fillArray(res, a[i]);
|
|
1097
|
-
}
|
|
1098
|
-
} else {
|
|
1099
|
-
res.push(a);
|
|
1100
|
-
}
|
|
984
|
+
if (nonce === SIX_BY_SIX_ZK_NONCE) {
|
|
985
|
+
return sixBySixLayout();
|
|
986
|
+
}
|
|
987
|
+
if (nonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
|
|
988
|
+
return sixBySixBindingLayout();
|
|
1101
989
|
}
|
|
990
|
+
throw new Error(`unknown zk config nonce ${nonce.toString()}`);
|
|
1102
991
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
992
|
+
function circuitProfileFromName(name) {
|
|
993
|
+
const normalized = name.trim().toLowerCase();
|
|
994
|
+
if (normalized === '6x6' || normalized === 'six' || normalized === '6') {
|
|
995
|
+
return {
|
|
996
|
+
name: '6x6',
|
|
997
|
+
nonce: SIX_BY_SIX_BINDING_ZK_NONCE,
|
|
998
|
+
layout: sixBySixBindingLayout(),
|
|
999
|
+
wasmFileName: 'main_6x6.wasm',
|
|
1000
|
+
zkeyFileName: 'main_6x6_final.zkey',
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
if (normalized === '2x2' || normalized === 'standard' || normalized === '2') {
|
|
1004
|
+
return {
|
|
1005
|
+
name: '2x2',
|
|
1006
|
+
nonce: BINDING_ZK_NONCE,
|
|
1007
|
+
layout: standardBindingLayout(),
|
|
1008
|
+
wasmFileName: 'main.wasm',
|
|
1009
|
+
zkeyFileName: 'main_final.zkey',
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
throw new Error(`unknown circuit profile ${name}`);
|
|
1108
1013
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
for (var i = 0; i < str.length; i++) {
|
|
1114
|
-
hash ^= BigInt(str[i].charCodeAt());
|
|
1115
|
-
hash *= BigInt(0x100000001B3);
|
|
1116
|
-
hash %= uint64_max;
|
|
1014
|
+
function circuitProfileFromNonce(nonce) {
|
|
1015
|
+
if (nonce === SIX_BY_SIX_ZK_NONCE ||
|
|
1016
|
+
nonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
|
|
1017
|
+
return circuitProfileFromName('6x6');
|
|
1117
1018
|
}
|
|
1118
|
-
|
|
1119
|
-
let n = 16 - shash.length;
|
|
1120
|
-
shash = '0'.repeat(n).concat(shash);
|
|
1121
|
-
return shash;
|
|
1019
|
+
return circuitProfileFromName('2x2');
|
|
1122
1020
|
}
|
|
1123
1021
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const dir = nodePath.dirname(fileURLToPath(import.meta.url));
|
|
1135
|
-
const assetsDir = nodePath.resolve(dir, '..', 'assets');
|
|
1136
|
-
wc = { default: witnessCalculatorModule };
|
|
1137
|
-
if (circuitWasm) {
|
|
1138
|
-
wasmBuffer = circuitWasm;
|
|
1139
|
-
}
|
|
1140
|
-
else {
|
|
1141
|
-
const wasmPath = nodePath.resolve(assetsDir, 'main.wasm');
|
|
1142
|
-
if (!fs.existsSync(wasmPath)) {
|
|
1143
|
-
throw new Error(`Circuit WASM not found at ${wasmPath}. Run build.sh first.`);
|
|
1144
|
-
}
|
|
1145
|
-
wasmBuffer = fs.readFileSync(wasmPath);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
if (!circuitWasm) {
|
|
1150
|
-
throw new Error('In browser, you must pass circuitWasm to PrivacyPoolSDK.init(). ' +
|
|
1151
|
-
'Load the circuit WASM file via fetch() and pass the ArrayBuffer.');
|
|
1152
|
-
}
|
|
1153
|
-
wasmBuffer = circuitWasm;
|
|
1154
|
-
wc = { default: witnessCalculatorModule };
|
|
1022
|
+
const ZK_ARTIFACT_GITHUB_REPO = "Polynom-Labs/stellar-privacy-layer-contracts";
|
|
1023
|
+
const ZK_SDK_PACKAGE_VERSION = "0.9.1-rc.0";
|
|
1024
|
+
function defaultZkArtifactBaseUrl(version = ZK_SDK_PACKAGE_VERSION) {
|
|
1025
|
+
const tag = version.startsWith("v") ? version.slice(1) : version;
|
|
1026
|
+
return `https://github.com/${ZK_ARTIFACT_GITHUB_REPO}/releases/download/v${tag}`;
|
|
1027
|
+
}
|
|
1028
|
+
function trimArtifactBaseUrl(value) {
|
|
1029
|
+
let trimmed = value.trim();
|
|
1030
|
+
while (trimmed.endsWith("/")) {
|
|
1031
|
+
trimmed = trimmed.slice(0, -1);
|
|
1155
1032
|
}
|
|
1156
|
-
|
|
1157
|
-
const wtns = await calculator.calculateWTNSBin(input, 0);
|
|
1158
|
-
return wtns;
|
|
1033
|
+
return trimmed;
|
|
1159
1034
|
}
|
|
1160
1035
|
|
|
1161
|
-
// @ts-ignore - snarkjs types
|
|
1162
1036
|
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
|
|
1163
|
-
/**
|
|
1164
|
-
* Normalize BufferSource to Uint8Array. Required for @iden3/binfileutils/fastfile:
|
|
1165
|
-
* they read via data.buffer and data.byteOffset, which ArrayBuffer does not have.
|
|
1166
|
-
*/
|
|
1167
1037
|
function toUint8Array(src) {
|
|
1168
|
-
if (src instanceof Uint8Array)
|
|
1038
|
+
if (src instanceof Uint8Array) {
|
|
1169
1039
|
return src;
|
|
1170
|
-
|
|
1040
|
+
}
|
|
1041
|
+
if (src instanceof ArrayBuffer) {
|
|
1171
1042
|
return new Uint8Array(src);
|
|
1043
|
+
}
|
|
1172
1044
|
return new Uint8Array(src.buffer, src.byteOffset, src.byteLength);
|
|
1173
1045
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1046
|
+
function nodeEnvArtifactBaseUrl() {
|
|
1047
|
+
if (!isNode) {
|
|
1048
|
+
return undefined;
|
|
1049
|
+
}
|
|
1050
|
+
const fromEnv = process.env.ZK_ARTIFACT_BASE_URL?.trim();
|
|
1051
|
+
return fromEnv ? trimArtifactBaseUrl(fromEnv) : undefined;
|
|
1052
|
+
}
|
|
1053
|
+
function resolveZkArtifactBaseUrl(explicit) {
|
|
1054
|
+
const trimmed = explicit?.trim();
|
|
1055
|
+
if (trimmed) {
|
|
1056
|
+
return trimArtifactBaseUrl(trimmed);
|
|
1057
|
+
}
|
|
1058
|
+
return nodeEnvArtifactBaseUrl() ?? defaultZkArtifactBaseUrl();
|
|
1059
|
+
}
|
|
1060
|
+
function isHttpUrl(value) {
|
|
1061
|
+
return /^https?:\/\//i.test(value) || value.startsWith('file:');
|
|
1062
|
+
}
|
|
1063
|
+
async function readNodeAssetFile(filePath) {
|
|
1064
|
+
const fs = await import('fs');
|
|
1065
|
+
if (!fs.existsSync(filePath)) {
|
|
1066
|
+
throw new Error(`Circuit artifact not found at ${filePath}. Set zkArtifactBaseUrl or ZK_ARTIFACT_BASE_URL.`);
|
|
1067
|
+
}
|
|
1068
|
+
return fs.readFileSync(filePath);
|
|
1069
|
+
}
|
|
1070
|
+
async function fetchArtifact(url) {
|
|
1071
|
+
const response = await fetch(url);
|
|
1072
|
+
if (!response.ok) {
|
|
1073
|
+
throw new Error(`Failed to fetch ${url}: ${String(response.status)}`);
|
|
1074
|
+
}
|
|
1075
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
1076
|
+
}
|
|
1077
|
+
function remoteArtifactHref(base, fileName) {
|
|
1078
|
+
const withSlash = `${trimArtifactBaseUrl(base)}/`;
|
|
1079
|
+
if (isHttpUrl(withSlash)) {
|
|
1080
|
+
return new URL(fileName, withSlash).href;
|
|
1178
1081
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1082
|
+
if (typeof window !== 'undefined') {
|
|
1083
|
+
return new URL(fileName, new URL(withSlash, window.location.href)).href;
|
|
1084
|
+
}
|
|
1085
|
+
return `${withSlash}${fileName}`;
|
|
1086
|
+
}
|
|
1087
|
+
async function loadNamedArtifact(base, fileName) {
|
|
1088
|
+
if (isNode && !isHttpUrl(base)) {
|
|
1181
1089
|
const nodePath = await import('path');
|
|
1182
|
-
|
|
1183
|
-
const dir = nodePath.dirname(fileURLToPath(import.meta.url));
|
|
1184
|
-
const zkeyPath = nodePath.resolve(dir, '..', 'assets', 'main_final.zkey');
|
|
1185
|
-
if (!fs.existsSync(zkeyPath)) {
|
|
1186
|
-
throw new Error(`Proving key not found at ${zkeyPath}. Run build.sh first.`);
|
|
1187
|
-
}
|
|
1188
|
-
const zkeyBuffer = fs.readFileSync(zkeyPath);
|
|
1189
|
-
zkeyData = toUint8Array(zkeyBuffer);
|
|
1090
|
+
return readNodeAssetFile(nodePath.resolve(base, fileName));
|
|
1190
1091
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1092
|
+
return fetchArtifact(remoteArtifactHref(base, fileName));
|
|
1093
|
+
}
|
|
1094
|
+
async function loadBundledCircuitArtifacts(circuit, artifactBaseUrl) {
|
|
1095
|
+
const base = resolveZkArtifactBaseUrl(artifactBaseUrl);
|
|
1096
|
+
const names = bundledCircuitFileNames(circuit);
|
|
1097
|
+
const [graph, r1cs, provingKey] = await Promise.all([
|
|
1098
|
+
loadNamedArtifact(base, names.graphFileName),
|
|
1099
|
+
loadNamedArtifact(base, names.r1csFileName),
|
|
1100
|
+
loadNamedArtifact(base, names.provingKeyFileName),
|
|
1101
|
+
]);
|
|
1102
|
+
return { graph, r1cs, provingKey };
|
|
1103
|
+
}
|
|
1104
|
+
async function resolveCircuitArtifacts(config, artifactBaseUrl) {
|
|
1105
|
+
if (isDevZkCircuitConfig(config)) {
|
|
1106
|
+
return {
|
|
1107
|
+
graph: toUint8Array(config.circuitGraph),
|
|
1108
|
+
r1cs: toUint8Array(config.r1cs),
|
|
1109
|
+
provingKey: toUint8Array(config.provingKey),
|
|
1110
|
+
};
|
|
1194
1111
|
}
|
|
1195
|
-
|
|
1196
|
-
const { proof, publicSignals } = await snarkjs.groth16.prove({ type: 'mem', data: zkeyData }, { type: 'mem', data: wtnsData });
|
|
1197
|
-
return { proof, publicSignals };
|
|
1112
|
+
return loadBundledCircuitArtifacts(config.circuit, artifactBaseUrl);
|
|
1198
1113
|
}
|
|
1199
1114
|
|
|
1200
1115
|
var dist = {};
|
|
@@ -1531,7 +1446,7 @@ function bytesToHex$1(bytes) {
|
|
|
1531
1446
|
}
|
|
1532
1447
|
return s;
|
|
1533
1448
|
}
|
|
1534
|
-
function concatBytes(a, b) {
|
|
1449
|
+
function concatBytes$1(a, b) {
|
|
1535
1450
|
const out = new Uint8Array(a.length + b.length);
|
|
1536
1451
|
out.set(a, 0);
|
|
1537
1452
|
out.set(b, a.length);
|
|
@@ -1547,7 +1462,7 @@ function encodeStealthAddress(decoded) {
|
|
|
1547
1462
|
if (xb.length !== yb.length) {
|
|
1548
1463
|
throw new Error('stealth-address: x and y must encode the same number of bytes for a reversible address');
|
|
1549
1464
|
}
|
|
1550
|
-
const payload = concatBytes(xb, yb);
|
|
1465
|
+
const payload = concatBytes$1(xb, yb);
|
|
1551
1466
|
const words = bech32.toWords(payload);
|
|
1552
1467
|
return bech32.encode(STEALTH_ADDRESS_HRP, words, BECH32_STEALTH_LIMIT);
|
|
1553
1468
|
}
|
|
@@ -1572,70 +1487,211 @@ function decodeStealthAddress(address) {
|
|
|
1572
1487
|
|
|
1573
1488
|
/** Default nonce when deriving the wallet sign-in message. */
|
|
1574
1489
|
const DEFAULT_STEALTH_SIGN_NONCE = 'main address';
|
|
1490
|
+
/** Domain tag mixed into the spend-scalar digest (H6). */
|
|
1491
|
+
const SPEND_SCALAR_DOMAIN_TAG = 'privacy-pool-spend-scalar-v1';
|
|
1492
|
+
/** Schema version bound into the spend-scalar message and digest. */
|
|
1493
|
+
const OWNER_BOUND_NOTE_SCHEMA_VERSION = 1;
|
|
1494
|
+
function resolveSpendScalarSchemaVersion(domain) {
|
|
1495
|
+
return domain.schemaVersion ?? OWNER_BOUND_NOTE_SCHEMA_VERSION;
|
|
1496
|
+
}
|
|
1575
1497
|
/**
|
|
1576
|
-
* Plaintext for Stellar wallet message signing (
|
|
1498
|
+
* Plaintext for Stellar wallet message signing (spend-key derivation).
|
|
1577
1499
|
* Sign the exact bytes of this string (UTF-8) with the stellar account key.
|
|
1500
|
+
*
|
|
1501
|
+
* The derived scalar is the owner-bound spend key for notes in this environment.
|
|
1502
|
+
*/
|
|
1503
|
+
function buildStealthAddressSignMessage(address, domain, nonce = DEFAULT_STEALTH_SIGN_NONCE) {
|
|
1504
|
+
const schemaVersion = resolveSpendScalarSchemaVersion(domain);
|
|
1505
|
+
return [
|
|
1506
|
+
'Arcane privacy layer: derive your private-note spend key.',
|
|
1507
|
+
'',
|
|
1508
|
+
`Wallet: ${address}`,
|
|
1509
|
+
`Network: ${domain.networkPassphrase}`,
|
|
1510
|
+
`Pool: ${domain.poolContract}`,
|
|
1511
|
+
`Registry: ${domain.registryContract}`,
|
|
1512
|
+
`Schema: owner-bound-note-v${schemaVersion}`,
|
|
1513
|
+
`Nonce: ${nonce}`,
|
|
1514
|
+
'',
|
|
1515
|
+
'This signature derives the key that authorizes spending your private notes in this environment.',
|
|
1516
|
+
].join('\n');
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
const DOM_PADDING = 1347436868n;
|
|
1520
|
+
const DOM_NULLIFIER = 1346917708n;
|
|
1521
|
+
const DOM_ESCROW = 1346720579n;
|
|
1522
|
+
const DOM_ENC = 1346719299n;
|
|
1523
|
+
const DOM_AUDIT_TAG = 1346458695n;
|
|
1524
|
+
const DOM_OUTPUT_NOTE_TAG = 1347310663n;
|
|
1525
|
+
const BABYJUB_SUBGROUP_ORDER = 2736030358979909402780800718157159386076813972158567259200215660948447373041n;
|
|
1526
|
+
/**
|
|
1527
|
+
* Map any integer into `(0, l)` so it satisfies circom `LessThan(252)` vs
|
|
1528
|
+
* `BABYJUB_SUBGROUP_ORDER` and `BabyPbk`. Values already in range are unchanged.
|
|
1578
1529
|
*/
|
|
1579
|
-
function
|
|
1580
|
-
|
|
1530
|
+
function canonicalBabyJubScalarFromInteger(value) {
|
|
1531
|
+
if (value > 0n && value < BABYJUB_SUBGROUP_ORDER) {
|
|
1532
|
+
return value;
|
|
1533
|
+
}
|
|
1534
|
+
return (value % (BABYJUB_SUBGROUP_ORDER - 1n)) + 1n;
|
|
1581
1535
|
}
|
|
1582
1536
|
|
|
1583
|
-
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
'21605515851820432880964235241069234202284600780825340516808373216881770219365',
|
|
1588
|
-
'18856460861531942120859708048677603751294231190189224157283439874962410808705',
|
|
1589
|
-
];
|
|
1590
|
-
function isActiveWithdraw(slot) {
|
|
1591
|
-
return slot.value !== '0';
|
|
1592
|
-
}
|
|
1593
|
-
function isActiveDeposit(slot) {
|
|
1594
|
-
return slot.value !== '0';
|
|
1595
|
-
}
|
|
1596
|
-
function resolveWithdrawSlot(slot) {
|
|
1597
|
-
if (slot === 'dummy') {
|
|
1598
|
-
return null;
|
|
1537
|
+
function hexToBytes(hex) {
|
|
1538
|
+
const out = new Uint8Array(hex.length / 2);
|
|
1539
|
+
for (let i = 0; i < out.length; i++) {
|
|
1540
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1599
1541
|
}
|
|
1600
|
-
return
|
|
1542
|
+
return out;
|
|
1601
1543
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1544
|
+
/** Base64 → bytes via `atob` (browsers; Node 16+). No Node `Buffer`. */
|
|
1545
|
+
function decodeBase64ToBytes(s) {
|
|
1546
|
+
const t = s.replace(/\s/g, '');
|
|
1547
|
+
if (typeof atob !== 'function') {
|
|
1548
|
+
throw new Error('Base64 decoding requires atob (browser or Node 16+)');
|
|
1549
|
+
}
|
|
1550
|
+
try {
|
|
1551
|
+
const binary = atob(t);
|
|
1552
|
+
const out = new Uint8Array(binary.length);
|
|
1553
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1554
|
+
out[i] = binary.charCodeAt(i);
|
|
1555
|
+
}
|
|
1556
|
+
return out;
|
|
1557
|
+
}
|
|
1558
|
+
catch {
|
|
1559
|
+
throw new Error('Invalid base64 signature');
|
|
1605
1560
|
}
|
|
1606
|
-
return slot;
|
|
1607
1561
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1562
|
+
/**
|
|
1563
|
+
* Ed25519 signature as **128 hex chars** (optional `0x`) or **base64** (typically 88 chars for 64 bytes, whitespace ignored).
|
|
1564
|
+
*/
|
|
1565
|
+
function parseStellarEd25519SignatureRaw(input) {
|
|
1566
|
+
const trimmed = input.trim();
|
|
1567
|
+
const no0x = trimmed.replace(/^0x/i, '');
|
|
1568
|
+
if (/^[0-9a-fA-F]+$/.test(no0x) && no0x.length === 128) {
|
|
1569
|
+
return hexToBytes(no0x.toLowerCase());
|
|
1570
|
+
}
|
|
1571
|
+
const fromB64 = decodeBase64ToBytes(trimmed);
|
|
1572
|
+
if (fromB64.length !== 64) {
|
|
1573
|
+
throw new Error(`Decoded signature must be 64 bytes (Ed25519); got ${fromB64.length} from base64`);
|
|
1574
|
+
}
|
|
1575
|
+
return fromB64;
|
|
1576
|
+
}
|
|
1577
|
+
/** SHA-256 via Web Crypto only (`crypto.subtle`) — works in browsers and Node 19+ (global `crypto`). */
|
|
1578
|
+
async function sha256$1(data) {
|
|
1579
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1580
|
+
if (!subtle) {
|
|
1581
|
+
throw new Error('SHA-256 requires crypto.subtle (HTTPS or localhost in browsers, or Node.js 19+).');
|
|
1582
|
+
}
|
|
1583
|
+
const copy = new Uint8Array(data.length);
|
|
1584
|
+
copy.set(data);
|
|
1585
|
+
return new Uint8Array(await subtle.digest('SHA-256', copy));
|
|
1586
|
+
}
|
|
1587
|
+
function appendU32BE$1(parts, value) {
|
|
1588
|
+
const buf = new Uint8Array(4);
|
|
1589
|
+
new DataView(buf.buffer).setUint32(0, value >>> 0, false);
|
|
1590
|
+
parts.push(buf);
|
|
1591
|
+
}
|
|
1592
|
+
function appendUtf8Prefixed(parts, text) {
|
|
1593
|
+
const bytes = new TextEncoder().encode(text);
|
|
1594
|
+
appendU32BE$1(parts, bytes.length);
|
|
1595
|
+
parts.push(bytes);
|
|
1596
|
+
}
|
|
1597
|
+
function concatBytes(parts) {
|
|
1598
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
1599
|
+
const out = new Uint8Array(total);
|
|
1600
|
+
let offset = 0;
|
|
1601
|
+
for (const part of parts) {
|
|
1602
|
+
out.set(part, offset);
|
|
1603
|
+
offset += part.length;
|
|
1604
|
+
}
|
|
1605
|
+
return out;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Domain-separated spend-scalar digest: SHA-256(
|
|
1609
|
+
* SPEND_SCALAR_DOMAIN_TAG || network || pool || registry || schemaVersion || signature
|
|
1610
|
+
* ).
|
|
1611
|
+
*/
|
|
1612
|
+
async function spendScalarDigestFromStellarSignature(signature, domain) {
|
|
1613
|
+
const raw = parseStellarEd25519SignatureRaw(signature);
|
|
1614
|
+
const parts = [new TextEncoder().encode(SPEND_SCALAR_DOMAIN_TAG)];
|
|
1615
|
+
appendUtf8Prefixed(parts, domain.networkPassphrase);
|
|
1616
|
+
appendUtf8Prefixed(parts, domain.poolContract);
|
|
1617
|
+
appendUtf8Prefixed(parts, domain.registryContract);
|
|
1618
|
+
appendU32BE$1(parts, resolveSpendScalarSchemaVersion(domain));
|
|
1619
|
+
parts.push(raw);
|
|
1620
|
+
return sha256$1(concatBytes(parts));
|
|
1621
|
+
}
|
|
1622
|
+
function digestToCanonicalScalar(digest) {
|
|
1623
|
+
let v = 0n;
|
|
1624
|
+
for (let i = 0; i < digest.length; i++) {
|
|
1625
|
+
v = (v << 8n) + BigInt(digest[i]);
|
|
1626
|
+
}
|
|
1627
|
+
return canonicalBabyJubScalarFromInteger(v);
|
|
1628
|
+
}
|
|
1629
|
+
function canonicalScalarHex(scalar) {
|
|
1630
|
+
return scalar.toString(16).padStart(64, '0');
|
|
1631
|
+
}
|
|
1632
|
+
async function spendScalarHexFromStellarSignature(signature, domain) {
|
|
1633
|
+
const digest = await spendScalarDigestFromStellarSignature(signature, domain);
|
|
1634
|
+
return canonicalScalarHex(digestToCanonicalScalar(digest));
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Stellar Ed25519 signature (hex or base64) → domain-separated SHA-256 → scalar → WASM ECDH.
|
|
1638
|
+
*/
|
|
1639
|
+
async function stealthAddressFromStellarSignature(ecdhFromScalarHex, encodeStealth, signature, domain) {
|
|
1640
|
+
const digest = await spendScalarDigestFromStellarSignature(signature, domain);
|
|
1641
|
+
const decoded = ecdhFromScalarHex(canonicalScalarHex(digestToCanonicalScalar(digest)));
|
|
1642
|
+
return encodeStealth(decoded);
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Domain-separated SHA-256(signature, network, pool, registry, schema) reduced into
|
|
1646
|
+
* `(0, BabyJub subgroup order)` so `privKeyScalar` matches `BabyPbk` and circom `LessThan(l)`.
|
|
1647
|
+
*/
|
|
1648
|
+
async function privKeyScalarDecimalFromStellarSignature(signature, domain) {
|
|
1649
|
+
const digest = await spendScalarDigestFromStellarSignature(signature, domain);
|
|
1650
|
+
return digestToCanonicalScalar(digest).toString(10);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
const TRANSACTION_N_AUDIT_SLOTS = 4;
|
|
1654
|
+
const DEFAULT_APPLICATION_ID = '101';
|
|
1655
|
+
/** BabyJub audit public key (decimal Fr) used in BDD / local demo when env is unset. */
|
|
1656
|
+
const DEMO_AUDIT_PUBLIC_KEY = [
|
|
1657
|
+
'21605515851820432880964235241069234202284600780825340516808373216881770219365',
|
|
1658
|
+
'18856460861531942120859708048677603751294231190189224157283439874962410808705',
|
|
1659
|
+
];
|
|
1660
|
+
function isActiveWithdraw(slot) {
|
|
1661
|
+
return slot.value !== '0';
|
|
1662
|
+
}
|
|
1663
|
+
function isActiveDeposit(slot) {
|
|
1664
|
+
return slot.value !== '0';
|
|
1665
|
+
}
|
|
1666
|
+
function resolveWithdrawSlot(slot) {
|
|
1667
|
+
if (slot === 'dummy') {
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
return slot;
|
|
1671
|
+
}
|
|
1672
|
+
function resolveDepositSlot(slot) {
|
|
1673
|
+
if (slot === 'dummy') {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
return slot;
|
|
1677
|
+
}
|
|
1678
|
+
function buildUniformAuditParams(applicationId = DEFAULT_APPLICATION_ID, auditPublicKey, _nAuditSlots = TRANSACTION_N_AUDIT_SLOTS) {
|
|
1679
|
+
return {
|
|
1680
|
+
applicationId,
|
|
1681
|
+
noteAuditPublicKey: auditPublicKey,
|
|
1682
|
+
auditEphemeralScalar: randomFrDecimal253(),
|
|
1683
|
+
};
|
|
1624
1684
|
}
|
|
1625
1685
|
function resolveSlotApplicationIds(audit, withdrawSlots, depositSlots) {
|
|
1626
|
-
const w0 = resolveWithdrawSlot(withdrawSlots[0]);
|
|
1627
|
-
const w1 = resolveWithdrawSlot(withdrawSlots[1]);
|
|
1628
|
-
const d0 = resolveDepositSlot(depositSlots[0]);
|
|
1629
|
-
const d1 = resolveDepositSlot(depositSlots[1]);
|
|
1630
1686
|
return {
|
|
1631
|
-
inputApplicationIds:
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
outputApplicationIds:
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1687
|
+
inputApplicationIds: withdrawSlots.map((slot) => {
|
|
1688
|
+
const resolved = resolveWithdrawSlot(slot);
|
|
1689
|
+
return resolved && isActiveWithdraw(resolved) ? audit.applicationId : '0';
|
|
1690
|
+
}),
|
|
1691
|
+
outputApplicationIds: depositSlots.map((slot) => {
|
|
1692
|
+
const resolved = resolveDepositSlot(slot);
|
|
1693
|
+
return resolved && isActiveDeposit(resolved) ? audit.applicationId : '0';
|
|
1694
|
+
}),
|
|
1639
1695
|
};
|
|
1640
1696
|
}
|
|
1641
1697
|
function withApplicationIdOnDeposit(deposit, applicationId) {
|
|
@@ -1644,16 +1700,34 @@ function withApplicationIdOnDeposit(deposit, applicationId) {
|
|
|
1644
1700
|
function withApplicationIdOnWithdraw(withdraw, applicationId) {
|
|
1645
1701
|
return { ...withdraw, applicationId };
|
|
1646
1702
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1703
|
+
/**
|
|
1704
|
+
* Prefer an explicit key, then `NOTE_AUDIT_PUBLIC_KEY_X`/`_Y` env (decimal Fr),
|
|
1705
|
+
* then the built-in demo key. Used by CLI/`demo.sh` for per-app audit pubkeys.
|
|
1706
|
+
*/
|
|
1707
|
+
function resolveAuditPublicKeyFromEnv(auditPublicKey) {
|
|
1708
|
+
if (auditPublicKey) {
|
|
1709
|
+
return auditPublicKey;
|
|
1710
|
+
}
|
|
1711
|
+
const x = process.env.NOTE_AUDIT_PUBLIC_KEY_X?.trim();
|
|
1712
|
+
const y = process.env.NOTE_AUDIT_PUBLIC_KEY_Y?.trim();
|
|
1713
|
+
if (x && y) {
|
|
1714
|
+
return [x, y];
|
|
1715
|
+
}
|
|
1716
|
+
return DEMO_AUDIT_PUBLIC_KEY;
|
|
1717
|
+
}
|
|
1718
|
+
function resolveTransactionAuditParams(applicationId, auditPublicKey, nAuditSlots = TRANSACTION_N_AUDIT_SLOTS) {
|
|
1719
|
+
return buildUniformAuditParams(applicationId, resolveAuditPublicKeyFromEnv(auditPublicKey), nAuditSlots);
|
|
1649
1720
|
}
|
|
1650
1721
|
|
|
1651
|
-
/** Matches `Transaction(20, 2, 2, publicNInputs, publicNOutputs, 4,
|
|
1722
|
+
/** Matches `Transaction(20, 2, 2, publicNInputs, publicNOutputs, 4, 12, 6)` in `circuits/main.circom`. */
|
|
1652
1723
|
const TRANSACTION_TREE_DEPTH = 20;
|
|
1653
1724
|
const TRANSACTION_N_INS = 2;
|
|
1654
1725
|
const TRANSACTION_N_OUTS = 2;
|
|
1655
|
-
const NOTE_AUDIT_LEN$
|
|
1726
|
+
const NOTE_AUDIT_LEN$2 = 12;
|
|
1656
1727
|
const NOTE_OUTPUT_LEN$1 = 6;
|
|
1728
|
+
/** Must match `Transaction(..., publicNInputs, publicNOutputs)` and contract `get_public_slot_config`. */
|
|
1729
|
+
const TRANSACTION_PUBLIC_N_INPUTS = 1;
|
|
1730
|
+
const TRANSACTION_PUBLIC_N_OUTPUTS = 1;
|
|
1657
1731
|
/** BN254 scalar field modulus (ark `Fr`, circom signals). */
|
|
1658
1732
|
const BN254_SCALAR_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
1659
1733
|
/**
|
|
@@ -1661,6 +1735,29 @@ const BN254_SCALAR_MOD = 2188824287183927522224640574525727508854836440041603434
|
|
|
1661
1735
|
* (matches `libs/cryptography` `scalar_mul_253`).
|
|
1662
1736
|
*/
|
|
1663
1737
|
const BN254_BABYJUB_SCALAR_MAX_EXCLUSIVE = 1n << 253n;
|
|
1738
|
+
function padPublicLegs(legs, publicNInputs, publicNOutputs) {
|
|
1739
|
+
const padPairs = (values, expected, label) => {
|
|
1740
|
+
if (values.length > expected) {
|
|
1741
|
+
throw new Error(`withdrawal-transaction-input: expected at most ${expected} ${label}`);
|
|
1742
|
+
}
|
|
1743
|
+
return [
|
|
1744
|
+
...values,
|
|
1745
|
+
...Array.from({ length: expected - values.length }, () => ['0', '0']),
|
|
1746
|
+
];
|
|
1747
|
+
};
|
|
1748
|
+
const padAmounts = (values, expected, label) => {
|
|
1749
|
+
if (values.length > expected) {
|
|
1750
|
+
throw new Error(`withdrawal-transaction-input: expected at most ${expected} ${label}`);
|
|
1751
|
+
}
|
|
1752
|
+
return [...values, ...Array.from({ length: expected - values.length }, () => '0')];
|
|
1753
|
+
};
|
|
1754
|
+
return {
|
|
1755
|
+
publicWithdrawnAssets: padPairs(legs.publicWithdrawnAssets, publicNOutputs, 'public withdrawn assets'),
|
|
1756
|
+
publicDepositedAssets: padPairs(legs.publicDepositedAssets, publicNInputs, 'public deposited assets'),
|
|
1757
|
+
publicDeposits: padAmounts(legs.publicDeposits, publicNInputs, 'public deposits'),
|
|
1758
|
+
publicWithdrawals: padAmounts(legs.publicWithdrawals, publicNOutputs, 'public withdrawals'),
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1664
1761
|
function normalizeHex$1(hex) {
|
|
1665
1762
|
const s = hex.trim().replace(/^0x/i, '');
|
|
1666
1763
|
if (!/^[0-9a-fA-F]*$/.test(s)) {
|
|
@@ -1722,6 +1819,12 @@ function randomFrDecimal253() {
|
|
|
1722
1819
|
const hex = generateRandomScalarHex32();
|
|
1723
1820
|
return BigInt(`0x${normalizeHex$1(hex)}`).toString(10);
|
|
1724
1821
|
}
|
|
1822
|
+
/** Spend-scalar hex in `(0, BabyJub subgroup order)` so dummy withdraws satisfy `LessThan(l)`. */
|
|
1823
|
+
function randomCanonicalBabyJubScalarHex() {
|
|
1824
|
+
const hex = generateRandomScalarHex32();
|
|
1825
|
+
const reduced = canonicalBabyJubScalarFromInteger(BigInt(`0x${normalizeHex$1(hex)}`));
|
|
1826
|
+
return reduced.toString(16).padStart(64, '0');
|
|
1827
|
+
}
|
|
1725
1828
|
function zerosTreeSiblings() {
|
|
1726
1829
|
return Array(TRANSACTION_TREE_DEPTH).fill('0');
|
|
1727
1830
|
}
|
|
@@ -1729,14 +1832,20 @@ function dummyWithdraw(wasm) {
|
|
|
1729
1832
|
const nullifier = randomFrDecimal();
|
|
1730
1833
|
const secretHex = generateRandomScalarHex32();
|
|
1731
1834
|
const secret = (BigInt(`0x${normalizeHex$1(secretHex)}`) % BN254_SCALAR_MOD).toString(10);
|
|
1732
|
-
const
|
|
1835
|
+
const scalarHex = randomCanonicalBabyJubScalarHex();
|
|
1836
|
+
const privKeyScalar = scalarHexToFrDecimal(scalarHex);
|
|
1837
|
+
const pt = wasm.ecdhEphemeralPublicKeyFromScalarHex(scalarHex);
|
|
1733
1838
|
return {
|
|
1734
1839
|
value: '0',
|
|
1735
1840
|
nullifier,
|
|
1736
1841
|
secret,
|
|
1737
1842
|
asset: ['0', '0'],
|
|
1738
1843
|
applicationId: '0',
|
|
1739
|
-
|
|
1844
|
+
ownerPub: [coordHexToDecimal(pt.x), coordHexToDecimal(pt.y)],
|
|
1845
|
+
privKeyScalar,
|
|
1846
|
+
paddingRandom: randomFrDecimal(),
|
|
1847
|
+
escrowNonce: '0',
|
|
1848
|
+
recipientStellar: ['0', '0'],
|
|
1740
1849
|
stateSiblings: zerosTreeSiblings(),
|
|
1741
1850
|
stateIndex: '0',
|
|
1742
1851
|
};
|
|
@@ -1753,6 +1862,8 @@ function dummyDeposit(wasm) {
|
|
|
1753
1862
|
asset: ['0', '0'],
|
|
1754
1863
|
applicationId: '0',
|
|
1755
1864
|
recipientPublicKeys: [coordHexToDecimal(pt.x), coordHexToDecimal(pt.y)],
|
|
1865
|
+
escrowNonce: '0',
|
|
1866
|
+
recipientStellar: ['0', '0'],
|
|
1756
1867
|
};
|
|
1757
1868
|
}
|
|
1758
1869
|
function resolveWithdraw(slot, wasm) {
|
|
@@ -1767,37 +1878,63 @@ function resolveDepositsForWitness(depositSlots, wasm) {
|
|
|
1767
1878
|
resolveDeposit(depositSlots[1], wasm),
|
|
1768
1879
|
];
|
|
1769
1880
|
}
|
|
1770
|
-
function
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1881
|
+
function padWithdrawSlots(slots, nIns) {
|
|
1882
|
+
if (slots.length > nIns) {
|
|
1883
|
+
throw new Error(`withdrawal-transaction-input: expected at most ${nIns} withdraw slots`);
|
|
1884
|
+
}
|
|
1885
|
+
return [...slots, ...Array.from({ length: nIns - slots.length }, () => 'dummy')];
|
|
1886
|
+
}
|
|
1887
|
+
function padDepositSlots(slots, nOuts) {
|
|
1888
|
+
if (slots.length > nOuts) {
|
|
1889
|
+
throw new Error(`withdrawal-transaction-input: expected at most ${nOuts} deposit slots`);
|
|
1890
|
+
}
|
|
1891
|
+
return [...slots, ...Array.from({ length: nOuts - slots.length }, () => 'dummy')];
|
|
1892
|
+
}
|
|
1893
|
+
function buildTransactionWitnessInput(publicParams, publicLegs, withdrawSlots, depositSlots, audit, wasm, dims = {}) {
|
|
1894
|
+
const nIns = dims.nIns ?? TRANSACTION_N_INS;
|
|
1895
|
+
const nOuts = dims.nOuts ?? TRANSACTION_N_OUTS;
|
|
1896
|
+
const publicNInputs = dims.publicNInputs ?? TRANSACTION_PUBLIC_N_INPUTS;
|
|
1897
|
+
const publicNOutputs = dims.publicNOutputs ?? TRANSACTION_PUBLIC_N_OUTPUTS;
|
|
1898
|
+
const paddedWithdraws = padWithdrawSlots(withdrawSlots, nIns);
|
|
1899
|
+
const paddedDeposits = padDepositSlots(depositSlots, nOuts);
|
|
1900
|
+
const paddedLegs = padPublicLegs(publicLegs, publicNInputs, publicNOutputs);
|
|
1901
|
+
const withdraws = paddedWithdraws.map((slot) => resolveWithdraw(slot, wasm));
|
|
1902
|
+
const deposits = paddedDeposits.map((slot) => resolveDeposit(slot, wasm));
|
|
1903
|
+
const appIds = resolveSlotApplicationIds(audit, paddedWithdraws, paddedDeposits);
|
|
1776
1904
|
return {
|
|
1777
1905
|
stateRoot: publicParams.stateRoot,
|
|
1778
1906
|
withdrawAddressHi: publicParams.withdrawAddressHi,
|
|
1779
1907
|
withdrawAddressLo: publicParams.withdrawAddressLo,
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1908
|
+
escrowRecipientHi: publicParams.escrowRecipientHi ?? '0',
|
|
1909
|
+
escrowRecipientLo: publicParams.escrowRecipientLo ?? '0',
|
|
1910
|
+
sweepOutputOwnerPubX: publicParams.sweepOutputOwnerPubX ?? '0',
|
|
1911
|
+
sweepOutputOwnerPubY: publicParams.sweepOutputOwnerPubY ?? '0',
|
|
1912
|
+
privKeyScalars: withdraws.map((w) => w.privKeyScalar),
|
|
1913
|
+
ownerPubs: withdraws.map((w) => w.ownerPub),
|
|
1914
|
+
paddingRandoms: withdraws.map((w) => w.paddingRandom),
|
|
1915
|
+
withdrawnValues: withdraws.map((w) => w.value),
|
|
1916
|
+
withdrawnNullifiers: withdraws.map((w) => w.nullifier),
|
|
1917
|
+
withdrawnSecrets: withdraws.map((w) => w.secret),
|
|
1918
|
+
withdrawnAssets: withdraws.map((w) => w.asset),
|
|
1919
|
+
withdrawnEscrowNonces: withdraws.map((w) => w.escrowNonce),
|
|
1920
|
+
inputRecipientStellar: withdraws.map((w) => w.recipientStellar),
|
|
1921
|
+
stateSiblings: withdraws.map((w) => w.stateSiblings),
|
|
1922
|
+
stateIndex: withdraws.map((w) => w.stateIndex),
|
|
1923
|
+
depositedValues: deposits.map((d) => d.value),
|
|
1924
|
+
depositedNullifiers: deposits.map((d) => d.nullifier),
|
|
1925
|
+
depositedAssets: deposits.map((d) => d.asset),
|
|
1926
|
+
depositedEphemeralKeyScalars: deposits.map((d) => d.ephemeralKeyScalar),
|
|
1927
|
+
depositedRecipientPublicKeys: deposits.map((d) => d.recipientPublicKeys),
|
|
1928
|
+
depositedEscrowNonces: deposits.map((d) => d.escrowNonce ?? '0'),
|
|
1929
|
+
outputRecipientStellar: deposits.map((d) => d.recipientStellar ?? ['0', '0']),
|
|
1793
1930
|
inputApplicationIds: appIds.inputApplicationIds,
|
|
1794
1931
|
outputApplicationIds: appIds.outputApplicationIds,
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
publicWithdrawnAssets:
|
|
1798
|
-
publicDepositedAssets:
|
|
1799
|
-
publicDeposits:
|
|
1800
|
-
publicWithdrawals:
|
|
1932
|
+
auditEphemeralScalar: audit.auditEphemeralScalar,
|
|
1933
|
+
noteAuditPublicKey: audit.noteAuditPublicKey,
|
|
1934
|
+
publicWithdrawnAssets: paddedLegs.publicWithdrawnAssets,
|
|
1935
|
+
publicDepositedAssets: paddedLegs.publicDepositedAssets,
|
|
1936
|
+
publicDeposits: paddedLegs.publicDeposits,
|
|
1937
|
+
publicWithdrawals: paddedLegs.publicWithdrawals,
|
|
1801
1938
|
};
|
|
1802
1939
|
}
|
|
1803
1940
|
/** `stpl1…` stealth address → `[x, y]` as decimal field strings for `depositedRecipientPublicKeys`. */
|
|
@@ -1805,135 +1942,574 @@ function recipientPublicKeysDecimalFromStealthAddress(stealthAddress) {
|
|
|
1805
1942
|
const { x, y } = decodeStealthAddress(stealthAddress);
|
|
1806
1943
|
return [coordHexToDecimal(x), coordHexToDecimal(y)];
|
|
1807
1944
|
}
|
|
1808
|
-
/** First withdraw leg: Merkle witness +
|
|
1809
|
-
function withdrawObjectFromMerkleWitness(witness,
|
|
1945
|
+
/** First withdraw leg: Merkle witness + owner public key (hex) and matching scalar. */
|
|
1946
|
+
function withdrawObjectFromMerkleWitness(witness, ownerPubHex, applicationId, privKeyScalar) {
|
|
1810
1947
|
return {
|
|
1811
1948
|
value: witness.value,
|
|
1812
1949
|
nullifier: witness.nullifier,
|
|
1813
1950
|
secret: witness.secret,
|
|
1814
1951
|
asset: witness.withdrawnAsset,
|
|
1815
1952
|
applicationId,
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1953
|
+
ownerPub: [coordHexToDecimal(ownerPubHex.x), coordHexToDecimal(ownerPubHex.y)],
|
|
1954
|
+
privKeyScalar,
|
|
1955
|
+
paddingRandom: randomFrDecimal(),
|
|
1956
|
+
escrowNonce: '0',
|
|
1957
|
+
recipientStellar: ['0', '0'],
|
|
1820
1958
|
stateSiblings: witness.stateSiblings,
|
|
1821
1959
|
stateIndex: witness.stateIndex,
|
|
1822
1960
|
};
|
|
1823
1961
|
}
|
|
1824
1962
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1963
|
+
const FR_SIZE$1 = 32;
|
|
1964
|
+
const CIPHERTEXT_BLOB_HEADER_BYTES = 4;
|
|
1965
|
+
const FR_MODULUS$1 = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
1966
|
+
function moduleFr$1(value) {
|
|
1967
|
+
const reduced = value % FR_MODULUS$1;
|
|
1968
|
+
return reduced >= 0n ? reduced : reduced + FR_MODULUS$1;
|
|
1969
|
+
}
|
|
1970
|
+
function fieldToBytes(value) {
|
|
1971
|
+
let remaining = moduleFr$1(value);
|
|
1972
|
+
const buf = Buffer.alloc(FR_SIZE$1);
|
|
1973
|
+
for (let index = FR_SIZE$1 - 1; index >= 0; index -= 1) {
|
|
1974
|
+
buf[index] = Number(remaining & 0xffn);
|
|
1975
|
+
remaining >>= 8n;
|
|
1829
1976
|
}
|
|
1830
|
-
return
|
|
1977
|
+
return buf;
|
|
1831
1978
|
}
|
|
1832
|
-
function
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1979
|
+
function decimalOrHexToBigint(value) {
|
|
1980
|
+
const trimmed = value.trim();
|
|
1981
|
+
if (/^0x/i.test(trimmed) || /^[0-9a-f]{64}$/i.test(trimmed)) {
|
|
1982
|
+
return BigInt(`0x${trimmed.replace(/^0x/i, '')}`);
|
|
1836
1983
|
}
|
|
1837
|
-
return
|
|
1984
|
+
return BigInt(trimmed);
|
|
1838
1985
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1986
|
+
function encodeCiphertextBlob(fields) {
|
|
1987
|
+
const prefix = Buffer.alloc(CIPHERTEXT_BLOB_HEADER_BYTES);
|
|
1988
|
+
prefix.writeUInt32BE(fields.length);
|
|
1989
|
+
return Buffer.concat([
|
|
1990
|
+
prefix,
|
|
1991
|
+
...fields.map((field) => fieldToBytes(decimalOrHexToBigint(field))),
|
|
1992
|
+
]);
|
|
1993
|
+
}
|
|
1994
|
+
function decodeCiphertextBlob(blob) {
|
|
1995
|
+
if (blob.length < CIPHERTEXT_BLOB_HEADER_BYTES) {
|
|
1996
|
+
throw new Error('ciphertext_bytes too short');
|
|
1844
1997
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
out[i] = binary.charCodeAt(i);
|
|
1850
|
-
}
|
|
1851
|
-
return out;
|
|
1998
|
+
const count = blob.readUInt32BE(0);
|
|
1999
|
+
const need = CIPHERTEXT_BLOB_HEADER_BYTES + count * FR_SIZE$1;
|
|
2000
|
+
if (blob.length < need) {
|
|
2001
|
+
throw new Error('ciphertext_bytes truncated');
|
|
1852
2002
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
2003
|
+
const fields = [];
|
|
2004
|
+
let offset = CIPHERTEXT_BLOB_HEADER_BYTES;
|
|
2005
|
+
for (let index = 0; index < count; index += 1) {
|
|
2006
|
+
fields.push(blob.subarray(offset, offset + FR_SIZE$1).toString('hex'));
|
|
2007
|
+
offset += FR_SIZE$1;
|
|
1855
2008
|
}
|
|
2009
|
+
return fields;
|
|
1856
2010
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const no0x = trimmed.replace(/^0x/i, '');
|
|
1863
|
-
if (/^[0-9a-fA-F]+$/.test(no0x) && no0x.length === 128) {
|
|
1864
|
-
return hexToBytes(no0x.toLowerCase());
|
|
2011
|
+
function encodeTransactionCiphertextBlob(parameters) {
|
|
2012
|
+
const expected = ciphertextFieldCount(parameters.layout);
|
|
2013
|
+
const fields = [];
|
|
2014
|
+
for (const slot of parameters.auditCiphertexts) {
|
|
2015
|
+
fields.push(...slot);
|
|
1865
2016
|
}
|
|
1866
|
-
const
|
|
1867
|
-
|
|
1868
|
-
throw new Error(`Decoded signature must be 64 bytes (Ed25519); got ${fromB64.length} from base64`);
|
|
2017
|
+
for (const note of parameters.outputNoteCiphertexts) {
|
|
2018
|
+
fields.push(...note);
|
|
1869
2019
|
}
|
|
1870
|
-
|
|
2020
|
+
if (fields.length !== expected) {
|
|
2021
|
+
throw new Error(`ciphertext blob expected ${String(expected)} fields, got ${String(fields.length)}`);
|
|
2022
|
+
}
|
|
2023
|
+
return encodeCiphertextBlob(fields);
|
|
1871
2024
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
const
|
|
1875
|
-
if (
|
|
1876
|
-
throw new Error(
|
|
2025
|
+
function splitCiphertextBlob(parameters) {
|
|
2026
|
+
const fields = decodeCiphertextBlob(parameters.blob);
|
|
2027
|
+
const expected = ciphertextFieldCount(parameters.layout);
|
|
2028
|
+
if (fields.length !== expected) {
|
|
2029
|
+
throw new Error(`ciphertext blob expected ${String(expected)} fields, got ${String(fields.length)}`);
|
|
1877
2030
|
}
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
2031
|
+
const { nAuditSlots, noteAuditLen, nOuts, noteOutputLen } = parameters.layout;
|
|
2032
|
+
const auditCiphertexts = [];
|
|
2033
|
+
let offset = 0;
|
|
2034
|
+
for (let slot = 0; slot < nAuditSlots; slot += 1) {
|
|
2035
|
+
auditCiphertexts.push(fields.slice(offset, offset + noteAuditLen));
|
|
2036
|
+
offset += noteAuditLen;
|
|
2037
|
+
}
|
|
2038
|
+
const outputNoteCiphertexts = [];
|
|
2039
|
+
for (let note = 0; note < nOuts; note += 1) {
|
|
2040
|
+
outputNoteCiphertexts.push(fields.slice(offset, offset + noteOutputLen));
|
|
2041
|
+
offset += noteOutputLen;
|
|
2042
|
+
}
|
|
2043
|
+
return { auditCiphertexts, outputNoteCiphertexts };
|
|
1881
2044
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2045
|
+
|
|
2046
|
+
const NOTE_OUTPUT_LEN = 6;
|
|
2047
|
+
const NOTE_AUDIT_LEN$1 = 12;
|
|
2048
|
+
const STREAM_DOMAIN = 1n;
|
|
2049
|
+
const FR_SIZE = 32;
|
|
2050
|
+
const FR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
2051
|
+
const ONE = 1n;
|
|
2052
|
+
const ZERO = 0n;
|
|
2053
|
+
const BABYJUB_SCALAR_BITS = 253n;
|
|
2054
|
+
const SCALAR_253_MASK = (ONE << BABYJUB_SCALAR_BITS) - ONE;
|
|
2055
|
+
let primitivesPromise;
|
|
2056
|
+
function primitives() {
|
|
2057
|
+
primitivesPromise ?? (primitivesPromise = Promise.all([
|
|
2058
|
+
buildBabyjub(),
|
|
2059
|
+
buildMimc7(),
|
|
2060
|
+
buildPoseidon(),
|
|
2061
|
+
]).then(([babyJub, mimc7, poseidon]) => ({ babyJub, mimc7, poseidon })));
|
|
2062
|
+
return primitivesPromise;
|
|
1891
2063
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
2064
|
+
function normalizeHex(value) {
|
|
2065
|
+
return value.trim().replace(/^0x/i, '').padStart(FR_SIZE * 2, '0');
|
|
2066
|
+
}
|
|
2067
|
+
function bigintFromHex(value) {
|
|
2068
|
+
return BigInt(`0x${normalizeHex(value)}`);
|
|
2069
|
+
}
|
|
2070
|
+
function bigintFromDecimal(value) {
|
|
2071
|
+
const parsed = BigInt(value);
|
|
2072
|
+
return parsed % FR_MODULUS;
|
|
2073
|
+
}
|
|
2074
|
+
function bigintToFrHex(value) {
|
|
2075
|
+
let v = moduleFr(value);
|
|
2076
|
+
const buf = Buffer.alloc(FR_SIZE);
|
|
2077
|
+
for (let index = FR_SIZE - 1; index >= 0; index -= 1) {
|
|
2078
|
+
buf[index] = Number(v & 0xffn);
|
|
2079
|
+
v >>= 8n;
|
|
1902
2080
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
2081
|
+
return buf.toString('hex');
|
|
2082
|
+
}
|
|
2083
|
+
function frDecimalToHex(value) {
|
|
2084
|
+
return bigintToFrHex(bigintFromDecimal(value));
|
|
2085
|
+
}
|
|
2086
|
+
function frDecimalFromBigint(value) {
|
|
2087
|
+
const reduced = value >= ZERO ? value % FR_MODULUS : (value % FR_MODULUS) + FR_MODULUS;
|
|
2088
|
+
return reduced.toString(10);
|
|
2089
|
+
}
|
|
2090
|
+
function fieldBigint(field, value) {
|
|
2091
|
+
return BigInt(field.toString(value));
|
|
2092
|
+
}
|
|
2093
|
+
function moduleFr(value) {
|
|
2094
|
+
const reduced = value % FR_MODULUS;
|
|
2095
|
+
return reduced >= ZERO ? reduced : reduced + FR_MODULUS;
|
|
2096
|
+
}
|
|
2097
|
+
function mimcHash(parameters) {
|
|
2098
|
+
return fieldBigint(parameters.mimc7.F, parameters.mimc7.hash(parameters.mimc7.F.e(parameters.message), parameters.mimc7.F.e(parameters.key)));
|
|
2099
|
+
}
|
|
2100
|
+
function poseidonHash(poseidon, inputs) {
|
|
2101
|
+
return fieldBigint(poseidon.F, poseidon(inputs.map((input) => poseidon.F.e(input))));
|
|
2102
|
+
}
|
|
2103
|
+
function deriveEncCipherKey(poseidon, sharedX) {
|
|
2104
|
+
return poseidonHash(poseidon, [DOM_ENC, sharedX]);
|
|
2105
|
+
}
|
|
2106
|
+
function sharedKeyFromDepositor(parameters) {
|
|
2107
|
+
const scalarHex = frDecimalToHex(parameters.ephemeralKeyScalarDecimal);
|
|
2108
|
+
const shared = parameters.ecdhShared(scalarHex, frDecimalToHex(parameters.recipientPublicKey[0]), frDecimalToHex(parameters.recipientPublicKey[1]));
|
|
2109
|
+
return [bigintFromHex(shared.x), bigintFromHex(shared.y)];
|
|
2110
|
+
}
|
|
2111
|
+
function sharedKeyFromRecipient(parameters) {
|
|
2112
|
+
const { babyJub, recipientScalarHex, ephemeralPublicKey } = parameters;
|
|
2113
|
+
const point = [
|
|
2114
|
+
babyJub.F.e(bigintFromHex(ephemeralPublicKey[0])),
|
|
2115
|
+
babyJub.F.e(bigintFromHex(ephemeralPublicKey[1])),
|
|
2116
|
+
];
|
|
2117
|
+
if (!babyJub.inCurve(point)) {
|
|
2118
|
+
throw new Error('Created ephemeral public key is not on BabyJubJub curve');
|
|
2119
|
+
}
|
|
2120
|
+
const product = babyJub.mulPointEscalar(point, bigintFromHex(recipientScalarHex) & SCALAR_253_MASK);
|
|
2121
|
+
return [
|
|
2122
|
+
fieldBigint(babyJub.F, product[0]),
|
|
2123
|
+
fieldBigint(babyJub.F, product[1]),
|
|
2124
|
+
];
|
|
2125
|
+
}
|
|
2126
|
+
function poseidonMacTag(parameters) {
|
|
2127
|
+
return poseidonHash(parameters.poseidon, [
|
|
2128
|
+
parameters.domain,
|
|
2129
|
+
parameters.key,
|
|
2130
|
+
parameters.nonce,
|
|
2131
|
+
...parameters.message,
|
|
2132
|
+
]);
|
|
2133
|
+
}
|
|
2134
|
+
function outputNoteTag(parameters) {
|
|
2135
|
+
return poseidonMacTag({
|
|
2136
|
+
poseidon: parameters.poseidon,
|
|
2137
|
+
domain: DOM_OUTPUT_NOTE_TAG,
|
|
2138
|
+
key: parameters.key,
|
|
2139
|
+
nonce: parameters.nonce,
|
|
2140
|
+
message: parameters.plaintext,
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
function encryptFields(parameters) {
|
|
2144
|
+
return parameters.plaintext.map((field, index) => {
|
|
2145
|
+
const seed = poseidonHash(parameters.poseidon, [
|
|
2146
|
+
parameters.nonce,
|
|
2147
|
+
STREAM_DOMAIN,
|
|
2148
|
+
BigInt(index),
|
|
2149
|
+
]);
|
|
2150
|
+
const stream = mimcHash({
|
|
2151
|
+
mimc7: parameters.mimc7,
|
|
2152
|
+
message: seed,
|
|
2153
|
+
key: parameters.key,
|
|
2154
|
+
});
|
|
2155
|
+
return moduleFr(field + stream);
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
function decryptFields(parameters) {
|
|
2159
|
+
return parameters.ciphertext.map((field, index) => {
|
|
2160
|
+
const seed = poseidonHash(parameters.poseidon, [
|
|
2161
|
+
parameters.nonce,
|
|
2162
|
+
STREAM_DOMAIN,
|
|
2163
|
+
BigInt(index),
|
|
2164
|
+
]);
|
|
2165
|
+
const stream = mimcHash({
|
|
2166
|
+
mimc7: parameters.mimc7,
|
|
2167
|
+
message: seed,
|
|
2168
|
+
key: parameters.key,
|
|
2169
|
+
});
|
|
2170
|
+
return moduleFr(bigintFromHex(field) - stream);
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
function plaintextBigints(plaintext) {
|
|
2174
|
+
return [
|
|
2175
|
+
bigintFromDecimal(plaintext.value),
|
|
2176
|
+
bigintFromDecimal(plaintext.assetHi),
|
|
2177
|
+
bigintFromDecimal(plaintext.assetLo),
|
|
2178
|
+
bigintFromDecimal(plaintext.nullifier),
|
|
2179
|
+
bigintFromDecimal(plaintext.secret),
|
|
2180
|
+
bigintFromDecimal(plaintext.applicationId),
|
|
2181
|
+
];
|
|
2182
|
+
}
|
|
2183
|
+
function plaintextFromBigints(fields) {
|
|
2184
|
+
return {
|
|
2185
|
+
value: frDecimalFromBigint(fields[0] ?? ZERO),
|
|
2186
|
+
assetHi: frDecimalFromBigint(fields[1] ?? ZERO),
|
|
2187
|
+
assetLo: frDecimalFromBigint(fields[2] ?? ZERO),
|
|
2188
|
+
nullifier: frDecimalFromBigint(fields[3] ?? ZERO),
|
|
2189
|
+
secret: frDecimalFromBigint(fields[4] ?? ZERO),
|
|
2190
|
+
applicationId: frDecimalFromBigint(fields[5] ?? ZERO),
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
async function secretFromDepositEphemeralScalarDecimal(ephemeralKeyScalarDecimal) {
|
|
2194
|
+
const { poseidon } = await primitives();
|
|
2195
|
+
const secret = poseidonHash(poseidon, [bigintFromDecimal(ephemeralKeyScalarDecimal)]);
|
|
2196
|
+
return frDecimalFromBigint(secret);
|
|
2197
|
+
}
|
|
2198
|
+
async function encryptOutputNoteForDeposit(parameters) {
|
|
2199
|
+
const { babyJub, mimc7, poseidon } = await primitives();
|
|
2200
|
+
const secret = await secretFromDepositEphemeralScalarDecimal(parameters.deposit.ephemeralKeyScalar);
|
|
2201
|
+
const plaintext = {
|
|
2202
|
+
value: parameters.deposit.value,
|
|
2203
|
+
assetHi: parameters.deposit.asset[0],
|
|
2204
|
+
assetLo: parameters.deposit.asset[1],
|
|
2205
|
+
nullifier: parameters.deposit.nullifier,
|
|
2206
|
+
secret,
|
|
2207
|
+
applicationId: parameters.deposit.applicationId,
|
|
2208
|
+
};
|
|
2209
|
+
const [sharedX, sharedY] = sharedKeyFromDepositor({
|
|
2210
|
+
babyJub,
|
|
2211
|
+
ecdhShared: parameters.ecdhShared,
|
|
2212
|
+
ephemeralKeyScalarDecimal: parameters.deposit.ephemeralKeyScalar,
|
|
2213
|
+
recipientPublicKey: parameters.deposit.recipientPublicKeys,
|
|
2214
|
+
});
|
|
2215
|
+
const key = deriveEncCipherKey(poseidon, sharedX);
|
|
2216
|
+
const nonce = sharedY;
|
|
2217
|
+
const message = plaintextBigints(plaintext);
|
|
2218
|
+
const ciphertext = encryptFields({ mimc7, poseidon, key, nonce, plaintext: message });
|
|
2219
|
+
const tag = outputNoteTag({ poseidon, key, nonce, plaintext: message });
|
|
2220
|
+
return {
|
|
2221
|
+
ciphertext: ciphertext.map((field) => frDecimalFromBigint(field)),
|
|
2222
|
+
tag: frDecimalFromBigint(tag),
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
function ownerPubFromRecipientScalar(babyJub, recipientScalarHex) {
|
|
2226
|
+
const product = babyJub.mulPointEscalar(babyJub.Base8, bigintFromHex(recipientScalarHex) & SCALAR_253_MASK);
|
|
2227
|
+
return [
|
|
2228
|
+
fieldBigint(babyJub.F, product[0]),
|
|
2229
|
+
fieldBigint(babyJub.F, product[1]),
|
|
2230
|
+
];
|
|
2231
|
+
}
|
|
2232
|
+
function commitmentFromPlaintextAndOwnerPub(parameters) {
|
|
2233
|
+
const commitment = poseidonHash(parameters.poseidon, [
|
|
2234
|
+
bigintFromDecimal(parameters.plaintext.value),
|
|
2235
|
+
bigintFromDecimal(parameters.plaintext.secret),
|
|
2236
|
+
bigintFromDecimal(parameters.plaintext.nullifier),
|
|
2237
|
+
parameters.ownerPub[0],
|
|
2238
|
+
parameters.ownerPub[1],
|
|
2239
|
+
parameters.ownerMode,
|
|
2240
|
+
bigintFromDecimal(parameters.plaintext.assetHi),
|
|
2241
|
+
bigintFromDecimal(parameters.plaintext.assetLo),
|
|
2242
|
+
bigintFromDecimal(parameters.plaintext.applicationId),
|
|
2243
|
+
]);
|
|
2244
|
+
return normalizeHex(commitment.toString(16));
|
|
2245
|
+
}
|
|
2246
|
+
async function decryptOutputNoteEvent(input) {
|
|
2247
|
+
const { babyJub, mimc7, poseidon } = await primitives();
|
|
2248
|
+
const [sharedX, sharedY] = sharedKeyFromRecipient({
|
|
2249
|
+
babyJub,
|
|
2250
|
+
recipientScalarHex: input.recipientScalarHex,
|
|
2251
|
+
ephemeralPublicKey: input.createdEphemeralKey,
|
|
2252
|
+
});
|
|
2253
|
+
const key = deriveEncCipherKey(poseidon, sharedX);
|
|
2254
|
+
const nonce = sharedY;
|
|
2255
|
+
const decrypted = decryptFields({
|
|
2256
|
+
mimc7,
|
|
2257
|
+
poseidon,
|
|
2258
|
+
key,
|
|
2259
|
+
nonce,
|
|
2260
|
+
ciphertext: input.ciphertext,
|
|
2261
|
+
});
|
|
2262
|
+
const expectedTag = outputNoteTag({
|
|
2263
|
+
poseidon,
|
|
2264
|
+
key,
|
|
2265
|
+
nonce,
|
|
2266
|
+
plaintext: decrypted,
|
|
2267
|
+
});
|
|
2268
|
+
if (expectedTag !== bigintFromHex(input.tag)) {
|
|
2269
|
+
throw new Error('Output note tag verification failed');
|
|
2270
|
+
}
|
|
2271
|
+
const plaintext = plaintextFromBigints(decrypted);
|
|
2272
|
+
const commitmentHex = commitmentFromPlaintextAndOwnerPub({
|
|
2273
|
+
poseidon,
|
|
2274
|
+
plaintext,
|
|
2275
|
+
ownerPub: ownerPubFromRecipientScalar(babyJub, input.recipientScalarHex),
|
|
2276
|
+
ownerMode: input.ownerMode ?? 0n,
|
|
2277
|
+
});
|
|
2278
|
+
const commitmentMatches = normalizeHex(input.commitmentHashHex) === commitmentHex;
|
|
2279
|
+
if (!commitmentMatches) {
|
|
2280
|
+
throw new Error('Output note commitment checksum mismatch');
|
|
2281
|
+
}
|
|
2282
|
+
return {
|
|
2283
|
+
...plaintext,
|
|
2284
|
+
commitmentHex,
|
|
2285
|
+
commitmentMatches,
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
function ownerModeFromEscrowNonce(escrowNonce) {
|
|
2289
|
+
return bigintFromDecimal(escrowNonce) === ZERO ? ZERO : ONE;
|
|
2290
|
+
}
|
|
2291
|
+
function emptyFlagFromValue(value) {
|
|
2292
|
+
return bigintFromDecimal(value) === ZERO ? ONE : ZERO;
|
|
2293
|
+
}
|
|
2294
|
+
function precommitmentFromOpening(parameters) {
|
|
2295
|
+
const ownerKeyHash = poseidonHash(parameters.poseidon, [
|
|
2296
|
+
parameters.ownerPub[0],
|
|
2297
|
+
parameters.ownerPub[1],
|
|
2298
|
+
]);
|
|
2299
|
+
return poseidonHash(parameters.poseidon, [
|
|
2300
|
+
parameters.secret,
|
|
2301
|
+
parameters.ownerMode,
|
|
2302
|
+
ownerKeyHash,
|
|
2303
|
+
]);
|
|
2304
|
+
}
|
|
2305
|
+
async function encryptNoteAuditSlot(parameters) {
|
|
2306
|
+
if (parameters.plaintext.length !== NOTE_AUDIT_LEN$1) {
|
|
2307
|
+
throw new Error(`encryptNoteAuditSlot: expected ${String(NOTE_AUDIT_LEN$1)} limbs`);
|
|
2308
|
+
}
|
|
2309
|
+
const { babyJub, mimc7, poseidon } = await primitives();
|
|
2310
|
+
const [sharedX, sharedY] = sharedKeyFromDepositor({
|
|
2311
|
+
babyJub,
|
|
2312
|
+
ecdhShared: parameters.ecdhShared,
|
|
2313
|
+
ephemeralKeyScalarDecimal: parameters.auditEphemeralScalar,
|
|
2314
|
+
recipientPublicKey: parameters.auditPublicKey,
|
|
2315
|
+
});
|
|
2316
|
+
const key = deriveEncCipherKey(poseidon, sharedX);
|
|
2317
|
+
const nonce = sharedY;
|
|
2318
|
+
const message = parameters.plaintext.map((field) => bigintFromDecimal(field));
|
|
2319
|
+
const ciphertext = encryptFields({ mimc7, poseidon, key, nonce, plaintext: message });
|
|
2320
|
+
const tag = poseidonMacTag({
|
|
2321
|
+
poseidon,
|
|
2322
|
+
domain: DOM_AUDIT_TAG,
|
|
2323
|
+
key,
|
|
2324
|
+
nonce,
|
|
2325
|
+
message,
|
|
2326
|
+
});
|
|
2327
|
+
return {
|
|
2328
|
+
ciphertext: ciphertext.map((field) => frDecimalFromBigint(field)),
|
|
2329
|
+
tag: frDecimalFromBigint(tag),
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
function auditPlaintextForWithdraw(parameters) {
|
|
2333
|
+
const value = parameters.witness.withdrawnValues[parameters.index] ?? '0';
|
|
2334
|
+
const ownerPub = parameters.witness.ownerPubs[parameters.index] ?? ['0', '0'];
|
|
2335
|
+
const escrowNonce = parameters.witness.withdrawnEscrowNonces[parameters.index] ?? '0';
|
|
2336
|
+
const secret = bigintFromDecimal(parameters.witness.withdrawnSecrets[parameters.index] ?? '0');
|
|
2337
|
+
const ownerMode = ownerModeFromEscrowNonce(escrowNonce);
|
|
2338
|
+
const precommitment = precommitmentFromOpening({
|
|
2339
|
+
poseidon: parameters.poseidon,
|
|
2340
|
+
secret,
|
|
2341
|
+
ownerMode,
|
|
2342
|
+
ownerPub: [bigintFromDecimal(ownerPub[0]), bigintFromDecimal(ownerPub[1])],
|
|
2343
|
+
});
|
|
2344
|
+
const asset = parameters.witness.withdrawnAssets[parameters.index] ?? ['0', '0'];
|
|
2345
|
+
const stellar = parameters.witness.inputRecipientStellar[parameters.index] ?? ['0', '0'];
|
|
2346
|
+
return [
|
|
2347
|
+
frDecimalFromBigint(emptyFlagFromValue(value)),
|
|
2348
|
+
value,
|
|
2349
|
+
asset[0],
|
|
2350
|
+
asset[1],
|
|
2351
|
+
frDecimalFromBigint(precommitment),
|
|
2352
|
+
parameters.witness.withdrawnNullifiers[parameters.index] ?? '0',
|
|
2353
|
+
ownerPub[0],
|
|
2354
|
+
ownerPub[1],
|
|
2355
|
+
parameters.witness.inputApplicationIds[parameters.index] ?? '0',
|
|
2356
|
+
stellar[0],
|
|
2357
|
+
stellar[1],
|
|
2358
|
+
escrowNonce,
|
|
2359
|
+
];
|
|
2360
|
+
}
|
|
2361
|
+
async function auditPlaintextForDeposit(parameters) {
|
|
2362
|
+
const value = parameters.witness.depositedValues[parameters.index] ?? '0';
|
|
2363
|
+
const ownerPub = parameters.witness.depositedRecipientPublicKeys[parameters.index] ?? ['0', '0'];
|
|
2364
|
+
const escrowNonce = parameters.witness.depositedEscrowNonces[parameters.index] ?? '0';
|
|
2365
|
+
const secret = poseidonHash(parameters.poseidon, [
|
|
2366
|
+
bigintFromDecimal(parameters.witness.depositedEphemeralKeyScalars[parameters.index] ?? '0'),
|
|
2367
|
+
]);
|
|
2368
|
+
const ownerMode = ownerModeFromEscrowNonce(escrowNonce);
|
|
2369
|
+
const precommitment = precommitmentFromOpening({
|
|
2370
|
+
poseidon: parameters.poseidon,
|
|
2371
|
+
secret,
|
|
2372
|
+
ownerMode,
|
|
2373
|
+
ownerPub: [bigintFromDecimal(ownerPub[0]), bigintFromDecimal(ownerPub[1])],
|
|
2374
|
+
});
|
|
2375
|
+
const asset = parameters.witness.depositedAssets[parameters.index] ?? ['0', '0'];
|
|
2376
|
+
const stellar = parameters.witness.outputRecipientStellar[parameters.index] ?? ['0', '0'];
|
|
2377
|
+
return [
|
|
2378
|
+
frDecimalFromBigint(emptyFlagFromValue(value)),
|
|
2379
|
+
value,
|
|
2380
|
+
asset[0],
|
|
2381
|
+
asset[1],
|
|
2382
|
+
frDecimalFromBigint(precommitment),
|
|
2383
|
+
parameters.witness.depositedNullifiers[parameters.index] ?? '0',
|
|
2384
|
+
ownerPub[0],
|
|
2385
|
+
ownerPub[1],
|
|
2386
|
+
parameters.witness.outputApplicationIds[parameters.index] ?? '0',
|
|
2387
|
+
stellar[0],
|
|
2388
|
+
stellar[1],
|
|
2389
|
+
escrowNonce,
|
|
2390
|
+
];
|
|
2391
|
+
}
|
|
2392
|
+
async function encryptTransactionCiphertextBlob(parameters) {
|
|
2393
|
+
const { poseidon } = await primitives();
|
|
2394
|
+
const nIns = parameters.witness.withdrawnValues.length;
|
|
2395
|
+
const nOuts = parameters.witness.depositedValues.length;
|
|
2396
|
+
const nAuditSlots = parameters.layout.nAuditSlots;
|
|
2397
|
+
const auditCiphertexts = [];
|
|
2398
|
+
for (let slot = 0; slot < nAuditSlots; slot += 1) {
|
|
2399
|
+
const plaintext = slot < nIns
|
|
2400
|
+
? auditPlaintextForWithdraw({
|
|
2401
|
+
poseidon,
|
|
2402
|
+
witness: parameters.witness,
|
|
2403
|
+
index: slot,
|
|
2404
|
+
})
|
|
2405
|
+
: await auditPlaintextForDeposit({
|
|
2406
|
+
poseidon,
|
|
2407
|
+
witness: parameters.witness,
|
|
2408
|
+
index: slot - nIns,
|
|
2409
|
+
});
|
|
2410
|
+
const encrypted = await encryptNoteAuditSlot({
|
|
2411
|
+
auditPublicKey: parameters.witness.noteAuditPublicKey,
|
|
2412
|
+
auditEphemeralScalar: parameters.witness.auditEphemeralScalar,
|
|
2413
|
+
plaintext,
|
|
2414
|
+
ecdhShared: parameters.ecdhShared,
|
|
2415
|
+
});
|
|
2416
|
+
auditCiphertexts.push(encrypted.ciphertext);
|
|
2417
|
+
}
|
|
2418
|
+
const outputNoteCiphertexts = [];
|
|
2419
|
+
for (let index = 0; index < nOuts; index += 1) {
|
|
2420
|
+
const deposit = {
|
|
2421
|
+
value: parameters.witness.depositedValues[index] ?? '0',
|
|
2422
|
+
nullifier: parameters.witness.depositedNullifiers[index] ?? '0',
|
|
2423
|
+
ephemeralKeyScalar: parameters.witness.depositedEphemeralKeyScalars[index] ?? '0',
|
|
2424
|
+
asset: parameters.witness.depositedAssets[index] ?? ['0', '0'],
|
|
2425
|
+
applicationId: parameters.witness.outputApplicationIds[index] ?? '0',
|
|
2426
|
+
recipientPublicKeys: parameters.witness.depositedRecipientPublicKeys[index] ?? ['0', '0'],
|
|
2427
|
+
};
|
|
2428
|
+
const encrypted = await encryptOutputNoteForDeposit({
|
|
2429
|
+
deposit,
|
|
2430
|
+
ecdhShared: parameters.ecdhShared,
|
|
2431
|
+
});
|
|
2432
|
+
outputNoteCiphertexts.push([...encrypted.ciphertext]);
|
|
2433
|
+
}
|
|
2434
|
+
const blob = encodeTransactionCiphertextBlob({
|
|
2435
|
+
layout: parameters.layout,
|
|
2436
|
+
auditCiphertexts,
|
|
2437
|
+
outputNoteCiphertexts,
|
|
2438
|
+
});
|
|
2439
|
+
return {
|
|
2440
|
+
ciphertextHex: blob.toString('hex'),
|
|
2441
|
+
auditCiphertexts,
|
|
2442
|
+
outputNoteCiphertexts,
|
|
2443
|
+
};
|
|
1905
2444
|
}
|
|
1906
2445
|
|
|
1907
|
-
/** Stroops amount as
|
|
1908
|
-
function
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2446
|
+
/** Stroops amount as a decimal field string. Reject JS `number` so 18-decimal values cannot silently narrow. */
|
|
2447
|
+
function coinValueDecimal(amount) {
|
|
2448
|
+
if (typeof amount === "number") {
|
|
2449
|
+
throw new TypeError("coin value must be bigint or decimal string, not number");
|
|
2450
|
+
}
|
|
2451
|
+
if (typeof amount === "bigint") {
|
|
2452
|
+
if (amount < 0n) {
|
|
2453
|
+
throw new RangeError("amount must be a non-negative integer");
|
|
2454
|
+
}
|
|
2455
|
+
return amount.toString(10);
|
|
2456
|
+
}
|
|
2457
|
+
if (typeof amount !== "string") {
|
|
2458
|
+
throw new TypeError("coin value must be bigint or decimal string, not number");
|
|
2459
|
+
}
|
|
2460
|
+
const trimmed = amount.trim();
|
|
2461
|
+
if (!/^[0-9]+$/.test(trimmed)) {
|
|
2462
|
+
throw new TypeError("coin value decimal string must be a non-negative integer");
|
|
1912
2463
|
}
|
|
1913
|
-
return
|
|
2464
|
+
return trimmed;
|
|
2465
|
+
}
|
|
2466
|
+
function generatedCoinFromWasm(value) {
|
|
2467
|
+
const raw = value;
|
|
2468
|
+
const precommitementHex = raw.precommitementHex ?? raw.precommitement_hex;
|
|
2469
|
+
if (!precommitementHex) {
|
|
2470
|
+
throw new Error("generated coin missing precommitement hex");
|
|
2471
|
+
}
|
|
2472
|
+
return {
|
|
2473
|
+
coin: raw.coin,
|
|
2474
|
+
commitment_hex: raw.commitment_hex,
|
|
2475
|
+
precommitement_hex: precommitementHex,
|
|
2476
|
+
precommitementHex,
|
|
2477
|
+
};
|
|
1914
2478
|
}
|
|
1915
2479
|
class PrivacyPoolSDK {
|
|
1916
|
-
constructor(wasm, options) {
|
|
2480
|
+
constructor(wasm, options, resolved) {
|
|
1917
2481
|
this.wasm = wasm;
|
|
1918
2482
|
this.options = options;
|
|
2483
|
+
this.zkConfigNonce = resolved.nonce;
|
|
2484
|
+
this.circuitConfig = resolved.config;
|
|
2485
|
+
this.layout = layoutFromCircuitConfig(resolved.config);
|
|
2486
|
+
}
|
|
2487
|
+
getZkConfigNonce() {
|
|
2488
|
+
return this.zkConfigNonce;
|
|
2489
|
+
}
|
|
2490
|
+
getLayout() {
|
|
2491
|
+
return this.layout;
|
|
1919
2492
|
}
|
|
1920
2493
|
/**
|
|
1921
2494
|
* Initialize the SDK by loading WASM modules.
|
|
1922
2495
|
*
|
|
1923
|
-
* In Node.js,
|
|
1924
|
-
*
|
|
2496
|
+
* In Node.js, proving artifacts load from `zkArtifactBaseUrl` or
|
|
2497
|
+
* `ZK_ARTIFACT_BASE_URL`, defaulting to this package's GitHub release.
|
|
2498
|
+
* In browser, pass pre-loaded WASM via options:
|
|
1925
2499
|
* ```ts
|
|
1926
2500
|
* const sdk = await PrivacyPoolSDK.init({
|
|
1927
2501
|
* wasmBinary: await fetch('/pkg/client_sdk_wasm_bg.wasm').then(r => r.arrayBuffer()),
|
|
1928
|
-
*
|
|
1929
|
-
* zkey: await fetch('/assets/main_final.zkey').then(r => r.arrayBuffer()),
|
|
2502
|
+
* zkArtifactBaseUrl: 'https://github.com/Polynom-Labs/stellar-privacy-layer-contracts/releases/download/v0.8.0',
|
|
1930
2503
|
* });
|
|
1931
2504
|
* ```
|
|
1932
2505
|
*/
|
|
1933
2506
|
static async init(options) {
|
|
1934
2507
|
const opts = options ?? {};
|
|
1935
2508
|
const wasm = await loadWasm(opts.wasmBinary);
|
|
1936
|
-
|
|
2509
|
+
const resolved = resolveZkCircuitConfig(opts.zkCircuits, opts.zkConfigNonce);
|
|
2510
|
+
const sdk = new PrivacyPoolSDK(wasm, opts, resolved);
|
|
2511
|
+
sdk.circuitArtifacts = await resolveCircuitArtifacts(resolved.config, opts.zkArtifactBaseUrl);
|
|
2512
|
+
return sdk;
|
|
1937
2513
|
}
|
|
1938
2514
|
/**
|
|
1939
2515
|
* Uniform 32-byte scalar as lowercase hex (Web Crypto). Same in browser and Node 19+.
|
|
@@ -1942,39 +2518,40 @@ class PrivacyPoolSDK {
|
|
|
1942
2518
|
return generateRandomScalarHex32();
|
|
1943
2519
|
}
|
|
1944
2520
|
/**
|
|
1945
|
-
* Text to sign with a Stellar wallet for
|
|
2521
|
+
* Text to sign with a Stellar wallet for spend-key derivation (UTF-8). No WASM required.
|
|
2522
|
+
* The message binds network, pool, registry, and schema version (H6).
|
|
1946
2523
|
*/
|
|
1947
|
-
static buildStealthAddressSignMessage(address, nonce = DEFAULT_STEALTH_SIGN_NONCE) {
|
|
1948
|
-
return buildStealthAddressSignMessage(address, nonce);
|
|
2524
|
+
static buildStealthAddressSignMessage(address, domain, nonce = DEFAULT_STEALTH_SIGN_NONCE) {
|
|
2525
|
+
return buildStealthAddressSignMessage(address, domain, nonce);
|
|
1949
2526
|
}
|
|
1950
2527
|
/**
|
|
1951
|
-
* Generate a new coin with random nullifier, secret, and random
|
|
1952
|
-
* @param amount
|
|
2528
|
+
* Generate a new coin with random nullifier, secret, and random owner-pub field elements (dev / self-contained tests).
|
|
2529
|
+
* @param amount Decimal field element as `bigint` or decimal `string` (never JS `number`).
|
|
1953
2530
|
* @param assetHiDecimal / assetLoDecimal Decimal Fr strings for Stellar asset contract id (two limbs).
|
|
1954
2531
|
*/
|
|
1955
|
-
generateCoin(amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal =
|
|
1956
|
-
return this.wasm.generateCoin(
|
|
2532
|
+
generateCoin(amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
|
|
2533
|
+
return generatedCoinFromWasm(this.wasm.generateCoin(coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
|
|
1957
2534
|
}
|
|
1958
2535
|
/**
|
|
1959
|
-
* Generate a coin with the same commitment shape as on-chain deposit: pass
|
|
1960
|
-
* @param amount
|
|
2536
|
+
* Generate a coin with the same commitment shape as on-chain deposit: pass owner public key (hex x, y).
|
|
2537
|
+
* @param amount Decimal field element (`bigint` | `string`).
|
|
1961
2538
|
*/
|
|
1962
|
-
|
|
1963
|
-
return this.wasm.
|
|
2539
|
+
generateCoinWithOwnerPub(ownerPub, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
|
|
2540
|
+
return generatedCoinFromWasm(this.wasm.generateCoinWithOwnerPubHex(ownerPub.x, ownerPub.y, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
|
|
1964
2541
|
}
|
|
1965
2542
|
/**
|
|
1966
2543
|
* Coin for a depositor `ephemeralKeyScalar` (32-byte hex): `coin.secret = Poseidon255(1)(scalar)` as in `deposit.circom`.
|
|
1967
|
-
* @param amount
|
|
2544
|
+
* @param amount Decimal field element (`bigint` | `string`).
|
|
1968
2545
|
*/
|
|
1969
|
-
generateCoinFromDepositEphemeralScalarHex(scalarHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal =
|
|
1970
|
-
return this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex,
|
|
2546
|
+
generateCoinFromDepositEphemeralScalarHex(scalarHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
|
|
2547
|
+
return generatedCoinFromWasm(this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
|
|
1971
2548
|
}
|
|
1972
2549
|
/**
|
|
1973
|
-
* Aligned deposit coin: `secret = Poseidon₁(scalar)` and
|
|
1974
|
-
* @param amount
|
|
2550
|
+
* Aligned deposit coin: `secret = Poseidon₁(scalar)` and owner pub from recipient hex coords.
|
|
2551
|
+
* @param amount Decimal field element (`bigint` | `string`).
|
|
1975
2552
|
*/
|
|
1976
|
-
|
|
1977
|
-
return this.wasm.
|
|
2553
|
+
generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
|
|
2554
|
+
return generatedCoinFromWasm(this.wasm.generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
|
|
1978
2555
|
}
|
|
1979
2556
|
/**
|
|
1980
2557
|
* Merkle root, path, and coin fields for the first withdraw leg (Rust LeanIMT + Poseidon).
|
|
@@ -1993,20 +2570,18 @@ class PrivacyPoolSDK {
|
|
|
1993
2570
|
*/
|
|
1994
2571
|
async proveWithdrawal(coin, state, params) {
|
|
1995
2572
|
const witness = this.buildWithdrawMerkleWitness(coin, state);
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
y: params.ephemeralYHex,
|
|
1999
|
-
}, params.applicationId ?? coin.application_id ?? '0');
|
|
2573
|
+
const ownerPubHex = this.ecdhEphemeralPublicKeyFromScalarHex(BigInt(params.privKeyScalar).toString(16).padStart(64, "0"));
|
|
2574
|
+
const w0 = withdrawObjectFromMerkleWitness(witness, ownerPubHex, params.applicationId ?? coin.application_id ?? "0", params.privKeyScalar);
|
|
2000
2575
|
const fullV = BigInt(coin.value);
|
|
2001
2576
|
let publicWithdrawals;
|
|
2002
2577
|
let deposits;
|
|
2003
2578
|
if (params.publicWithdrawStroops !== undefined) {
|
|
2004
2579
|
const W = params.publicWithdrawStroops;
|
|
2005
2580
|
if (params.changeRecipientStealthAddress === undefined) {
|
|
2006
|
-
throw new Error(
|
|
2581
|
+
throw new Error("proveWithdrawal: changeRecipientStealthAddress is required when publicWithdrawStroops is set");
|
|
2007
2582
|
}
|
|
2008
2583
|
if (W <= 0n || W >= fullV) {
|
|
2009
|
-
throw new Error(
|
|
2584
|
+
throw new Error("proveWithdrawal: publicWithdrawStroops must be strictly between 0 and coin.value");
|
|
2010
2585
|
}
|
|
2011
2586
|
const change = fullV - W;
|
|
2012
2587
|
publicWithdrawals = [W.toString()];
|
|
@@ -2015,29 +2590,29 @@ class PrivacyPoolSDK {
|
|
|
2015
2590
|
nullifier: randomFrDecimal(),
|
|
2016
2591
|
ephemeralKeyScalar: randomFrDecimal253(),
|
|
2017
2592
|
asset: [coin.asset_hi, coin.asset_lo],
|
|
2018
|
-
applicationId: params.applicationId ??
|
|
2593
|
+
applicationId: params.applicationId ?? "0",
|
|
2019
2594
|
recipientPublicKeys: recipientPublicKeysDecimalFromStealthAddress(params.changeRecipientStealthAddress),
|
|
2020
2595
|
};
|
|
2021
|
-
deposits = [changeDeposit
|
|
2596
|
+
deposits = [changeDeposit];
|
|
2022
2597
|
}
|
|
2023
2598
|
else {
|
|
2024
2599
|
publicWithdrawals = [coin.value];
|
|
2025
|
-
deposits = [
|
|
2600
|
+
deposits = [];
|
|
2026
2601
|
}
|
|
2027
2602
|
// On-chain `transact` sends `publicWithdrawals` / `publicWithdrawnAssets` to the withdraw account.
|
|
2028
2603
|
const publicLegs = {
|
|
2029
2604
|
publicWithdrawnAssets: [[coin.asset_hi, coin.asset_lo]],
|
|
2030
|
-
publicDepositedAssets: [[
|
|
2031
|
-
publicDeposits: [
|
|
2605
|
+
publicDepositedAssets: [["0", "0"]],
|
|
2606
|
+
publicDeposits: ["0"],
|
|
2032
2607
|
publicWithdrawals,
|
|
2033
2608
|
};
|
|
2034
|
-
return this.
|
|
2609
|
+
return this.proveTransactionSlots({
|
|
2035
2610
|
stateRoot: witness.stateRoot,
|
|
2036
2611
|
withdrawAddressHi: params.withdrawAddressHi,
|
|
2037
2612
|
withdrawAddressLo: params.withdrawAddressLo,
|
|
2038
2613
|
privKeyScalar: params.privKeyScalar,
|
|
2039
|
-
}, publicLegs, [w0
|
|
2040
|
-
|
|
2614
|
+
}, publicLegs, [w0], deposits, params.audit ??
|
|
2615
|
+
resolveTransactionAuditParams(params.applicationId ?? coin.application_id ?? DEFAULT_APPLICATION_ID, DEMO_AUDIT_PUBLIC_KEY, this.layout.nAuditSlots));
|
|
2041
2616
|
}
|
|
2042
2617
|
/**
|
|
2043
2618
|
* Convert a snarkjs proof JSON to hex bytes for Soroban.
|
|
@@ -2062,39 +2637,50 @@ class PrivacyPoolSDK {
|
|
|
2062
2637
|
return this.wasm.ecdhSharedKey(scalarHex, recipientPubXHex, recipientPubYHex);
|
|
2063
2638
|
}
|
|
2064
2639
|
/**
|
|
2065
|
-
* `Transaction(20,
|
|
2066
|
-
*
|
|
2067
|
-
* @param publicParams Public inputs: `stateRoot`, `withdrawAddressHi` / `withdrawAddressLo`, `privKeyScalar` (decimal field strings).
|
|
2068
|
-
* @param publicLegs Public token legs (`publicWithdrawnAssets`, `publicDepositedAssets`, `publicDeposits`, `publicWithdrawals`).
|
|
2069
|
-
* @param withdraws Exactly two withdraw slots (`WithdrawObject` or `"dummy"`).
|
|
2070
|
-
* @param deposits Exactly two deposit slots (`DepositObject` or `"dummy"`).
|
|
2640
|
+
* `Transaction(20,nIns,nOuts)` proof from high-level legs: maps to witness input (incl. `"dummy"` ECDH via WASM), then Groth16 → Soroban hex.
|
|
2641
|
+
* Slots are padded to the selected ZK config layout (bundled 2×2 or 6×6).
|
|
2071
2642
|
*/
|
|
2072
2643
|
async proveTransaction(publicParams, publicLegs, withdraws, deposits, audit) {
|
|
2644
|
+
return this.proveTransactionSlots(publicParams, publicLegs, withdraws, deposits, audit);
|
|
2645
|
+
}
|
|
2646
|
+
async proveTransactionSlots(publicParams, publicLegs, withdraws, deposits, audit) {
|
|
2073
2647
|
const wasmEcdh = {
|
|
2074
2648
|
ecdhEphemeralPublicKeyFromScalarHex: (h) => this.wasm.ecdhEphemeralPublicKeyFromScalarHex(h),
|
|
2075
2649
|
};
|
|
2076
|
-
const
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2650
|
+
const { nIns, nOuts, publicNInputs, publicNOutputs } = this.layout;
|
|
2651
|
+
const witnessInput = buildTransactionWitnessInput(publicParams, publicLegs, withdraws, deposits, audit, wasmEcdh, { nIns, nOuts, publicNInputs, publicNOutputs });
|
|
2652
|
+
const artifacts = this.circuitArtifacts;
|
|
2653
|
+
if (!artifacts) {
|
|
2654
|
+
throw new Error("PrivacyPoolSDK circuit artifacts are not loaded");
|
|
2655
|
+
}
|
|
2656
|
+
const proved = this.wasm.proveGroth16(artifacts.graph, artifacts.provingKey, artifacts.r1cs, JSON.stringify(witnessInput));
|
|
2657
|
+
const encrypted = await encryptTransactionCiphertextBlob({
|
|
2658
|
+
witness: witnessInput,
|
|
2659
|
+
layout: this.layout,
|
|
2660
|
+
ecdhShared: (priv, pubX, pubY) => this.wasm.ecdhSharedKey(priv, pubX, pubY),
|
|
2661
|
+
});
|
|
2079
2662
|
return {
|
|
2080
|
-
proof_hex:
|
|
2081
|
-
public_hex:
|
|
2663
|
+
proof_hex: proved.proof_hex,
|
|
2664
|
+
public_hex: proved.public_hex,
|
|
2665
|
+
ciphertext_hex: encrypted.ciphertextHex,
|
|
2666
|
+
output_note_ephemeral_scalars: witnessInput.depositedEphemeralKeyScalars,
|
|
2082
2667
|
};
|
|
2083
2668
|
}
|
|
2084
2669
|
/**
|
|
2085
|
-
* Calculate nullifier hash: Poseidon(nullifier)
|
|
2670
|
+
* Calculate owner-bound nullifier hash: Poseidon(DOM_NULLIFIER, nullifier, privKeyScalar)
|
|
2086
2671
|
* @param nullifier Nullifier decimal string from coin data
|
|
2672
|
+
* @param privKeyScalar Spend scalar decimal string
|
|
2087
2673
|
* @returns Hex string (0x...) of the hash bytes
|
|
2088
2674
|
*/
|
|
2089
|
-
calculateNullifierHash(nullifier) {
|
|
2090
|
-
return this.wasm.calculateNullifierHash(nullifier);
|
|
2675
|
+
calculateNullifierHash(nullifier, privKeyScalar) {
|
|
2676
|
+
return this.wasm.calculateNullifierHash(nullifier, privKeyScalar);
|
|
2091
2677
|
}
|
|
2092
2678
|
/**
|
|
2093
2679
|
* Ed25519 signature from signing {@link buildStealthAddressSignMessage}: **128 hex chars** (optional `0x`)
|
|
2094
|
-
* or **base64** (64 raw bytes after decode).
|
|
2680
|
+
* or **base64** (64 raw bytes after decode). Domain-separated SHA-256 → scalar → ECDH → `stpl1` Bech32.
|
|
2095
2681
|
*/
|
|
2096
|
-
async generateStealthAddressFromStellarSignature(signature) {
|
|
2097
|
-
return stealthAddressFromStellarSignature((h) => this.wasm.ecdhEphemeralPublicKeyFromScalarHex(h), encodeStealthAddress, signature);
|
|
2682
|
+
async generateStealthAddressFromStellarSignature(signature, domain) {
|
|
2683
|
+
return stealthAddressFromStellarSignature((h) => this.wasm.ecdhEphemeralPublicKeyFromScalarHex(h), encodeStealthAddress, signature, domain);
|
|
2098
2684
|
}
|
|
2099
2685
|
encodeDecodedEphemeralKey(decoded) {
|
|
2100
2686
|
return encodeDecodedEphemeralKey(decoded);
|
|
@@ -2116,11 +2702,30 @@ class PrivacyPoolSDK {
|
|
|
2116
2702
|
}
|
|
2117
2703
|
}
|
|
2118
2704
|
|
|
2705
|
+
/** @deprecated Witness generation is in the main SDK WASM (`proveGroth16`). */
|
|
2706
|
+
async function generateWitness(_input, _circuitWasm, _wasmFileName) {
|
|
2707
|
+
throw new Error('generateWitness is removed. Call PrivacyPoolSDK.proveTransaction (graph witness + Groth16 in sdk.wasm).');
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2119
2710
|
/** Pool Merkle tree depth (matches `coin::TREE_DEPTH` / `Transaction` circuit). */
|
|
2120
2711
|
const POOL_MERKLE_TREE_DEPTH = 20;
|
|
2121
2712
|
/** Default coin value in stroops (1 XLM); matches Rust `coin::COIN_VALUE`. */
|
|
2122
2713
|
const COIN_VALUE_STROOPS = 1000000000;
|
|
2123
2714
|
|
|
2715
|
+
// Split a JS number into u32 halves without a BigInt allocation. Exact only for integers
|
|
2716
|
+
// `0 <= n < 2**53`; callers use it on byte / bit counters, which JS length math caps far below
|
|
2717
|
+
// that (an ArrayBuffer cannot exceed 2**53 - 1 bytes).
|
|
2718
|
+
const fromNumH = (n) => (n / 2 ** 32) | 0;
|
|
2719
|
+
const fromNumL = (n) => n >>> 0;
|
|
2720
|
+
// Drop-in replacement for `view.setBigUint64(byteOffset, BigInt(n), isLE)` without the per-call
|
|
2721
|
+
// BigInt allocation. Same `n < 2**53` precondition as `fromNumH`/`fromNumL`.
|
|
2722
|
+
function setU64FromNum(view, byteOffset, n, isLE) {
|
|
2723
|
+
const h = fromNumH(n);
|
|
2724
|
+
const l = fromNumL(n);
|
|
2725
|
+
view.setUint32(byteOffset, isLE ? l : h, isLE);
|
|
2726
|
+
view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2124
2729
|
/**
|
|
2125
2730
|
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
|
|
2126
2731
|
* @param a - value to test
|
|
@@ -2142,6 +2747,9 @@ function isBytes(a) {
|
|
|
2142
2747
|
'BYTES_PER_ELEMENT' in a &&
|
|
2143
2748
|
a.BYTES_PER_ELEMENT === 1));
|
|
2144
2749
|
}
|
|
2750
|
+
// Shared error-message prefix builder. Only called on throw paths, so assert
|
|
2751
|
+
// success paths never pay for the string concatenation.
|
|
2752
|
+
const atitle = (title) => (title ? `"${title}" ` : '');
|
|
2145
2753
|
/**
|
|
2146
2754
|
* Asserts something is Uint8Array.
|
|
2147
2755
|
* @param value - value to validate
|
|
@@ -2157,20 +2765,32 @@ function isBytes(a) {
|
|
|
2157
2765
|
* ```
|
|
2158
2766
|
*/
|
|
2159
2767
|
function abytes(value, length, title = '') {
|
|
2768
|
+
// Success path first: this runs at the start of every update() / digestInto(), and the
|
|
2769
|
+
// common `abytes(data)` form must not pay for length handling it does not use.
|
|
2770
|
+
if (isBytes(value) && (length === undefined))
|
|
2771
|
+
return value;
|
|
2160
2772
|
const bytes = isBytes(value);
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
throw new
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2773
|
+
const ofLen = '';
|
|
2774
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
2775
|
+
const message = atitle(title) + 'expected Uint8Array' + ofLen + ', got ' + got;
|
|
2776
|
+
if (!bytes)
|
|
2777
|
+
throw new TypeError(message);
|
|
2778
|
+
throw new RangeError(message);
|
|
2779
|
+
}
|
|
2780
|
+
const aobject = (value, label) => {
|
|
2781
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
2782
|
+
throw new TypeError((label === 'object' ? '' : `"${label}" `) + 'expected object, got type=' + typeof value);
|
|
2783
|
+
};
|
|
2784
|
+
const aopts = (value, label) => {
|
|
2785
|
+
aobject(value, label);
|
|
2786
|
+
const proto = Object.getPrototypeOf(value);
|
|
2787
|
+
if (proto !== Object.prototype && proto !== null)
|
|
2788
|
+
throw new TypeError(`"${label}" expected plain object`);
|
|
2789
|
+
// Object.assign() treats an own "__proto__" source key as a write to the target's legacy
|
|
2790
|
+
// prototype setter. Reject it before merging so inherited option values cannot be injected.
|
|
2791
|
+
if (Object.hasOwn(value, '__proto__'))
|
|
2792
|
+
throw new TypeError(`"${label}.__proto__" is not allowed`);
|
|
2793
|
+
};
|
|
2174
2794
|
/**
|
|
2175
2795
|
* Asserts a hash instance has not been destroyed or finished.
|
|
2176
2796
|
* @param instance - hash instance to validate
|
|
@@ -2186,10 +2806,12 @@ function abytes(value, length, title = '') {
|
|
|
2186
2806
|
* ```
|
|
2187
2807
|
*/
|
|
2188
2808
|
function aexists(instance, checkFinished = true) {
|
|
2809
|
+
// Runs on every update()/digestInto(); the flags are library-owned booleans, so only their
|
|
2810
|
+
// truthiness is checked - re-validating their type per call was pure hot-path overhead.
|
|
2189
2811
|
if (instance.destroyed)
|
|
2190
|
-
throw new Error('
|
|
2812
|
+
throw new Error('hash was destroyed');
|
|
2191
2813
|
if (checkFinished && instance.finished)
|
|
2192
|
-
throw new Error('
|
|
2814
|
+
throw new Error('digest() was already called');
|
|
2193
2815
|
}
|
|
2194
2816
|
/**
|
|
2195
2817
|
* Asserts output is a sufficiently-sized byte array.
|
|
@@ -2208,10 +2830,12 @@ function aexists(instance, checkFinished = true) {
|
|
|
2208
2830
|
* ```
|
|
2209
2831
|
*/
|
|
2210
2832
|
function aoutput(out, instance) {
|
|
2211
|
-
abytes(out, undefined, '
|
|
2833
|
+
abytes(out, undefined, 'output');
|
|
2834
|
+
// `outputLen` is a library-owned readonly number; the negated comparison keeps failing fast
|
|
2835
|
+
// when it is missing/NaN (comparisons with undefined/NaN are false) without an anumber() call.
|
|
2212
2836
|
const min = instance.outputLen;
|
|
2213
|
-
if (out.length
|
|
2214
|
-
throw new RangeError('"
|
|
2837
|
+
if (!(out.length >= min)) {
|
|
2838
|
+
throw new RangeError('"output" expected length >= ' + min);
|
|
2215
2839
|
}
|
|
2216
2840
|
}
|
|
2217
2841
|
/**
|
|
@@ -2286,6 +2910,28 @@ function bytesToHex(bytes) {
|
|
|
2286
2910
|
}
|
|
2287
2911
|
return hex;
|
|
2288
2912
|
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Merges default options and passed options.
|
|
2915
|
+
* @param defaults - base option object
|
|
2916
|
+
* @param opts - user overrides
|
|
2917
|
+
* @param title - label included in thrown override errors
|
|
2918
|
+
* @returns Fresh merged option object with a null prototype.
|
|
2919
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
2920
|
+
* @example
|
|
2921
|
+
* Merge user overrides onto default options.
|
|
2922
|
+
* ```ts
|
|
2923
|
+
* checkOpts({ dkLen: 32 }, { asyncTick: 10 });
|
|
2924
|
+
* ```
|
|
2925
|
+
*/
|
|
2926
|
+
function checkOpts(defaults, opts, title = 'opts') {
|
|
2927
|
+
aopts(defaults, 'defaults');
|
|
2928
|
+
if (opts !== undefined)
|
|
2929
|
+
aopts(opts, title);
|
|
2930
|
+
// Callers read optional fields directly, so omitted values must not fall through to ambient
|
|
2931
|
+
// Object.prototype pollution (for example a forged `dkLen` changing SHAKE's default output).
|
|
2932
|
+
const merged = Object.assign(Object.create(null), defaults, opts);
|
|
2933
|
+
return merged;
|
|
2934
|
+
}
|
|
2289
2935
|
/**
|
|
2290
2936
|
* Creates a callable hash function from a stateful class constructor.
|
|
2291
2937
|
* @param hashCons - hash constructor or factory
|
|
@@ -2294,6 +2940,7 @@ function bytesToHex(bytes) {
|
|
|
2294
2940
|
* Wrapper construction eagerly calls `hashCons(undefined)` once to read
|
|
2295
2941
|
* `outputLen` / `blockLen`, so constructor side effects happen at module
|
|
2296
2942
|
* init time.
|
|
2943
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
2297
2944
|
* @example
|
|
2298
2945
|
* Wrap a stateful hash constructor into a callable helper.
|
|
2299
2946
|
* ```ts
|
|
@@ -2304,6 +2951,9 @@ function bytesToHex(bytes) {
|
|
|
2304
2951
|
* ```
|
|
2305
2952
|
*/
|
|
2306
2953
|
function createHasher(hashCons, info = {}) {
|
|
2954
|
+
if (typeof hashCons !== 'function')
|
|
2955
|
+
throw new TypeError('"hashCons" expected function, got type=' + typeof hashCons);
|
|
2956
|
+
info = checkOpts({}, info, 'info');
|
|
2307
2957
|
const hashC = (msg, opts) => hashCons(opts)
|
|
2308
2958
|
.update(msg)
|
|
2309
2959
|
.digest();
|
|
@@ -2415,6 +3065,7 @@ class HashMD {
|
|
|
2415
3065
|
abytes(data);
|
|
2416
3066
|
const { view, buffer, blockLen } = this;
|
|
2417
3067
|
const len = data.length;
|
|
3068
|
+
let processed = false;
|
|
2418
3069
|
for (let pos = 0; pos < len;) {
|
|
2419
3070
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
2420
3071
|
// Fast path only when there is no buffered partial block: `take === blockLen` implies
|
|
@@ -2423,18 +3074,25 @@ class HashMD {
|
|
|
2423
3074
|
const dataView = createView(data);
|
|
2424
3075
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
2425
3076
|
this.process(dataView, pos);
|
|
3077
|
+
processed = true;
|
|
2426
3078
|
continue;
|
|
2427
3079
|
}
|
|
2428
|
-
|
|
3080
|
+
// When the whole input is buffered in one go (common for short messages), passing `data`
|
|
3081
|
+
// directly avoids allocating a subarray view.
|
|
3082
|
+
buffer.set(pos === 0 && take === len ? data : data.subarray(pos, pos + take), this.pos);
|
|
2429
3083
|
this.pos += take;
|
|
2430
3084
|
pos += take;
|
|
2431
3085
|
if (this.pos === blockLen) {
|
|
2432
3086
|
this.process(view, 0);
|
|
2433
3087
|
this.pos = 0;
|
|
3088
|
+
processed = true;
|
|
2434
3089
|
}
|
|
2435
3090
|
}
|
|
2436
3091
|
this.length += data.length;
|
|
2437
|
-
|
|
3092
|
+
// Shared schedule buffers only pick up input-derived words inside process(); if everything
|
|
3093
|
+
// was buffered without processing, there is nothing to zero.
|
|
3094
|
+
if (processed)
|
|
3095
|
+
this.roundClean();
|
|
2438
3096
|
return this;
|
|
2439
3097
|
}
|
|
2440
3098
|
digestInto(out) {
|
|
@@ -2446,32 +3104,33 @@ class HashMD {
|
|
|
2446
3104
|
// was previously not allocated here. But it won't change performance.
|
|
2447
3105
|
const { buffer, view, blockLen, isLE } = this;
|
|
2448
3106
|
let { pos } = this;
|
|
2449
|
-
// append the bit '1' to the message
|
|
3107
|
+
// append the bit '1' to the message, then zero-pad the rest of the block
|
|
2450
3108
|
buffer[pos++] = 0b10000000;
|
|
2451
|
-
|
|
3109
|
+
buffer.fill(0, pos);
|
|
2452
3110
|
// we have less than padOffset left in buffer, so we cannot put length in
|
|
2453
3111
|
// current block, need process it and pad again
|
|
2454
3112
|
if (this.padOffset > blockLen - pos) {
|
|
2455
3113
|
this.process(view, 0);
|
|
2456
|
-
|
|
3114
|
+
buffer.fill(0);
|
|
2457
3115
|
}
|
|
2458
|
-
// Pad until full block byte with zeros
|
|
2459
|
-
for (let i = pos; i < blockLen; i++)
|
|
2460
|
-
buffer[i] = 0;
|
|
2461
3116
|
// `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from
|
|
2462
3117
|
// the padding fill above, and JS will overflow before user input can make that half non-zero.
|
|
2463
|
-
// So we only need to write the low 64 bits here
|
|
2464
|
-
|
|
3118
|
+
// So we only need to write the low 64 bits here (`length * 8` only scales the exponent of an
|
|
3119
|
+
// integer below 2**53, so the split inside the helper stays exact).
|
|
3120
|
+
setU64FromNum(view, blockLen - 8, this.length * 8, isLE);
|
|
2465
3121
|
this.process(view, 0);
|
|
2466
|
-
|
|
3122
|
+
// The final block above is processed outside update(), so the shared message-schedule
|
|
3123
|
+
// buffers (e.g. SHA256_W) would otherwise retain input-derived words after digest().
|
|
3124
|
+
this.roundClean();
|
|
3125
|
+
// digest() passes our own `buffer` as `out`; reuse its cached view instead of allocating one.
|
|
3126
|
+
const oview = out === buffer ? view : createView(out);
|
|
2467
3127
|
const len = this.outputLen;
|
|
2468
3128
|
// NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT
|
|
2469
|
-
if (len % 4)
|
|
2470
|
-
throw new Error('_sha2: outputLen must be aligned to 32bit');
|
|
2471
3129
|
const outLen = len / 4;
|
|
2472
3130
|
const state = this.get();
|
|
2473
|
-
|
|
2474
|
-
|
|
3131
|
+
// Subclass-misconfiguration invariant: outputLen must be 32-bit aligned and fit the state.
|
|
3132
|
+
if (len % 4 || outLen > state.length)
|
|
3133
|
+
throw new Error('invalid outputLen');
|
|
2475
3134
|
for (let i = 0; i < outLen; i++)
|
|
2476
3135
|
oview.setUint32(4 * i, state[i], isLE);
|
|
2477
3136
|
}
|
|
@@ -2484,18 +3143,16 @@ class HashMD {
|
|
|
2484
3143
|
this.destroy();
|
|
2485
3144
|
return res;
|
|
2486
3145
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
to.set(...this.get());
|
|
2490
|
-
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
3146
|
+
_cloneIntoMeta(to) {
|
|
3147
|
+
const { buffer, length, finished, destroyed, pos } = this;
|
|
2491
3148
|
to.destroyed = destroyed;
|
|
2492
3149
|
to.finished = finished;
|
|
2493
3150
|
to.length = length;
|
|
2494
3151
|
to.pos = pos;
|
|
2495
3152
|
// Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and
|
|
2496
3153
|
// later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.
|
|
2497
|
-
if (
|
|
2498
|
-
to.buffer.set(buffer);
|
|
3154
|
+
if (pos)
|
|
3155
|
+
to.buffer.set(buffer); // Avoid a hot modulo guard.
|
|
2499
3156
|
return to;
|
|
2500
3157
|
}
|
|
2501
3158
|
clone() {
|
|
@@ -2539,8 +3196,28 @@ const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
|
2539
3196
|
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
2540
3197
|
/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */
|
|
2541
3198
|
class SHA2_32B extends HashMD {
|
|
2542
|
-
|
|
3199
|
+
// We cannot use array here since array allows indexing by variable
|
|
3200
|
+
// which means optimizer/compiler cannot use registers.
|
|
3201
|
+
// Numeric initializers matter: starting the fields as `undefined` changes
|
|
3202
|
+
// V8's field representation and makes sha256 3x slower (measured).
|
|
3203
|
+
A = 0;
|
|
3204
|
+
B = 0;
|
|
3205
|
+
C = 0;
|
|
3206
|
+
D = 0;
|
|
3207
|
+
E = 0;
|
|
3208
|
+
F = 0;
|
|
3209
|
+
G = 0;
|
|
3210
|
+
H = 0;
|
|
3211
|
+
constructor(outputLen, IV) {
|
|
2543
3212
|
super(64, outputLen, 8, false);
|
|
3213
|
+
this.A = IV[0] | 0;
|
|
3214
|
+
this.B = IV[1] | 0;
|
|
3215
|
+
this.C = IV[2] | 0;
|
|
3216
|
+
this.D = IV[3] | 0;
|
|
3217
|
+
this.E = IV[4] | 0;
|
|
3218
|
+
this.F = IV[5] | 0;
|
|
3219
|
+
this.G = IV[6] | 0;
|
|
3220
|
+
this.H = IV[7] | 0;
|
|
2544
3221
|
}
|
|
2545
3222
|
get() {
|
|
2546
3223
|
const { A, B, C, D, E, F, G, H } = this;
|
|
@@ -2557,6 +3234,10 @@ class SHA2_32B extends HashMD {
|
|
|
2557
3234
|
this.G = G | 0;
|
|
2558
3235
|
this.H = H | 0;
|
|
2559
3236
|
}
|
|
3237
|
+
_cloneInto(to) {
|
|
3238
|
+
(to ||= new this.constructor()).set(...this.get());
|
|
3239
|
+
return this._cloneIntoMeta(to);
|
|
3240
|
+
}
|
|
2560
3241
|
process(view, offset) {
|
|
2561
3242
|
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
|
|
2562
3243
|
for (let i = 0; i < 16; i++, offset += 4)
|
|
@@ -2608,153 +3289,39 @@ class SHA2_32B extends HashMD {
|
|
|
2608
3289
|
}
|
|
2609
3290
|
/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */
|
|
2610
3291
|
class _SHA256 extends SHA2_32B {
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
B = SHA256_IV[1] | 0;
|
|
2615
|
-
C = SHA256_IV[2] | 0;
|
|
2616
|
-
D = SHA256_IV[3] | 0;
|
|
2617
|
-
E = SHA256_IV[4] | 0;
|
|
2618
|
-
F = SHA256_IV[5] | 0;
|
|
2619
|
-
G = SHA256_IV[6] | 0;
|
|
2620
|
-
H = SHA256_IV[7] | 0;
|
|
2621
|
-
constructor() {
|
|
2622
|
-
super(32);
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
/**
|
|
2626
|
-
* SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:
|
|
2627
|
-
*
|
|
2628
|
-
* - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.
|
|
2629
|
-
* - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
2630
|
-
* - Each sha256 hash is executing 2^18 bit operations.
|
|
2631
|
-
* - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.
|
|
2632
|
-
* @param msg - message bytes to hash
|
|
2633
|
-
* @returns Digest bytes.
|
|
2634
|
-
* @example
|
|
2635
|
-
* Hash a message with SHA2-256.
|
|
2636
|
-
* ```ts
|
|
2637
|
-
* sha256(new Uint8Array([97, 98, 99]));
|
|
2638
|
-
* ```
|
|
2639
|
-
*/
|
|
2640
|
-
const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(),
|
|
2641
|
-
/* @__PURE__ */ oidNist(0x01));
|
|
2642
|
-
|
|
2643
|
-
const ONBOARDING_ZERO_DOMAIN = 'privacy-pool-onboarding-zero-v1';
|
|
2644
|
-
function sha256Hex$1(data) {
|
|
2645
|
-
return bytesToHex(sha256(data));
|
|
2646
|
-
}
|
|
2647
|
-
function appendU8$1(parts, value) {
|
|
2648
|
-
parts.push(Buffer.from([value & 0xff]));
|
|
2649
|
-
}
|
|
2650
|
-
function appendU32BE$1(parts, value) {
|
|
2651
|
-
const buf = Buffer.alloc(4);
|
|
2652
|
-
buf.writeUInt32BE(value >>> 0);
|
|
2653
|
-
parts.push(buf);
|
|
2654
|
-
}
|
|
2655
|
-
function appendU128BE$1(parts, value) {
|
|
2656
|
-
const buf = Buffer.alloc(16);
|
|
2657
|
-
buf.writeBigUInt64BE(value >> 64n, 0);
|
|
2658
|
-
buf.writeBigUInt64BE(value & ((1n << 64n) - 1n), 8);
|
|
2659
|
-
parts.push(buf);
|
|
2660
|
-
}
|
|
2661
|
-
function appendAddressStrkey$1(parts, address) {
|
|
2662
|
-
const bytes = Buffer.from(address.trim(), 'utf8');
|
|
2663
|
-
appendU32BE$1(parts, bytes.length);
|
|
2664
|
-
parts.push(bytes);
|
|
2665
|
-
}
|
|
2666
|
-
function appendBytes32$1(parts, value) {
|
|
2667
|
-
if (value.length !== 32) {
|
|
2668
|
-
throw new Error(`expected 32-byte field, got ${value.length}`);
|
|
2669
|
-
}
|
|
2670
|
-
parts.push(value);
|
|
2671
|
-
}
|
|
2672
|
-
function appendBytesBlob(parts, data) {
|
|
2673
|
-
appendU32BE$1(parts, data.length);
|
|
2674
|
-
parts.push(data);
|
|
2675
|
-
}
|
|
2676
|
-
function encodePlaintextNote(parts, note) {
|
|
2677
|
-
appendU128BE$1(parts, note.value);
|
|
2678
|
-
appendBytes32$1(parts, note.asset_hi);
|
|
2679
|
-
appendBytes32$1(parts, note.asset_lo);
|
|
2680
|
-
appendBytes32$1(parts, note.nullifier);
|
|
2681
|
-
appendBytes32$1(parts, note.secret);
|
|
2682
|
-
if (note.deposited_ephemeral_scalar !== undefined) {
|
|
2683
|
-
appendU8$1(parts, 1);
|
|
2684
|
-
appendBytes32$1(parts, note.deposited_ephemeral_scalar);
|
|
2685
|
-
}
|
|
2686
|
-
else {
|
|
2687
|
-
appendU8$1(parts, 0);
|
|
2688
|
-
}
|
|
2689
|
-
}
|
|
2690
|
-
function encodeOptionalRegistration(parts, registration) {
|
|
2691
|
-
if (registration.tag === 'None') {
|
|
2692
|
-
appendU8$1(parts, 0);
|
|
2693
|
-
return;
|
|
2694
|
-
}
|
|
2695
|
-
appendU8$1(parts, 1);
|
|
2696
|
-
const reg = registration.values[0];
|
|
2697
|
-
appendAddressStrkey$1(parts, reg.owner);
|
|
2698
|
-
appendBytes32$1(parts, reg.public_key_x);
|
|
2699
|
-
appendBytes32$1(parts, reg.public_key_y);
|
|
2700
|
-
}
|
|
2701
|
-
/** Canonical `onboardingHash` matching `libs/kyt-passage/src/lib.rs::hash_onboarding`. */
|
|
2702
|
-
function hashOnboardingCanonical(onboarding) {
|
|
2703
|
-
if (onboarding === null || onboarding === undefined) {
|
|
2704
|
-
return sha256Hex$1(Buffer.from(ONBOARDING_ZERO_DOMAIN, 'utf8'));
|
|
2705
|
-
}
|
|
2706
|
-
const parts = [];
|
|
2707
|
-
appendAddressStrkey$1(parts, onboarding.owner);
|
|
2708
|
-
appendBytes32$1(parts, onboarding.temp_public_key_x);
|
|
2709
|
-
appendBytes32$1(parts, onboarding.temp_public_key_y);
|
|
2710
|
-
appendBytesBlob(parts, onboarding.encrypted_private_key);
|
|
2711
|
-
appendU32BE$1(parts, onboarding.notes.length);
|
|
2712
|
-
for (const note of onboarding.notes) {
|
|
2713
|
-
encodePlaintextNote(parts, note);
|
|
2714
|
-
}
|
|
2715
|
-
encodeOptionalRegistration(parts, onboarding.private_address_registration);
|
|
2716
|
-
return sha256Hex$1(Buffer.concat(parts));
|
|
2717
|
-
}
|
|
2718
|
-
function onboardingContractValue(onboarding) {
|
|
2719
|
-
return {
|
|
2720
|
-
owner: onboarding.owner,
|
|
2721
|
-
temp_public_key_x: onboarding.temp_public_key_x,
|
|
2722
|
-
temp_public_key_y: onboarding.temp_public_key_y,
|
|
2723
|
-
encrypted_private_key: onboarding.encrypted_private_key,
|
|
2724
|
-
notes: {
|
|
2725
|
-
notes: onboarding.notes.map((note) => ({
|
|
2726
|
-
value: note.value,
|
|
2727
|
-
asset_hi: note.asset_hi,
|
|
2728
|
-
asset_lo: note.asset_lo,
|
|
2729
|
-
nullifier: note.nullifier,
|
|
2730
|
-
secret: note.secret,
|
|
2731
|
-
deposited_ephemeral_scalar: note.deposited_ephemeral_scalar,
|
|
2732
|
-
})),
|
|
2733
|
-
},
|
|
2734
|
-
private_address_registration: onboarding.private_address_registration,
|
|
2735
|
-
};
|
|
2736
|
-
}
|
|
2737
|
-
function onboardingToScVal(onboarding) {
|
|
2738
|
-
return nativeToScVal(onboardingContractValue(onboarding));
|
|
2739
|
-
}
|
|
2740
|
-
function onboardingOptionToScVal(onboarding) {
|
|
2741
|
-
if (onboarding === null || onboarding === undefined) {
|
|
2742
|
-
return xdr.ScVal.scvVoid();
|
|
2743
|
-
}
|
|
2744
|
-
return onboardingToScVal(onboarding);
|
|
2745
|
-
}
|
|
2746
|
-
function addressToScVal(address) {
|
|
2747
|
-
return nativeToScVal(address, { type: 'address' });
|
|
3292
|
+
constructor() {
|
|
3293
|
+
super(32, SHA256_IV);
|
|
3294
|
+
}
|
|
2748
3295
|
}
|
|
3296
|
+
/**
|
|
3297
|
+
* SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:
|
|
3298
|
+
*
|
|
3299
|
+
* - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.
|
|
3300
|
+
* - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
3301
|
+
* - Each sha256 hash is executing 2^18 bit operations.
|
|
3302
|
+
* - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.
|
|
3303
|
+
* @param msg - message bytes to hash
|
|
3304
|
+
* @param opts - Reserved hash options.
|
|
3305
|
+
* @returns Digest bytes.
|
|
3306
|
+
* @example
|
|
3307
|
+
* Hash a message with SHA2-256.
|
|
3308
|
+
* ```ts
|
|
3309
|
+
* sha256(new Uint8Array([97, 98, 99]));
|
|
3310
|
+
* ```
|
|
3311
|
+
*/
|
|
3312
|
+
const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(),
|
|
3313
|
+
/* @__PURE__ */ oidNist(0x01));
|
|
2749
3314
|
|
|
2750
3315
|
const KYT_PASSAGE_AUTH_DOMAIN = 'privacy-pool-kyt-passage-v1';
|
|
2751
3316
|
const PUBLIC_LEG_CONTEXT_DOMAIN = 'privacy-pool-public-leg-context-v1';
|
|
2752
|
-
const PASSAGE_ID_DOMAIN = 'privacy-pool-passage-id-
|
|
2753
|
-
const
|
|
2754
|
-
const
|
|
3317
|
+
const PASSAGE_ID_DOMAIN = 'privacy-pool-passage-id-v3';
|
|
3318
|
+
const PASSAGE_ID_DOMAIN_V2 = 'privacy-pool-passage-id-v2';
|
|
3319
|
+
const PASSAGE_ID_DOMAIN_V1 = 'privacy-pool-passage-id-v1';
|
|
3320
|
+
const NOTE_AUDIT_LEN = 12;
|
|
3321
|
+
const TOTAL_PUBLIC_SIGNALS = 93;
|
|
3322
|
+
const NOTE_AUDIT_PLAINTEXT_LEN = 12;
|
|
2755
3323
|
const FIELD_BYTES = 32;
|
|
2756
3324
|
const LENGTH_PREFIX_BYTES = 4;
|
|
2757
|
-
const STATE_ROOT_SIGNAL_INDEX = 70;
|
|
2758
3325
|
function sha256Hex(data) {
|
|
2759
3326
|
return bytesToHex(sha256(data));
|
|
2760
3327
|
}
|
|
@@ -2763,7 +3330,14 @@ function publicSignalsBuffer(publicSignalsBytes) {
|
|
|
2763
3330
|
? Buffer.from(publicSignalsBytes.replace(/^0x/, ''), 'hex')
|
|
2764
3331
|
: publicSignalsBytes;
|
|
2765
3332
|
}
|
|
2766
|
-
function
|
|
3333
|
+
function expectedSignalCountForNonce(zkConfigNonce = 0n) {
|
|
3334
|
+
return totalPublicSignals(layoutForKnownNonce(zkConfigNonce));
|
|
3335
|
+
}
|
|
3336
|
+
function stateRootIndexForNonce(zkConfigNonce = 0n) {
|
|
3337
|
+
return stateRootIndex(layoutForKnownNonce(zkConfigNonce));
|
|
3338
|
+
}
|
|
3339
|
+
function parsePublicSignalsBytes(publicSignalsBytes, zkConfigNonce = 0n) {
|
|
3340
|
+
const expectedCount = expectedSignalCountForNonce(zkConfigNonce);
|
|
2767
3341
|
const buf = publicSignalsBuffer(publicSignalsBytes);
|
|
2768
3342
|
let offset = 0;
|
|
2769
3343
|
let hasLengthPrefix = false;
|
|
@@ -2775,11 +3349,11 @@ function parsePublicSignalsBytes(publicSignalsBytes) {
|
|
|
2775
3349
|
}
|
|
2776
3350
|
}
|
|
2777
3351
|
const signalBytes = buf.subarray(offset);
|
|
2778
|
-
if (signalBytes.length !==
|
|
2779
|
-
throw new Error(`expected ${
|
|
3352
|
+
if (signalBytes.length !== expectedCount * FIELD_BYTES) {
|
|
3353
|
+
throw new Error(`expected ${expectedCount} public signals`);
|
|
2780
3354
|
}
|
|
2781
3355
|
return {
|
|
2782
|
-
fields: Array.from({ length:
|
|
3356
|
+
fields: Array.from({ length: expectedCount }, (_, index) => Buffer.from(signalBytes.subarray(index * FIELD_BYTES, (index + 1) * FIELD_BYTES))),
|
|
2783
3357
|
hasLengthPrefix,
|
|
2784
3358
|
};
|
|
2785
3359
|
}
|
|
@@ -2874,9 +3448,9 @@ function encodeLegSlot(parts, kind, slot, isPresent, assetHi, assetLo, amount, a
|
|
|
2874
3448
|
parts.push(zeroAddressBuffer());
|
|
2875
3449
|
}
|
|
2876
3450
|
}
|
|
2877
|
-
function hashPublicSignalBytes(publicSignalsBytes) {
|
|
2878
|
-
const parsed = parsePublicSignalsBytes(publicSignalsBytes);
|
|
2879
|
-
parsed.fields[
|
|
3451
|
+
function hashPublicSignalBytes(publicSignalsBytes, zkConfigNonce = 0n) {
|
|
3452
|
+
const parsed = parsePublicSignalsBytes(publicSignalsBytes, zkConfigNonce);
|
|
3453
|
+
parsed.fields[stateRootIndexForNonce(zkConfigNonce)] = Buffer.alloc(FIELD_BYTES);
|
|
2880
3454
|
return sha256Hex(encodePublicSignalsBytes(parsed.fields, parsed.hasLengthPrefix));
|
|
2881
3455
|
}
|
|
2882
3456
|
function hashPublicLegContext(layout, input) {
|
|
@@ -2898,16 +3472,32 @@ function hashPublicLegContext(layout, input) {
|
|
|
2898
3472
|
}
|
|
2899
3473
|
return sha256Hex(Buffer.concat(parts));
|
|
2900
3474
|
}
|
|
2901
|
-
function
|
|
2902
|
-
return hashOnboardingCanonical(onboarding);
|
|
2903
|
-
}
|
|
2904
|
-
function derivePassageId(owner, nonce, publicSignalHash, publicLegContextHash, onboardingHash) {
|
|
3475
|
+
function derivePassageId(owner, nonce, publicSignalHash, publicLegContextHash, ciphertextHash) {
|
|
2905
3476
|
const parts = [];
|
|
2906
3477
|
appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN, 'utf8'));
|
|
2907
3478
|
appendAddressStrkey(parts, owner);
|
|
2908
3479
|
appendU64BE(parts, nonce);
|
|
2909
3480
|
appendBytes32(parts, publicSignalHash);
|
|
2910
3481
|
appendBytes32(parts, publicLegContextHash);
|
|
3482
|
+
appendBytes32(parts, ciphertextHash);
|
|
3483
|
+
return sha256Hex(Buffer.concat(parts));
|
|
3484
|
+
}
|
|
3485
|
+
function derivePassageIdV2(owner, nonce, publicSignalHash, publicLegContextHash) {
|
|
3486
|
+
const parts = [];
|
|
3487
|
+
appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN_V2, 'utf8'));
|
|
3488
|
+
appendAddressStrkey(parts, owner);
|
|
3489
|
+
appendU64BE(parts, nonce);
|
|
3490
|
+
appendBytes32(parts, publicSignalHash);
|
|
3491
|
+
appendBytes32(parts, publicLegContextHash);
|
|
3492
|
+
return sha256Hex(Buffer.concat(parts));
|
|
3493
|
+
}
|
|
3494
|
+
function derivePassageIdV1(owner, nonce, publicSignalHash, publicLegContextHash, onboardingHash) {
|
|
3495
|
+
const parts = [];
|
|
3496
|
+
appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN_V1, 'utf8'));
|
|
3497
|
+
appendAddressStrkey(parts, owner);
|
|
3498
|
+
appendU64BE(parts, nonce);
|
|
3499
|
+
appendBytes32(parts, publicSignalHash);
|
|
3500
|
+
appendBytes32(parts, publicLegContextHash);
|
|
2911
3501
|
appendBytes32(parts, onboardingHash);
|
|
2912
3502
|
return sha256Hex(Buffer.concat(parts));
|
|
2913
3503
|
}
|
|
@@ -2929,32 +3519,93 @@ function buildKytPassageAuthorization(input) {
|
|
|
2929
3519
|
}
|
|
2930
3520
|
function derivePassageFromTransactContext(input) {
|
|
2931
3521
|
const layout = input.layout ?? { publicNInputs: 1, publicNOutputs: 1 };
|
|
2932
|
-
const nonce = input.nonce ??
|
|
2933
|
-
const publicSignalHash = hashPublicSignalBytes(input.publicSignalsBytes);
|
|
3522
|
+
const nonce = BigInt(input.nonce ?? 0);
|
|
3523
|
+
const publicSignalHash = hashPublicSignalBytes(input.publicSignalsBytes, nonce);
|
|
2934
3524
|
const publicLegContextHash = hashPublicLegContext(layout, input.publicLegContext);
|
|
2935
|
-
const
|
|
2936
|
-
const passageId =
|
|
3525
|
+
const zkLayout = layoutForKnownNonce(nonce);
|
|
3526
|
+
const passageId = ciphertextsInPublicSignals(zkLayout)
|
|
3527
|
+
? derivePassageIdV2(input.owner, nonce, publicSignalHash, publicLegContextHash)
|
|
3528
|
+
: derivePassageId(input.owner, nonce, publicSignalHash, publicLegContextHash, hashCiphertextBytes(input.ciphertextBytes ?? Buffer.alloc(0)));
|
|
2937
3529
|
return {
|
|
2938
3530
|
publicSignalHash,
|
|
2939
3531
|
publicLegContextHash,
|
|
2940
|
-
onboardingHash,
|
|
2941
3532
|
passageId,
|
|
2942
3533
|
};
|
|
2943
3534
|
}
|
|
3535
|
+
function hashCiphertextBytes(ciphertextBytes) {
|
|
3536
|
+
const buffer = typeof ciphertextBytes === 'string'
|
|
3537
|
+
? Buffer.from(ciphertextBytes.replace(/^0x/i, ''), 'hex')
|
|
3538
|
+
: ciphertextBytes;
|
|
3539
|
+
return sha256Hex(buffer);
|
|
3540
|
+
}
|
|
3541
|
+
function zeroBindingTagsInPublicSignals(publicSignalsBytes, zkConfigNonce = 0n) {
|
|
3542
|
+
const layout = layoutForKnownNonce(zkConfigNonce);
|
|
3543
|
+
if (ciphertextsInPublicSignals(layout)) {
|
|
3544
|
+
return publicSignalsBuffer(publicSignalsBytes);
|
|
3545
|
+
}
|
|
3546
|
+
const parsed = parsePublicSignalsBytes(publicSignalsBytes, zkConfigNonce);
|
|
3547
|
+
const zero = Buffer.alloc(FIELD_BYTES);
|
|
3548
|
+
const auditStart = indexAuditTags(layout);
|
|
3549
|
+
for (let slot = 0; slot < layout.nAuditSlots; slot += 1) {
|
|
3550
|
+
parsed.fields[auditStart + slot] = Buffer.from(zero);
|
|
3551
|
+
}
|
|
3552
|
+
const outputStart = indexOutputNoteTags(layout);
|
|
3553
|
+
for (let note = 0; note < layout.nOuts; note += 1) {
|
|
3554
|
+
parsed.fields[outputStart + note] = Buffer.from(zero);
|
|
3555
|
+
}
|
|
3556
|
+
return encodePublicSignalsBytes(parsed.fields, parsed.hasLengthPrefix);
|
|
3557
|
+
}
|
|
3558
|
+
function spliceBindingTagsIntoPublicSignals(parameters) {
|
|
3559
|
+
const layout = layoutForKnownNonce(parameters.zkConfigNonce);
|
|
3560
|
+
const parsed = parsePublicSignalsBytes(parameters.publicSignalsBytes, parameters.zkConfigNonce);
|
|
3561
|
+
const auditStart = indexAuditTags(layout);
|
|
3562
|
+
for (let slot = 0; slot < layout.nAuditSlots; slot += 1) {
|
|
3563
|
+
const tag = parameters.auditTags[slot];
|
|
3564
|
+
if (tag !== undefined) {
|
|
3565
|
+
parsed.fields[auditStart + slot] = fieldElementBuffer(tag);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
const outputStart = indexOutputNoteTags(layout);
|
|
3569
|
+
for (let note = 0; note < layout.nOuts; note += 1) {
|
|
3570
|
+
const tag = parameters.outputNoteTags[note];
|
|
3571
|
+
if (tag !== undefined) {
|
|
3572
|
+
parsed.fields[outputStart + note] = fieldElementBuffer(tag);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
return encodePublicSignalsBytes(parsed.fields, parsed.hasLengthPrefix);
|
|
3576
|
+
}
|
|
3577
|
+
function fieldElementBuffer(value) {
|
|
3578
|
+
const trimmed = value.trim().replace(/^0x/i, '');
|
|
3579
|
+
const asBigint = /^[0-9]+$/.test(trimmed)
|
|
3580
|
+
? BigInt(trimmed)
|
|
3581
|
+
: BigInt(`0x${trimmed}`);
|
|
3582
|
+
const buf = Buffer.alloc(FIELD_BYTES);
|
|
3583
|
+
let remaining = asBigint;
|
|
3584
|
+
for (let index = FIELD_BYTES - 1; index >= 0; index -= 1) {
|
|
3585
|
+
buf[index] = Number(remaining & 0xffn);
|
|
3586
|
+
remaining >>= 8n;
|
|
3587
|
+
}
|
|
3588
|
+
return buf;
|
|
3589
|
+
}
|
|
2944
3590
|
async function inspectKytPassage(backendUrl, request) {
|
|
3591
|
+
const nonce = BigInt(request.zkConfigNonce ?? 0);
|
|
3592
|
+
const body = {
|
|
3593
|
+
...request,
|
|
3594
|
+
publicSignalsBytes: zeroBindingTagsInPublicSignals(request.publicSignalsBytes, nonce).toString('hex'),
|
|
3595
|
+
};
|
|
2945
3596
|
const response = await fetch(`${backendUrl.replace(/\/$/, '')}/api/kyt/passages/inspect`, {
|
|
2946
3597
|
method: 'POST',
|
|
2947
3598
|
headers: { 'content-type': 'application/json' },
|
|
2948
|
-
body: JSON.stringify(
|
|
3599
|
+
body: JSON.stringify(body),
|
|
2949
3600
|
});
|
|
2950
3601
|
if (!response.ok) {
|
|
2951
3602
|
throw new Error(`KYT inspect failed: ${response.status} ${await response.text()}`);
|
|
2952
3603
|
}
|
|
2953
|
-
const
|
|
2954
|
-
if (
|
|
2955
|
-
throw new Error(`KYT inspect rejected: ${JSON.stringify(
|
|
3604
|
+
const payload = await response.json();
|
|
3605
|
+
if (payload.status !== 'approved') {
|
|
3606
|
+
throw new Error(`KYT inspect rejected: ${JSON.stringify(payload)}`);
|
|
2956
3607
|
}
|
|
2957
|
-
return
|
|
3608
|
+
return payload;
|
|
2958
3609
|
}
|
|
2959
3610
|
function signatureBase64ToBytes(signature) {
|
|
2960
3611
|
const value = signature.trim();
|
|
@@ -2968,21 +3619,31 @@ var kytPassage = /*#__PURE__*/Object.freeze({
|
|
|
2968
3619
|
__proto__: null,
|
|
2969
3620
|
KYT_PASSAGE_AUTH_DOMAIN: KYT_PASSAGE_AUTH_DOMAIN,
|
|
2970
3621
|
NOTE_AUDIT_LEN: NOTE_AUDIT_LEN,
|
|
3622
|
+
NOTE_AUDIT_PLAINTEXT_LEN: NOTE_AUDIT_PLAINTEXT_LEN,
|
|
2971
3623
|
PASSAGE_ID_DOMAIN: PASSAGE_ID_DOMAIN,
|
|
3624
|
+
PASSAGE_ID_DOMAIN_V1: PASSAGE_ID_DOMAIN_V1,
|
|
3625
|
+
PASSAGE_ID_DOMAIN_V2: PASSAGE_ID_DOMAIN_V2,
|
|
2972
3626
|
PUBLIC_LEG_CONTEXT_DOMAIN: PUBLIC_LEG_CONTEXT_DOMAIN,
|
|
2973
3627
|
TOTAL_PUBLIC_SIGNALS: TOTAL_PUBLIC_SIGNALS,
|
|
2974
3628
|
addressToCanonicalXdr: addressToCanonicalXdr,
|
|
2975
3629
|
buildKytPassageAuthorization: buildKytPassageAuthorization,
|
|
2976
3630
|
derivePassageFromTransactContext: derivePassageFromTransactContext,
|
|
2977
3631
|
derivePassageId: derivePassageId,
|
|
2978
|
-
|
|
3632
|
+
derivePassageIdV1: derivePassageIdV1,
|
|
3633
|
+
derivePassageIdV2: derivePassageIdV2,
|
|
3634
|
+
hashCiphertextBytes: hashCiphertextBytes,
|
|
2979
3635
|
hashPublicLegContext: hashPublicLegContext,
|
|
2980
3636
|
hashPublicSignalBytes: hashPublicSignalBytes,
|
|
2981
3637
|
inspectKytPassage: inspectKytPassage,
|
|
2982
3638
|
signatureBase64ToBytes: signatureBase64ToBytes,
|
|
2983
|
-
|
|
3639
|
+
spliceBindingTagsIntoPublicSignals: spliceBindingTagsIntoPublicSignals,
|
|
3640
|
+
zeroAddressSentinelBuffer: zeroAddressSentinelBuffer,
|
|
3641
|
+
zeroBindingTagsInPublicSignals: zeroBindingTagsInPublicSignals
|
|
2984
3642
|
});
|
|
2985
3643
|
|
|
3644
|
+
function addressToScVal(address) {
|
|
3645
|
+
return nativeToScVal(address, { type: 'address' });
|
|
3646
|
+
}
|
|
2986
3647
|
function passageIdHexToScVal(passageIdHex) {
|
|
2987
3648
|
const bytes = Buffer.from(passageIdHex.replace(/^0x/, ''), 'hex');
|
|
2988
3649
|
return xdr.ScVal.scvBytes(bytes);
|
|
@@ -3019,7 +3680,7 @@ function poolTransactOperation(params) {
|
|
|
3019
3680
|
nativeToScVal(params.nonce, { type: 'u64' }),
|
|
3020
3681
|
xdr.ScVal.scvBytes(params.proofBytes),
|
|
3021
3682
|
xdr.ScVal.scvBytes(params.publicSignalsBytes),
|
|
3022
|
-
|
|
3683
|
+
xdr.ScVal.scvBytes(params.ciphertextBytes ?? Buffer.alloc(0)),
|
|
3023
3684
|
kytAuthorizationOptionToScVal(params.kytAuthorization),
|
|
3024
3685
|
],
|
|
3025
3686
|
});
|
|
@@ -3033,7 +3694,6 @@ function helperSubmitWithPassageOperation(helperContractId, params, passageId, e
|
|
|
3033
3694
|
nativeToScVal(params.nonce, { type: 'u64' }),
|
|
3034
3695
|
xdr.ScVal.scvBytes(params.proofBytes),
|
|
3035
3696
|
xdr.ScVal.scvBytes(params.publicSignalsBytes),
|
|
3036
|
-
onboardingOptionToScVal(params.onboarding),
|
|
3037
3697
|
passageIdHexToScVal(passageId),
|
|
3038
3698
|
xdr.ScVal.scvU32(expiresAtLedger),
|
|
3039
3699
|
signatureToScVal(signatureBase64ToBytes(signature)),
|
|
@@ -3165,8 +3825,8 @@ async function registerKytPassage(params) {
|
|
|
3165
3825
|
owner: params.owner,
|
|
3166
3826
|
nonce: params.nonce,
|
|
3167
3827
|
publicSignalsBytes: params.publicSignalsBytes,
|
|
3828
|
+
ciphertextBytes: params.ciphertextBytes,
|
|
3168
3829
|
publicLegContext: params.publicLegContext,
|
|
3169
|
-
onboarding: params.onboarding,
|
|
3170
3830
|
});
|
|
3171
3831
|
await submitTransaction({
|
|
3172
3832
|
source: params.source,
|
|
@@ -3215,8 +3875,8 @@ async function submitWithKytPassage(params) {
|
|
|
3215
3875
|
owner: params.owner,
|
|
3216
3876
|
nonce: params.nonce,
|
|
3217
3877
|
publicSignalsBytes: params.publicSignalsBytes,
|
|
3878
|
+
ciphertextBytes: params.ciphertextBytes,
|
|
3218
3879
|
publicLegContext: params.publicLegContext,
|
|
3219
|
-
onboarding: params.onboarding,
|
|
3220
3880
|
});
|
|
3221
3881
|
if (approved.passageId.replace(/^0x/, '') !== derivation.passageId) {
|
|
3222
3882
|
throw new Error('KYT approval passageId does not match local transaction context');
|
|
@@ -3231,7 +3891,6 @@ async function submitWithKytPassage(params) {
|
|
|
3231
3891
|
nonce: params.nonce,
|
|
3232
3892
|
proofBytes: params.proofBytes,
|
|
3233
3893
|
publicSignalsBytes: params.publicSignalsBytes,
|
|
3234
|
-
onboarding: params.onboarding,
|
|
3235
3894
|
kytSubmitHelperId: params.kytSubmitHelperId,
|
|
3236
3895
|
passageId: derivation.passageId,
|
|
3237
3896
|
expiresAtLedger: approved.expiresAtLedger,
|
|
@@ -3255,246 +3914,43 @@ function localSignKytPassageAuthorization(secretSeed, authorization) {
|
|
|
3255
3914
|
return sig.toString('base64');
|
|
3256
3915
|
}
|
|
3257
3916
|
|
|
3258
|
-
const
|
|
3259
|
-
const
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
const FR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
3263
|
-
const ONE = 1n;
|
|
3264
|
-
const ZERO = 0n;
|
|
3265
|
-
const BABYJUB_SCALAR_BITS = 253n;
|
|
3266
|
-
const SCALAR_253_MASK = (ONE << BABYJUB_SCALAR_BITS) - ONE;
|
|
3267
|
-
let primitivesPromise;
|
|
3268
|
-
function primitives() {
|
|
3269
|
-
primitivesPromise ?? (primitivesPromise = Promise.all([
|
|
3270
|
-
buildBabyjub(),
|
|
3271
|
-
buildMimc7(),
|
|
3272
|
-
buildPoseidon(),
|
|
3273
|
-
]).then(([babyJub, mimc7, poseidon]) => ({ babyJub, mimc7, poseidon })));
|
|
3274
|
-
return primitivesPromise;
|
|
3275
|
-
}
|
|
3276
|
-
function normalizeHex(value) {
|
|
3277
|
-
return value.trim().replace(/^0x/i, '').padStart(FR_SIZE * 2, '0');
|
|
3278
|
-
}
|
|
3279
|
-
function bigintFromHex(value) {
|
|
3280
|
-
return BigInt(`0x${normalizeHex(value)}`);
|
|
3281
|
-
}
|
|
3282
|
-
function bigintFromDecimal(value) {
|
|
3283
|
-
const parsed = BigInt(value);
|
|
3284
|
-
return parsed % FR_MODULUS;
|
|
3285
|
-
}
|
|
3286
|
-
function bigintToFrHex(value) {
|
|
3287
|
-
let v = moduleFr(value);
|
|
3288
|
-
const buf = Buffer.alloc(FR_SIZE);
|
|
3289
|
-
for (let index = FR_SIZE - 1; index >= 0; index -= 1) {
|
|
3290
|
-
buf[index] = Number(v & 0xffn);
|
|
3291
|
-
v >>= 8n;
|
|
3292
|
-
}
|
|
3293
|
-
return buf.toString('hex');
|
|
3294
|
-
}
|
|
3295
|
-
function frDecimalToHex(value) {
|
|
3296
|
-
return bigintToFrHex(bigintFromDecimal(value));
|
|
3297
|
-
}
|
|
3298
|
-
function frDecimalFromBigint(value) {
|
|
3299
|
-
const reduced = value >= ZERO ? value % FR_MODULUS : (value % FR_MODULUS) + FR_MODULUS;
|
|
3300
|
-
return reduced.toString(10);
|
|
3301
|
-
}
|
|
3302
|
-
function fieldBigint(field, value) {
|
|
3303
|
-
return BigInt(field.toString(value));
|
|
3304
|
-
}
|
|
3305
|
-
function moduleFr(value) {
|
|
3306
|
-
const reduced = value % FR_MODULUS;
|
|
3307
|
-
return reduced >= ZERO ? reduced : reduced + FR_MODULUS;
|
|
3308
|
-
}
|
|
3309
|
-
function mimcHash(parameters) {
|
|
3310
|
-
return fieldBigint(parameters.mimc7.F, parameters.mimc7.hash(parameters.mimc7.F.e(parameters.message), parameters.mimc7.F.e(parameters.key)));
|
|
3311
|
-
}
|
|
3312
|
-
function poseidonHash(poseidon, inputs) {
|
|
3313
|
-
return fieldBigint(poseidon.F, poseidon(inputs.map((input) => poseidon.F.e(input))));
|
|
3314
|
-
}
|
|
3315
|
-
function sharedKeyFromDepositor(parameters) {
|
|
3316
|
-
const scalarHex = frDecimalToHex(parameters.ephemeralKeyScalarDecimal);
|
|
3317
|
-
const shared = parameters.ecdhShared(scalarHex, frDecimalToHex(parameters.recipientPublicKey[0]), frDecimalToHex(parameters.recipientPublicKey[1]));
|
|
3318
|
-
return [bigintFromHex(shared.x), bigintFromHex(shared.y)];
|
|
3319
|
-
}
|
|
3320
|
-
function sharedKeyFromRecipient(parameters) {
|
|
3321
|
-
const { babyJub, recipientScalarHex, ephemeralPublicKey } = parameters;
|
|
3322
|
-
const point = [
|
|
3323
|
-
babyJub.F.e(bigintFromHex(ephemeralPublicKey[0])),
|
|
3324
|
-
babyJub.F.e(bigintFromHex(ephemeralPublicKey[1])),
|
|
3325
|
-
];
|
|
3326
|
-
if (!babyJub.inCurve(point)) {
|
|
3327
|
-
throw new Error('Created ephemeral public key is not on BabyJubJub curve');
|
|
3328
|
-
}
|
|
3329
|
-
const product = babyJub.mulPointEscalar(point, bigintFromHex(recipientScalarHex) & SCALAR_253_MASK);
|
|
3330
|
-
return [
|
|
3331
|
-
fieldBigint(babyJub.F, product[0]),
|
|
3332
|
-
fieldBigint(babyJub.F, product[1]),
|
|
3333
|
-
];
|
|
3917
|
+
const SCALAR_HEX_LENGTH = 64;
|
|
3918
|
+
const OUT_OF_RANGE_MESSAGE = 'derived escrow hash is outside BabyJub subgroup; retry nonce';
|
|
3919
|
+
function hexFromCanonicalScalar(value) {
|
|
3920
|
+
return value.toString(16).padStart(SCALAR_HEX_LENGTH, '0');
|
|
3334
3921
|
}
|
|
3335
|
-
function
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
OUTPUT_NOTE_TAG_DOMAIN,
|
|
3339
|
-
BigInt(parameters.plaintext.length),
|
|
3340
|
-
]);
|
|
3341
|
-
for (const field of parameters.plaintext) {
|
|
3342
|
-
accumulator = poseidonHash(parameters.poseidon, [accumulator, field]);
|
|
3922
|
+
function assertCanonicalBabyJubScalar(scalar) {
|
|
3923
|
+
if (!(scalar > 0n && scalar < BABYJUB_SUBGROUP_ORDER)) {
|
|
3924
|
+
throw new Error(OUT_OF_RANGE_MESSAGE);
|
|
3343
3925
|
}
|
|
3344
|
-
return mimcHash({
|
|
3345
|
-
mimc7: parameters.mimc7,
|
|
3346
|
-
message: accumulator,
|
|
3347
|
-
key: parameters.key,
|
|
3348
|
-
});
|
|
3349
|
-
}
|
|
3350
|
-
function encryptFields(parameters) {
|
|
3351
|
-
return parameters.plaintext.map((field, index) => {
|
|
3352
|
-
const seed = poseidonHash(parameters.poseidon, [
|
|
3353
|
-
parameters.nonce,
|
|
3354
|
-
STREAM_DOMAIN,
|
|
3355
|
-
BigInt(index),
|
|
3356
|
-
]);
|
|
3357
|
-
const stream = mimcHash({
|
|
3358
|
-
mimc7: parameters.mimc7,
|
|
3359
|
-
message: seed,
|
|
3360
|
-
key: parameters.key,
|
|
3361
|
-
});
|
|
3362
|
-
return moduleFr(field + stream);
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
|
-
function decryptFields(parameters) {
|
|
3366
|
-
return parameters.ciphertext.map((field, index) => {
|
|
3367
|
-
const seed = poseidonHash(parameters.poseidon, [
|
|
3368
|
-
parameters.nonce,
|
|
3369
|
-
STREAM_DOMAIN,
|
|
3370
|
-
BigInt(index),
|
|
3371
|
-
]);
|
|
3372
|
-
const stream = mimcHash({
|
|
3373
|
-
mimc7: parameters.mimc7,
|
|
3374
|
-
message: seed,
|
|
3375
|
-
key: parameters.key,
|
|
3376
|
-
});
|
|
3377
|
-
return moduleFr(bigintFromHex(field) - stream);
|
|
3378
|
-
});
|
|
3379
|
-
}
|
|
3380
|
-
function plaintextBigints(plaintext) {
|
|
3381
|
-
return [
|
|
3382
|
-
bigintFromDecimal(plaintext.value),
|
|
3383
|
-
bigintFromDecimal(plaintext.assetHi),
|
|
3384
|
-
bigintFromDecimal(plaintext.assetLo),
|
|
3385
|
-
bigintFromDecimal(plaintext.nullifier),
|
|
3386
|
-
bigintFromDecimal(plaintext.secret),
|
|
3387
|
-
bigintFromDecimal(plaintext.applicationId),
|
|
3388
|
-
];
|
|
3389
|
-
}
|
|
3390
|
-
function plaintextFromBigints(fields) {
|
|
3391
|
-
return {
|
|
3392
|
-
value: frDecimalFromBigint(fields[0] ?? ZERO),
|
|
3393
|
-
assetHi: frDecimalFromBigint(fields[1] ?? ZERO),
|
|
3394
|
-
assetLo: frDecimalFromBigint(fields[2] ?? ZERO),
|
|
3395
|
-
nullifier: frDecimalFromBigint(fields[3] ?? ZERO),
|
|
3396
|
-
secret: frDecimalFromBigint(fields[4] ?? ZERO),
|
|
3397
|
-
applicationId: frDecimalFromBigint(fields[5] ?? ZERO),
|
|
3398
|
-
};
|
|
3399
|
-
}
|
|
3400
|
-
async function secretFromDepositEphemeralScalarDecimal(ephemeralKeyScalarDecimal) {
|
|
3401
|
-
const { poseidon } = await primitives();
|
|
3402
|
-
const secret = poseidonHash(poseidon, [bigintFromDecimal(ephemeralKeyScalarDecimal)]);
|
|
3403
|
-
return frDecimalFromBigint(secret);
|
|
3404
3926
|
}
|
|
3405
|
-
async function
|
|
3406
|
-
const
|
|
3407
|
-
const
|
|
3408
|
-
const
|
|
3409
|
-
|
|
3410
|
-
assetHi: parameters.deposit.asset[0],
|
|
3411
|
-
assetLo: parameters.deposit.asset[1],
|
|
3412
|
-
nullifier: parameters.deposit.nullifier,
|
|
3413
|
-
secret,
|
|
3414
|
-
applicationId: parameters.deposit.applicationId,
|
|
3415
|
-
};
|
|
3416
|
-
const [key, nonce] = sharedKeyFromDepositor({
|
|
3417
|
-
babyJub,
|
|
3418
|
-
ecdhShared: parameters.ecdhShared,
|
|
3419
|
-
ephemeralKeyScalarDecimal: parameters.deposit.ephemeralKeyScalar,
|
|
3420
|
-
recipientPublicKey: parameters.deposit.recipientPublicKeys,
|
|
3421
|
-
});
|
|
3422
|
-
const message = plaintextBigints(plaintext);
|
|
3423
|
-
const ciphertext = encryptFields({ mimc7, poseidon, key, nonce, plaintext: message });
|
|
3424
|
-
const tag = outputNoteTag({ mimc7, poseidon, key, nonce, plaintext: message });
|
|
3927
|
+
async function derivedEscrowKey(nonce, recipientHi, recipientLo) {
|
|
3928
|
+
const wasm = await loadWasm();
|
|
3929
|
+
const out = wasm.derivedEscrowKey(nonce.toString(), recipientHi.toString(), recipientLo.toString());
|
|
3930
|
+
const scalar = BigInt(`0x${out.scalarHex}`);
|
|
3931
|
+
assertCanonicalBabyJubScalar(scalar);
|
|
3425
3932
|
return {
|
|
3426
|
-
|
|
3427
|
-
|
|
3933
|
+
scalarHex: hexFromCanonicalScalar(scalar),
|
|
3934
|
+
pointXHex: out.pointXHex,
|
|
3935
|
+
pointYHex: out.pointYHex,
|
|
3428
3936
|
};
|
|
3429
3937
|
}
|
|
3430
|
-
function
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
const appCommitment = poseidonHash(parameters.poseidon, [
|
|
3444
|
-
bigintFromDecimal(parameters.plaintext.applicationId),
|
|
3445
|
-
balance,
|
|
3446
|
-
precommitment,
|
|
3447
|
-
]);
|
|
3448
|
-
const commitment = poseidonHash(parameters.poseidon, [
|
|
3449
|
-
bigintFromDecimal(parameters.plaintext.nullifier),
|
|
3450
|
-
appCommitment,
|
|
3451
|
-
]);
|
|
3452
|
-
return normalizeHex(commitment.toString(16));
|
|
3453
|
-
}
|
|
3454
|
-
async function decryptOutputNoteEvent(input) {
|
|
3455
|
-
const { babyJub, mimc7, poseidon } = await primitives();
|
|
3456
|
-
const [key, nonce] = sharedKeyFromRecipient({
|
|
3457
|
-
babyJub,
|
|
3458
|
-
recipientScalarHex: input.recipientScalarHex,
|
|
3459
|
-
ephemeralPublicKey: input.createdEphemeralKey,
|
|
3460
|
-
});
|
|
3461
|
-
const decrypted = decryptFields({
|
|
3462
|
-
mimc7,
|
|
3463
|
-
poseidon,
|
|
3464
|
-
key,
|
|
3465
|
-
nonce,
|
|
3466
|
-
ciphertext: input.ciphertext,
|
|
3467
|
-
});
|
|
3468
|
-
const expectedTag = outputNoteTag({
|
|
3469
|
-
mimc7,
|
|
3470
|
-
poseidon,
|
|
3471
|
-
key,
|
|
3472
|
-
nonce,
|
|
3473
|
-
plaintext: decrypted,
|
|
3474
|
-
});
|
|
3475
|
-
if (expectedTag !== bigintFromHex(input.tag)) {
|
|
3476
|
-
throw new Error('Output note tag verification failed');
|
|
3477
|
-
}
|
|
3478
|
-
const plaintext = plaintextFromBigints(decrypted);
|
|
3479
|
-
const sharedSecret = {
|
|
3480
|
-
x: bigintToFrHex(key),
|
|
3481
|
-
y: bigintToFrHex(nonce),
|
|
3482
|
-
};
|
|
3483
|
-
const commitmentHex = commitmentFromPlaintextAndSharedSecret({
|
|
3484
|
-
poseidon,
|
|
3485
|
-
plaintext,
|
|
3486
|
-
sharedSecret,
|
|
3487
|
-
});
|
|
3488
|
-
const commitmentMatches = normalizeHex(input.commitmentHashHex) === commitmentHex;
|
|
3489
|
-
if (!commitmentMatches) {
|
|
3490
|
-
throw new Error('Output note commitment checksum mismatch');
|
|
3938
|
+
async function sampleDerivedEscrowKey(recipientHi, recipientLo, startNonce = 1n, window = 1024n) {
|
|
3939
|
+
for (let offset = 0n; offset < window; offset += 1n) {
|
|
3940
|
+
const nonce = startNonce + offset;
|
|
3941
|
+
try {
|
|
3942
|
+
const key = await derivedEscrowKey(nonce, recipientHi, recipientLo);
|
|
3943
|
+
return { nonce, key };
|
|
3944
|
+
}
|
|
3945
|
+
catch (error) {
|
|
3946
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3947
|
+
if (!message.includes('outside BabyJub subgroup')) {
|
|
3948
|
+
throw error;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3491
3951
|
}
|
|
3492
|
-
|
|
3493
|
-
...plaintext,
|
|
3494
|
-
commitmentHex,
|
|
3495
|
-
commitmentMatches,
|
|
3496
|
-
};
|
|
3952
|
+
throw new Error(OUT_OF_RANGE_MESSAGE);
|
|
3497
3953
|
}
|
|
3498
3954
|
|
|
3499
|
-
export { BN254_BABYJUB_SCALAR_MAX_EXCLUSIVE, BN254_SCALAR_MOD, COIN_VALUE_STROOPS, DECODED_EPHEMERAL_HRP, DEFAULT_APPLICATION_ID, DEFAULT_STEALTH_SIGN_NONCE, DEMO_AUDIT_PUBLIC_KEY, DEPOSITOR_SHARED_SECRET_PREIMAGE_HRP, NOTE_AUDIT_LEN as KYT_NOTE_AUDIT_LEN, NOTE_AUDIT_LEN$
|
|
3955
|
+
export { BABYJUB_SUBGROUP_ORDER, BINDING_ZK_NONCE, BN254_BABYJUB_SCALAR_MAX_EXCLUSIVE, BN254_SCALAR_MOD, BundledZkCircuit, COIN_VALUE_STROOPS, COMMITMENT_V2_ZK_NONCE, DECODED_EPHEMERAL_HRP, DEFAULT_APPLICATION_ID, DEFAULT_STEALTH_SIGN_NONCE, DEFAULT_ZK_CIRCUITS, DEFAULT_ZK_CONFIG_NONCE, DEMO_AUDIT_PUBLIC_KEY, DEPOSITOR_SHARED_SECRET_PREIMAGE_HRP, DOM_ENC, DOM_ESCROW, DOM_NULLIFIER, DOM_PADDING, NOTE_AUDIT_LEN as KYT_NOTE_AUDIT_LEN, LEGACY_ZK_NONCE, NOTE_AUDIT_LEN$2 as NOTE_AUDIT_LEN, NOTE_AUDIT_PLAINTEXT_LEN, NOTE_OUTPUT_LEN$1 as NOTE_OUTPUT_LEN, NOTE_OUTPUT_LEN as OUTPUT_NOTE_LEN, OWNER_BOUND_NOTE_SCHEMA_VERSION, POOL_MERKLE_TREE_DEPTH, PrivacyPoolSDK, SIX_BY_SIX_BINDING_ZK_NONCE, SIX_BY_SIX_ZK_NONCE, SPEND_SCALAR_DOMAIN_TAG, STEALTH_ADDRESS_HRP, TOTAL_PUBLIC_SIGNALS, TRANSACTION_N_AUDIT_SLOTS, TRANSACTION_N_INS, TRANSACTION_N_OUTS, TRANSACTION_TREE_DEPTH, ZK_ARTIFACT_GITHUB_REPO, ZK_SDK_PACKAGE_VERSION, addressToCanonicalXdr, auditEphemeralPublicKeyCount, buildKytPassageAuthorization, buildStealthAddressSignMessage, buildTransactionWitnessInput, buildUniformAuditParams, bundledCircuitFileNames, canonicalBabyJubScalarFromInteger, ciphertextFieldCount, ciphertextsInPublicSignals, circuitProfileFromName, circuitProfileFromNonce, coordHexToDecimal, decodeCiphertextBlob, decodeDecodedEphemeralKey, decodeDepositorSharedSecretPreimage, decodeStealthAddress, decryptOutputNoteEvent, defaultZkArtifactBaseUrl, derivePassageFromTransactContext, derivePassageId, derivePassageIdV1, derivePassageIdV2, derivedEscrowKey, ed25519PubkeyPayloadHexToWithdrawFrDecimals, encodeCiphertextBlob, encodeDecodedEphemeralKey, encodeDepositorSharedSecretPreimage, encodeStealthAddress, encodeTransactionCiphertextBlob, encryptNoteAuditSlot, encryptOutputNoteForDeposit, encryptTransactionCiphertextBlob, generateRandomScalarHex32, generateWitness, hashCiphertextBytes, hashPublicLegContext, hashPublicSignalBytes, indexAuditTags, indexOutputNoteTags, inspectKytPassage, layoutForKnownNonce, layoutFromCircuitConfig, localSignKytPassageAuthorization, padDepositSlots, padPublicLegs, padWithdrawSlots, planKytSubmit, poseidonMacTag, privKeyScalarDecimalFromStellarSignature, randomFrDecimal, randomFrDecimal253, recipientPublicKeysDecimalFromStealthAddress, registerKytPassage, resolveAuditPublicKeyFromEnv, resolveDepositsForWitness, resolveSlotApplicationIds, resolveSpendScalarSchemaVersion, resolveTransactionAuditParams, resolveZkCircuitConfig, sampleDerivedEscrowKey, scalarHexToFrDecimal, secretFromDepositEphemeralScalarDecimal, sharedSecretFromDepositorPreimage, sharedSecretFromRecipientPreimage, sixBySixBindingLayout, sixBySixLayout, spendScalarHexFromStellarSignature, spliceBindingTagsIntoPublicSignals, splitCiphertextBlob, standardBindingLayout, standardLayout, stateRootIndex, stellarContractAddressToAssetFrDecimals, submitApprovedKytPassage, submitPoolTransact, submitWithKytPassage, totalPublicSignals, withApplicationIdOnDeposit, withApplicationIdOnWithdraw, withdrawObjectFromMerkleWitness, zeroAddressSentinelBuffer, zeroBindingTagsInPublicSignals };
|
|
3500
3956
|
//# sourceMappingURL=index.mjs.map
|