@auditable/privacy-pool-zk-sdk 0.1.0 → 0.6.1-rc.5
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/assets/main.wasm +0 -0
- package/assets/main_final.zkey +0 -0
- package/dist/cli.js +511 -101
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.mjs +1600 -127
- package/dist/index.mjs.map +1 -1
- package/dist/kyt-flow.d.ts +54 -0
- package/dist/kyt-onboarding.d.ts +36 -0
- package/dist/kyt-passage.d.ts +71 -0
- package/dist/output-note-encryption.d.ts +42 -0
- package/dist/sdk.d.ts +18 -6
- package/dist/transaction-audit.d.ts +25 -0
- package/dist/types.d.ts +9 -0
- package/dist/withdrawal-transaction-input.d.ts +60 -4
- package/package.json +8 -2
- package/pkg/client_sdk_wasm.d.ts +10 -9
- package/pkg/client_sdk_wasm.js +58 -82
- package/pkg/client_sdk_wasm_bg.wasm +0 -0
- package/pkg/client_sdk_wasm_bg.wasm.d.ts +4 -4
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var fs = require('fs');
|
|
5
|
+
var stellarSdk = require('@stellar/stellar-sdk');
|
|
5
6
|
var snarkjs = require('snarkjs');
|
|
6
7
|
|
|
7
8
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -397,7 +398,68 @@ function decodeDepositorSharedSecretPreimage(encoded) {
|
|
|
397
398
|
};
|
|
398
399
|
}
|
|
399
400
|
|
|
400
|
-
|
|
401
|
+
const DEFAULT_APPLICATION_ID = '101';
|
|
402
|
+
/** BabyJub audit public key (decimal Fr) used in BDD / local demo when env is unset. */
|
|
403
|
+
const DEMO_AUDIT_PUBLIC_KEY = [
|
|
404
|
+
'21605515851820432880964235241069234202284600780825340516808373216881770219365',
|
|
405
|
+
'18856460861531942120859708048677603751294231190189224157283439874962410808705',
|
|
406
|
+
];
|
|
407
|
+
function isActiveWithdraw(slot) {
|
|
408
|
+
return slot.value !== '0';
|
|
409
|
+
}
|
|
410
|
+
function isActiveDeposit(slot) {
|
|
411
|
+
return slot.value !== '0';
|
|
412
|
+
}
|
|
413
|
+
function resolveWithdrawSlot(slot) {
|
|
414
|
+
if (slot === 'dummy') {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
return slot;
|
|
418
|
+
}
|
|
419
|
+
function resolveDepositSlot(slot) {
|
|
420
|
+
if (slot === 'dummy') {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return slot;
|
|
424
|
+
}
|
|
425
|
+
function buildUniformAuditParams(applicationId = DEFAULT_APPLICATION_ID, auditPublicKey) {
|
|
426
|
+
return {
|
|
427
|
+
applicationId,
|
|
428
|
+
noteAuditPublicKeys: [
|
|
429
|
+
auditPublicKey,
|
|
430
|
+
auditPublicKey,
|
|
431
|
+
auditPublicKey,
|
|
432
|
+
auditPublicKey,
|
|
433
|
+
],
|
|
434
|
+
auditEphemeralScalars: [
|
|
435
|
+
randomFrDecimal253(),
|
|
436
|
+
randomFrDecimal253(),
|
|
437
|
+
randomFrDecimal253(),
|
|
438
|
+
randomFrDecimal253(),
|
|
439
|
+
],
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function resolveSlotApplicationIds(audit, withdrawSlots, depositSlots) {
|
|
443
|
+
const w0 = resolveWithdrawSlot(withdrawSlots[0]);
|
|
444
|
+
const w1 = resolveWithdrawSlot(withdrawSlots[1]);
|
|
445
|
+
const d0 = resolveDepositSlot(depositSlots[0]);
|
|
446
|
+
const d1 = resolveDepositSlot(depositSlots[1]);
|
|
447
|
+
return {
|
|
448
|
+
inputApplicationIds: [
|
|
449
|
+
w0 && isActiveWithdraw(w0) ? audit.applicationId : '0',
|
|
450
|
+
w1 && isActiveWithdraw(w1) ? audit.applicationId : '0',
|
|
451
|
+
],
|
|
452
|
+
outputApplicationIds: [
|
|
453
|
+
d0 && isActiveDeposit(d0) ? audit.applicationId : '0',
|
|
454
|
+
d1 && isActiveDeposit(d1) ? audit.applicationId : '0',
|
|
455
|
+
],
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function resolveTransactionAuditParams(applicationId, auditPublicKey) {
|
|
459
|
+
return buildUniformAuditParams(applicationId, auditPublicKey ?? DEMO_AUDIT_PUBLIC_KEY);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Matches `Transaction(20, 2, 2, publicNInputs, publicNOutputs, 4, 9)` in `circuits/main.circom`. */
|
|
401
463
|
const TRANSACTION_TREE_DEPTH = 20;
|
|
402
464
|
/** BN254 scalar field modulus (ark `Fr`, circom signals). */
|
|
403
465
|
const BN254_SCALAR_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
@@ -447,6 +509,15 @@ function ed25519PubkeyPayloadHexToWithdrawFrDecimals(hex) {
|
|
|
447
509
|
const lo = BigInt(`0x${h.slice(32, 64)}`);
|
|
448
510
|
return { hi: hi.toString(10), lo: lo.toString(10) };
|
|
449
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Stellar contract id (`C…`) → two circom field decimals for `asset[0]`, `asset[1]` (same 32-byte split as accounts).
|
|
514
|
+
*/
|
|
515
|
+
function stellarContractAddressToAssetFrDecimals(address) {
|
|
516
|
+
const raw = stellarSdk.StrKey.decodeContract(address);
|
|
517
|
+
const hex = Buffer.from(raw).toString('hex');
|
|
518
|
+
const { hi, lo } = ed25519PubkeyPayloadHexToWithdrawFrDecimals(hex);
|
|
519
|
+
return [hi, lo];
|
|
520
|
+
}
|
|
450
521
|
/** Uniform random `Fr` as decimal (32 random bytes, mod r). For Poseidon-only inputs (e.g. nullifiers). */
|
|
451
522
|
function randomFrDecimal() {
|
|
452
523
|
const hex = generateRandomScalarHex32();
|
|
@@ -470,6 +541,8 @@ function dummyWithdraw(wasm) {
|
|
|
470
541
|
value: '0',
|
|
471
542
|
nullifier,
|
|
472
543
|
secret,
|
|
544
|
+
asset: ['0', '0'],
|
|
545
|
+
applicationId: '0',
|
|
473
546
|
ephemeralKeys: [coordHexToDecimal(pt.x), coordHexToDecimal(pt.y)],
|
|
474
547
|
stateSiblings: zerosTreeSiblings(),
|
|
475
548
|
stateIndex: '0',
|
|
@@ -484,6 +557,8 @@ function dummyDeposit(wasm) {
|
|
|
484
557
|
value: '0',
|
|
485
558
|
nullifier,
|
|
486
559
|
ephemeralKeyScalar,
|
|
560
|
+
asset: ['0', '0'],
|
|
561
|
+
applicationId: '0',
|
|
487
562
|
recipientPublicKeys: [coordHexToDecimal(pt.x), coordHexToDecimal(pt.y)],
|
|
488
563
|
};
|
|
489
564
|
}
|
|
@@ -493,11 +568,12 @@ function resolveWithdraw(slot, wasm) {
|
|
|
493
568
|
function resolveDeposit(slot, wasm) {
|
|
494
569
|
return slot === 'dummy' ? dummyDeposit(wasm) : slot;
|
|
495
570
|
}
|
|
496
|
-
function buildTransactionWitnessInput(publicParams, withdrawSlots, depositSlots, wasm) {
|
|
571
|
+
function buildTransactionWitnessInput(publicParams, publicLegs, withdrawSlots, depositSlots, audit, wasm) {
|
|
497
572
|
const w0 = resolveWithdraw(withdrawSlots[0], wasm);
|
|
498
573
|
const w1 = resolveWithdraw(withdrawSlots[1], wasm);
|
|
499
574
|
const d0 = resolveDeposit(depositSlots[0], wasm);
|
|
500
575
|
const d1 = resolveDeposit(depositSlots[1], wasm);
|
|
576
|
+
const appIds = resolveSlotApplicationIds(audit, withdrawSlots, depositSlots);
|
|
501
577
|
return {
|
|
502
578
|
stateRoot: publicParams.stateRoot,
|
|
503
579
|
withdrawAddressHi: publicParams.withdrawAddressHi,
|
|
@@ -506,13 +582,23 @@ function buildTransactionWitnessInput(publicParams, withdrawSlots, depositSlots,
|
|
|
506
582
|
withdrawnValues: [w0.value, w1.value],
|
|
507
583
|
withdrawnNullifiers: [w0.nullifier, w1.nullifier],
|
|
508
584
|
withdrawnSecrets: [w0.secret, w1.secret],
|
|
585
|
+
withdrawnAssets: [w0.asset, w1.asset],
|
|
509
586
|
ephemeralKeys: [w0.ephemeralKeys, w1.ephemeralKeys],
|
|
510
587
|
stateSiblings: [w0.stateSiblings, w1.stateSiblings],
|
|
511
588
|
stateIndex: [w0.stateIndex, w1.stateIndex],
|
|
512
589
|
depositedValues: [d0.value, d1.value],
|
|
513
590
|
depositedNullifiers: [d0.nullifier, d1.nullifier],
|
|
591
|
+
depositedAssets: [d0.asset, d1.asset],
|
|
514
592
|
depositedEphemeralKeyScalars: [d0.ephemeralKeyScalar, d1.ephemeralKeyScalar],
|
|
515
593
|
depositedRecipientPublicKeys: [d0.recipientPublicKeys, d1.recipientPublicKeys],
|
|
594
|
+
inputApplicationIds: appIds.inputApplicationIds,
|
|
595
|
+
outputApplicationIds: appIds.outputApplicationIds,
|
|
596
|
+
auditEphemeralScalars: audit.auditEphemeralScalars,
|
|
597
|
+
noteAuditPublicKeys: audit.noteAuditPublicKeys,
|
|
598
|
+
publicWithdrawnAssets: publicLegs.publicWithdrawnAssets,
|
|
599
|
+
publicDepositedAssets: publicLegs.publicDepositedAssets,
|
|
600
|
+
publicDeposits: publicLegs.publicDeposits,
|
|
601
|
+
publicWithdrawals: publicLegs.publicWithdrawals,
|
|
516
602
|
};
|
|
517
603
|
}
|
|
518
604
|
/** `stpl1…` stealth address → `[x, y]` as decimal field strings for `depositedRecipientPublicKeys`. */
|
|
@@ -521,11 +607,13 @@ function recipientPublicKeysDecimalFromStealthAddress(stealthAddress) {
|
|
|
521
607
|
return [coordHexToDecimal(x), coordHexToDecimal(y)];
|
|
522
608
|
}
|
|
523
609
|
/** First withdraw leg: Merkle witness + depositor ECDH point coordinates (hex). */
|
|
524
|
-
function withdrawObjectFromMerkleWitness(witness, depositorEphemeralHex) {
|
|
610
|
+
function withdrawObjectFromMerkleWitness(witness, depositorEphemeralHex, applicationId) {
|
|
525
611
|
return {
|
|
526
612
|
value: witness.value,
|
|
527
613
|
nullifier: witness.nullifier,
|
|
528
614
|
secret: witness.secret,
|
|
615
|
+
asset: witness.withdrawnAsset,
|
|
616
|
+
applicationId,
|
|
529
617
|
ephemeralKeys: [
|
|
530
618
|
coordHexToDecimal(depositorEphemeralHex.x),
|
|
531
619
|
coordHexToDecimal(depositorEphemeralHex.y),
|
|
@@ -766,13 +854,34 @@ function ecdhSharedKey(priv_hex, pub_x_hex, pub_y_hex) {
|
|
|
766
854
|
/**
|
|
767
855
|
* Generate a new coin with random nullifier, secret, and shared-secret field elements.
|
|
768
856
|
* `amount` is stroops (u64); JS passes `bigint`.
|
|
769
|
-
*
|
|
857
|
+
* `asset_hi_decimal` / `asset_lo_decimal` are decimal Fr strings for the Stellar asset contract id (two limbs).
|
|
858
|
+
* Returns JSON: { coin: { value, nullifier, secret, commitment, asset_hi, asset_lo }, commitment_hex, precommitement_hex }
|
|
770
859
|
* @param {bigint} amount
|
|
860
|
+
* @param {string} asset_hi_decimal
|
|
861
|
+
* @param {string} asset_lo_decimal
|
|
862
|
+
* @param {string} application_id_decimal
|
|
771
863
|
* @returns {any}
|
|
772
864
|
*/
|
|
773
|
-
function generateCoin(amount) {
|
|
774
|
-
|
|
775
|
-
|
|
865
|
+
function generateCoin(amount, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
866
|
+
try {
|
|
867
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
868
|
+
const ptr0 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
869
|
+
const len0 = WASM_VECTOR_LEN;
|
|
870
|
+
const ptr1 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
871
|
+
const len1 = WASM_VECTOR_LEN;
|
|
872
|
+
const ptr2 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
873
|
+
const len2 = WASM_VECTOR_LEN;
|
|
874
|
+
wasm.generateCoin(retptr, amount, ptr0, len0, ptr1, len1, ptr2, len2);
|
|
875
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
876
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
877
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
878
|
+
if (r2) {
|
|
879
|
+
throw takeObject(r1);
|
|
880
|
+
}
|
|
881
|
+
return takeObject(r0);
|
|
882
|
+
} finally {
|
|
883
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
884
|
+
}
|
|
776
885
|
}
|
|
777
886
|
|
|
778
887
|
/**
|
|
@@ -781,9 +890,12 @@ function generateCoin(amount) {
|
|
|
781
890
|
* @param {string} shared_x_hex
|
|
782
891
|
* @param {string} shared_y_hex
|
|
783
892
|
* @param {bigint} amount
|
|
893
|
+
* @param {string} asset_hi_decimal
|
|
894
|
+
* @param {string} asset_lo_decimal
|
|
895
|
+
* @param {string} application_id_decimal
|
|
784
896
|
* @returns {any}
|
|
785
897
|
*/
|
|
786
|
-
function generateCoinForDepositWithSharedHex(scalar_hex, shared_x_hex, shared_y_hex, amount) {
|
|
898
|
+
function generateCoinForDepositWithSharedHex(scalar_hex, shared_x_hex, shared_y_hex, amount, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
787
899
|
try {
|
|
788
900
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
789
901
|
const ptr0 = passStringToWasm0(scalar_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
@@ -792,7 +904,13 @@ function generateCoinForDepositWithSharedHex(scalar_hex, shared_x_hex, shared_y_
|
|
|
792
904
|
const len1 = WASM_VECTOR_LEN;
|
|
793
905
|
const ptr2 = passStringToWasm0(shared_y_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
794
906
|
const len2 = WASM_VECTOR_LEN;
|
|
795
|
-
|
|
907
|
+
const ptr3 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
908
|
+
const len3 = WASM_VECTOR_LEN;
|
|
909
|
+
const ptr4 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
910
|
+
const len4 = WASM_VECTOR_LEN;
|
|
911
|
+
const ptr5 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
912
|
+
const len5 = WASM_VECTOR_LEN;
|
|
913
|
+
wasm.generateCoinForDepositWithSharedHex(retptr, ptr0, len0, ptr1, len1, ptr2, len2, amount, ptr3, len3, ptr4, len4, ptr5, len5);
|
|
796
914
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
797
915
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
798
916
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -809,14 +927,23 @@ function generateCoinForDepositWithSharedHex(scalar_hex, shared_x_hex, shared_y_
|
|
|
809
927
|
* `secret` in coin = `Poseidon255(1)(scalar)` per `deposit.circom`; scalar is 32-byte hex (64 chars, optional `0x`).
|
|
810
928
|
* @param {string} scalar_hex
|
|
811
929
|
* @param {bigint} amount
|
|
930
|
+
* @param {string} asset_hi_decimal
|
|
931
|
+
* @param {string} asset_lo_decimal
|
|
932
|
+
* @param {string} application_id_decimal
|
|
812
933
|
* @returns {any}
|
|
813
934
|
*/
|
|
814
|
-
function generateCoinFromDepositEphemeralScalarHex(scalar_hex, amount) {
|
|
935
|
+
function generateCoinFromDepositEphemeralScalarHex(scalar_hex, amount, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
815
936
|
try {
|
|
816
937
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
817
938
|
const ptr0 = passStringToWasm0(scalar_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
818
939
|
const len0 = WASM_VECTOR_LEN;
|
|
819
|
-
|
|
940
|
+
const ptr1 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
941
|
+
const len1 = WASM_VECTOR_LEN;
|
|
942
|
+
const ptr2 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
943
|
+
const len2 = WASM_VECTOR_LEN;
|
|
944
|
+
const ptr3 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
945
|
+
const len3 = WASM_VECTOR_LEN;
|
|
946
|
+
wasm.generateCoinFromDepositEphemeralScalarHex(retptr, ptr0, len0, amount, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
820
947
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
821
948
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
822
949
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -834,16 +961,25 @@ function generateCoinFromDepositEphemeralScalarHex(scalar_hex, amount) {
|
|
|
834
961
|
* @param {string} shared_x_hex
|
|
835
962
|
* @param {string} shared_y_hex
|
|
836
963
|
* @param {bigint} amount
|
|
964
|
+
* @param {string} asset_hi_decimal
|
|
965
|
+
* @param {string} asset_lo_decimal
|
|
966
|
+
* @param {string} application_id_decimal
|
|
837
967
|
* @returns {any}
|
|
838
968
|
*/
|
|
839
|
-
function generateCoinWithSharedSecretHex(shared_x_hex, shared_y_hex, amount) {
|
|
969
|
+
function generateCoinWithSharedSecretHex(shared_x_hex, shared_y_hex, amount, asset_hi_decimal, asset_lo_decimal, application_id_decimal) {
|
|
840
970
|
try {
|
|
841
971
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
842
972
|
const ptr0 = passStringToWasm0(shared_x_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
843
973
|
const len0 = WASM_VECTOR_LEN;
|
|
844
974
|
const ptr1 = passStringToWasm0(shared_y_hex, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
845
975
|
const len1 = WASM_VECTOR_LEN;
|
|
846
|
-
|
|
976
|
+
const ptr2 = passStringToWasm0(asset_hi_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
977
|
+
const len2 = WASM_VECTOR_LEN;
|
|
978
|
+
const ptr3 = passStringToWasm0(asset_lo_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
979
|
+
const len3 = WASM_VECTOR_LEN;
|
|
980
|
+
const ptr4 = passStringToWasm0(application_id_decimal, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
981
|
+
const len4 = WASM_VECTOR_LEN;
|
|
982
|
+
wasm.generateCoinWithSharedSecretHex(retptr, ptr0, len0, ptr1, len1, amount, ptr2, len2, ptr3, len3, ptr4, len4);
|
|
847
983
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
848
984
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
849
985
|
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
@@ -914,13 +1050,6 @@ function __wbg_get_imports() {
|
|
|
914
1050
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
915
1051
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
916
1052
|
},
|
|
917
|
-
__wbg___wbindgen_debug_string_5398f5bb970e0daa: function(arg0, arg1) {
|
|
918
|
-
const ret = debugString(getObject(arg1));
|
|
919
|
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
920
|
-
const len1 = WASM_VECTOR_LEN;
|
|
921
|
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
922
|
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
923
|
-
},
|
|
924
1053
|
__wbg___wbindgen_is_function_3c846841762788c1: function(arg0) {
|
|
925
1054
|
const ret = typeof(getObject(arg0)) === 'function';
|
|
926
1055
|
return ret;
|
|
@@ -1046,71 +1175,6 @@ function addHeapObject(obj) {
|
|
|
1046
1175
|
return idx;
|
|
1047
1176
|
}
|
|
1048
1177
|
|
|
1049
|
-
function debugString(val) {
|
|
1050
|
-
// primitive types
|
|
1051
|
-
const type = typeof val;
|
|
1052
|
-
if (type == 'number' || type == 'boolean' || val == null) {
|
|
1053
|
-
return `${val}`;
|
|
1054
|
-
}
|
|
1055
|
-
if (type == 'string') {
|
|
1056
|
-
return `"${val}"`;
|
|
1057
|
-
}
|
|
1058
|
-
if (type == 'symbol') {
|
|
1059
|
-
const description = val.description;
|
|
1060
|
-
if (description == null) {
|
|
1061
|
-
return 'Symbol';
|
|
1062
|
-
} else {
|
|
1063
|
-
return `Symbol(${description})`;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
if (type == 'function') {
|
|
1067
|
-
const name = val.name;
|
|
1068
|
-
if (typeof name == 'string' && name.length > 0) {
|
|
1069
|
-
return `Function(${name})`;
|
|
1070
|
-
} else {
|
|
1071
|
-
return 'Function';
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
// objects
|
|
1075
|
-
if (Array.isArray(val)) {
|
|
1076
|
-
const length = val.length;
|
|
1077
|
-
let debug = '[';
|
|
1078
|
-
if (length > 0) {
|
|
1079
|
-
debug += debugString(val[0]);
|
|
1080
|
-
}
|
|
1081
|
-
for(let i = 1; i < length; i++) {
|
|
1082
|
-
debug += ', ' + debugString(val[i]);
|
|
1083
|
-
}
|
|
1084
|
-
debug += ']';
|
|
1085
|
-
return debug;
|
|
1086
|
-
}
|
|
1087
|
-
// Test for built-in
|
|
1088
|
-
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
|
1089
|
-
let className;
|
|
1090
|
-
if (builtInMatches && builtInMatches.length > 1) {
|
|
1091
|
-
className = builtInMatches[1];
|
|
1092
|
-
} else {
|
|
1093
|
-
// Failed to match the standard '[object ClassName]'
|
|
1094
|
-
return toString.call(val);
|
|
1095
|
-
}
|
|
1096
|
-
if (className == 'Object') {
|
|
1097
|
-
// we're a user defined class or Object
|
|
1098
|
-
// JSON.stringify avoids problems with cycles, and is generally much
|
|
1099
|
-
// easier than looping through ownProperties of `val`.
|
|
1100
|
-
try {
|
|
1101
|
-
return 'Object(' + JSON.stringify(val) + ')';
|
|
1102
|
-
} catch (_) {
|
|
1103
|
-
return 'Object';
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
// errors
|
|
1107
|
-
if (val instanceof Error) {
|
|
1108
|
-
return `${val.name}: ${val.message}\n${val.stack}`;
|
|
1109
|
-
}
|
|
1110
|
-
// TODO we could test for more things here, like `Set`s and `Map`s.
|
|
1111
|
-
return className;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
1178
|
function dropObject(idx) {
|
|
1115
1179
|
if (idx < 1028) return;
|
|
1116
1180
|
heap[idx] = heap_next;
|
|
@@ -1893,30 +1957,31 @@ class PrivacyPoolSDK {
|
|
|
1893
1957
|
/**
|
|
1894
1958
|
* Generate a new coin with random nullifier, secret, and random shared-secret field elements (dev / self-contained tests).
|
|
1895
1959
|
* @param amount Stroops encoded as `bigint` or integer `number` (WASM `u64`).
|
|
1960
|
+
* @param assetHiDecimal / assetLoDecimal Decimal Fr strings for Stellar asset contract id (two limbs).
|
|
1896
1961
|
*/
|
|
1897
|
-
generateCoin(amount) {
|
|
1898
|
-
return this.wasm.generateCoin(wasmU64Stroops(amount));
|
|
1962
|
+
generateCoin(amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
|
|
1963
|
+
return this.wasm.generateCoin(wasmU64Stroops(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal);
|
|
1899
1964
|
}
|
|
1900
1965
|
/**
|
|
1901
1966
|
* Generate a coin with the same commitment shape as on-chain deposit: pass `ecdhSharedKey` output (hex x, y).
|
|
1902
1967
|
* @param amount Stroops (`bigint` | `number`).
|
|
1903
1968
|
*/
|
|
1904
|
-
generateCoinWithSharedSecret(shared, amount) {
|
|
1905
|
-
return this.wasm.generateCoinWithSharedSecretHex(shared.x, shared.y, wasmU64Stroops(amount));
|
|
1969
|
+
generateCoinWithSharedSecret(shared, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
|
|
1970
|
+
return this.wasm.generateCoinWithSharedSecretHex(shared.x, shared.y, wasmU64Stroops(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal);
|
|
1906
1971
|
}
|
|
1907
1972
|
/**
|
|
1908
1973
|
* Coin for a depositor `ephemeralKeyScalar` (32-byte hex): `coin.secret = Poseidon255(1)(scalar)` as in `deposit.circom`.
|
|
1909
1974
|
* @param amount Stroops (`bigint` | `number`).
|
|
1910
1975
|
*/
|
|
1911
|
-
generateCoinFromDepositEphemeralScalarHex(scalarHex, amount) {
|
|
1912
|
-
return this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex, wasmU64Stroops(amount));
|
|
1976
|
+
generateCoinFromDepositEphemeralScalarHex(scalarHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
|
|
1977
|
+
return this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex, wasmU64Stroops(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal);
|
|
1913
1978
|
}
|
|
1914
1979
|
/**
|
|
1915
1980
|
* Aligned deposit coin: `secret = Poseidon₁(scalar)` and ECDH shared key from hex coords (e.g. `ecdhSharedKey(scalar, recipient_x, recipient_y)`).
|
|
1916
1981
|
* @param amount Stroops (`bigint` | `number`).
|
|
1917
1982
|
*/
|
|
1918
|
-
generateCoinForDepositWithSharedHex(scalarHex, sharedXHex, sharedYHex, amount) {
|
|
1919
|
-
return this.wasm.generateCoinForDepositWithSharedHex(scalarHex, sharedXHex, sharedYHex, wasmU64Stroops(amount));
|
|
1983
|
+
generateCoinForDepositWithSharedHex(scalarHex, sharedXHex, sharedYHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
|
|
1984
|
+
return this.wasm.generateCoinForDepositWithSharedHex(scalarHex, sharedXHex, sharedYHex, wasmU64Stroops(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal);
|
|
1920
1985
|
}
|
|
1921
1986
|
/**
|
|
1922
1987
|
* Merkle root, path, and coin fields for the first withdraw leg (Rust LeanIMT + Poseidon).
|
|
@@ -1929,19 +1994,57 @@ class PrivacyPoolSDK {
|
|
|
1929
1994
|
}
|
|
1930
1995
|
/**
|
|
1931
1996
|
* Full `Transaction(20,2,2)` withdrawal proof: one real withdraw + dummies, using coin/state and depositor ECDH point (hex).
|
|
1997
|
+
*
|
|
1998
|
+
* Optional **partial public withdraw**: spend the full coin commitment `coin.value` (V), send `publicWithdrawStroops` (W) to the
|
|
1999
|
+
* Stellar receiver, and re-deposit the remainder (V−W) as a new private note to `changeRecipientStealthAddress` (same circuit balance).
|
|
1932
2000
|
*/
|
|
1933
2001
|
async proveWithdrawal(coin, state, params) {
|
|
1934
2002
|
const witness = this.buildWithdrawMerkleWitness(coin, state);
|
|
1935
2003
|
const w0 = withdrawObjectFromMerkleWitness(witness, {
|
|
1936
2004
|
x: params.ephemeralXHex,
|
|
1937
2005
|
y: params.ephemeralYHex,
|
|
1938
|
-
});
|
|
2006
|
+
}, params.applicationId ?? coin.application_id ?? '0');
|
|
2007
|
+
const fullV = BigInt(coin.value);
|
|
2008
|
+
let publicWithdrawals;
|
|
2009
|
+
let deposits;
|
|
2010
|
+
if (params.publicWithdrawStroops !== undefined) {
|
|
2011
|
+
const W = params.publicWithdrawStroops;
|
|
2012
|
+
if (params.changeRecipientStealthAddress === undefined) {
|
|
2013
|
+
throw new Error('proveWithdrawal: changeRecipientStealthAddress is required when publicWithdrawStroops is set');
|
|
2014
|
+
}
|
|
2015
|
+
if (W <= 0n || W >= fullV) {
|
|
2016
|
+
throw new Error('proveWithdrawal: publicWithdrawStroops must be strictly between 0 and coin.value');
|
|
2017
|
+
}
|
|
2018
|
+
const change = fullV - W;
|
|
2019
|
+
publicWithdrawals = [W.toString()];
|
|
2020
|
+
const changeDeposit = {
|
|
2021
|
+
value: change.toString(),
|
|
2022
|
+
nullifier: randomFrDecimal(),
|
|
2023
|
+
ephemeralKeyScalar: randomFrDecimal253(),
|
|
2024
|
+
asset: [coin.asset_hi, coin.asset_lo],
|
|
2025
|
+
applicationId: params.applicationId ?? '0',
|
|
2026
|
+
recipientPublicKeys: recipientPublicKeysDecimalFromStealthAddress(params.changeRecipientStealthAddress),
|
|
2027
|
+
};
|
|
2028
|
+
deposits = [changeDeposit, 'dummy'];
|
|
2029
|
+
}
|
|
2030
|
+
else {
|
|
2031
|
+
publicWithdrawals = [coin.value];
|
|
2032
|
+
deposits = ['dummy', 'dummy'];
|
|
2033
|
+
}
|
|
2034
|
+
// On-chain `transact` sends `publicWithdrawals` / `publicWithdrawnAssets` to the withdraw account.
|
|
2035
|
+
const publicLegs = {
|
|
2036
|
+
publicWithdrawnAssets: [[coin.asset_hi, coin.asset_lo]],
|
|
2037
|
+
publicDepositedAssets: [['0', '0']],
|
|
2038
|
+
publicDeposits: ['0'],
|
|
2039
|
+
publicWithdrawals,
|
|
2040
|
+
};
|
|
1939
2041
|
return this.proveTransaction({
|
|
1940
2042
|
stateRoot: witness.stateRoot,
|
|
1941
2043
|
withdrawAddressHi: params.withdrawAddressHi,
|
|
1942
2044
|
withdrawAddressLo: params.withdrawAddressLo,
|
|
1943
2045
|
privKeyScalar: params.privKeyScalar,
|
|
1944
|
-
}, [w0, 'dummy'],
|
|
2046
|
+
}, publicLegs, [w0, 'dummy'], deposits, params.audit
|
|
2047
|
+
?? resolveTransactionAuditParams(params.applicationId ?? coin.application_id ?? DEFAULT_APPLICATION_ID, DEMO_AUDIT_PUBLIC_KEY));
|
|
1945
2048
|
}
|
|
1946
2049
|
/**
|
|
1947
2050
|
* Convert a snarkjs proof JSON to hex bytes for Soroban.
|
|
@@ -1969,14 +2072,15 @@ class PrivacyPoolSDK {
|
|
|
1969
2072
|
* `Transaction(20,2,2)` proof from high-level legs: maps to witness input (incl. `"dummy"` ECDH via WASM), then Groth16 → Soroban hex.
|
|
1970
2073
|
*
|
|
1971
2074
|
* @param publicParams Public inputs: `stateRoot`, `withdrawAddressHi` / `withdrawAddressLo`, `privKeyScalar` (decimal field strings).
|
|
2075
|
+
* @param publicLegs Public token legs (`publicWithdrawnAssets`, `publicDepositedAssets`, `publicDeposits`, `publicWithdrawals`).
|
|
1972
2076
|
* @param withdraws Exactly two withdraw slots (`WithdrawObject` or `"dummy"`).
|
|
1973
2077
|
* @param deposits Exactly two deposit slots (`DepositObject` or `"dummy"`).
|
|
1974
2078
|
*/
|
|
1975
|
-
async proveTransaction(publicParams, withdraws, deposits) {
|
|
2079
|
+
async proveTransaction(publicParams, publicLegs, withdraws, deposits, audit) {
|
|
1976
2080
|
const wasmEcdh = {
|
|
1977
2081
|
ecdhEphemeralPublicKeyFromScalarHex: (h) => this.wasm.ecdhEphemeralPublicKeyFromScalarHex(h),
|
|
1978
2082
|
};
|
|
1979
|
-
const witnessInput = buildTransactionWitnessInput(publicParams, withdraws, deposits, wasmEcdh);
|
|
2083
|
+
const witnessInput = buildTransactionWitnessInput(publicParams, publicLegs, withdraws, deposits, audit, wasmEcdh);
|
|
1980
2084
|
const wtns = await generateWitness(witnessInput, this.options.circuitWasm);
|
|
1981
2085
|
const { proof, publicSignals } = await generateProof(wtns, this.options.zkey);
|
|
1982
2086
|
return {
|
|
@@ -2019,10 +2123,191 @@ class PrivacyPoolSDK {
|
|
|
2019
2123
|
}
|
|
2020
2124
|
}
|
|
2021
2125
|
|
|
2126
|
+
function onboardingContractValue(onboarding) {
|
|
2127
|
+
return {
|
|
2128
|
+
owner: onboarding.owner,
|
|
2129
|
+
temp_public_key_x: onboarding.temp_public_key_x,
|
|
2130
|
+
temp_public_key_y: onboarding.temp_public_key_y,
|
|
2131
|
+
encrypted_private_key: onboarding.encrypted_private_key,
|
|
2132
|
+
notes: {
|
|
2133
|
+
notes: onboarding.notes.map((note) => ({
|
|
2134
|
+
value: note.value,
|
|
2135
|
+
asset_hi: note.asset_hi,
|
|
2136
|
+
asset_lo: note.asset_lo,
|
|
2137
|
+
nullifier: note.nullifier,
|
|
2138
|
+
secret: note.secret,
|
|
2139
|
+
deposited_ephemeral_scalar: note.deposited_ephemeral_scalar,
|
|
2140
|
+
})),
|
|
2141
|
+
},
|
|
2142
|
+
private_address_registration: onboarding.private_address_registration,
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
function onboardingToScVal(onboarding) {
|
|
2146
|
+
return stellarSdk.nativeToScVal(onboardingContractValue(onboarding));
|
|
2147
|
+
}
|
|
2148
|
+
function onboardingOptionToScVal(onboarding) {
|
|
2149
|
+
if (onboarding === null || onboarding === undefined) {
|
|
2150
|
+
return stellarSdk.xdr.ScVal.scvVoid();
|
|
2151
|
+
}
|
|
2152
|
+
return onboardingToScVal(onboarding);
|
|
2153
|
+
}
|
|
2154
|
+
function addressToScVal(address) {
|
|
2155
|
+
return stellarSdk.nativeToScVal(address, { type: 'address' });
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
function signatureBase64ToBytes(signature) {
|
|
2159
|
+
const value = signature.trim();
|
|
2160
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(value)) {
|
|
2161
|
+
return Buffer.from(value.replace(/^0x/i, ''), 'hex');
|
|
2162
|
+
}
|
|
2163
|
+
return Buffer.from(value, 'base64');
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
function passageIdHexToScVal(passageIdHex) {
|
|
2167
|
+
const bytes = Buffer.from(passageIdHex.replace(/^0x/, ''), 'hex');
|
|
2168
|
+
return stellarSdk.xdr.ScVal.scvBytes(bytes);
|
|
2169
|
+
}
|
|
2170
|
+
function signatureToScVal(signature) {
|
|
2171
|
+
return stellarSdk.xdr.ScVal.scvBytes(signature);
|
|
2172
|
+
}
|
|
2173
|
+
function helperSubmitWithPassageOperation(helperContractId, params, passageId, expiresAtLedger, signature) {
|
|
2174
|
+
return stellarSdk.Operation.invokeContractFunction({
|
|
2175
|
+
contract: helperContractId,
|
|
2176
|
+
function: 'submit_with_passage',
|
|
2177
|
+
args: [
|
|
2178
|
+
addressToScVal(params.owner),
|
|
2179
|
+
stellarSdk.xdr.ScVal.scvBytes(params.proofBytes),
|
|
2180
|
+
stellarSdk.xdr.ScVal.scvBytes(params.publicSignalsBytes),
|
|
2181
|
+
onboardingOptionToScVal(params.onboarding),
|
|
2182
|
+
passageIdHexToScVal(passageId),
|
|
2183
|
+
stellarSdk.xdr.ScVal.scvU32(expiresAtLedger),
|
|
2184
|
+
signatureToScVal(signatureBase64ToBytes(signature)),
|
|
2185
|
+
],
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
async function submitTransaction(params) {
|
|
2189
|
+
const server = new stellarSdk.rpc.Server(params.rpcUrl);
|
|
2190
|
+
const sourceAccount = await server.getAccount(params.source.publicKey());
|
|
2191
|
+
let builder = new stellarSdk.TransactionBuilder(sourceAccount, {
|
|
2192
|
+
fee: params.fee,
|
|
2193
|
+
networkPassphrase: params.networkPassphrase,
|
|
2194
|
+
});
|
|
2195
|
+
for (const operation of params.operations) {
|
|
2196
|
+
builder = builder.addOperation(operation);
|
|
2197
|
+
}
|
|
2198
|
+
const tx = builder.setTimeout(300).build();
|
|
2199
|
+
const prepared = await prepareTransactionAllowingNonRootAuth(server, params.rpcUrl, tx, params.source, params.networkPassphrase)
|
|
2200
|
+
;
|
|
2201
|
+
prepared.sign(params.source);
|
|
2202
|
+
const result = await server.sendTransaction(prepared);
|
|
2203
|
+
const status = String(result.status);
|
|
2204
|
+
if (status !== 'PENDING' && status !== 'SUCCESS') {
|
|
2205
|
+
throw new Error(`transaction submit failed: ${status}`);
|
|
2206
|
+
}
|
|
2207
|
+
await waitForTransaction(server, result.hash);
|
|
2208
|
+
return result.hash;
|
|
2209
|
+
}
|
|
2210
|
+
async function prepareTransactionAllowingNonRootAuth(server, rpcUrl, tx, source, networkPassphrase) {
|
|
2211
|
+
const recording = await simulateTransactionRecordingNonRoot(rpcUrl, tx.toXDR());
|
|
2212
|
+
const validUntilLedgerSeq = recording.latestLedger + 1000;
|
|
2213
|
+
const signedAuth = await Promise.all(recording.auth.map((entry) => {
|
|
2214
|
+
if (entry.credentials().switch()
|
|
2215
|
+
=== stellarSdk.xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount()) {
|
|
2216
|
+
return entry;
|
|
2217
|
+
}
|
|
2218
|
+
return stellarSdk.authorizeEntry(entry, source, validUntilLedgerSeq, networkPassphrase);
|
|
2219
|
+
}));
|
|
2220
|
+
const invokeOp = tx.operations[0];
|
|
2221
|
+
if (tx.operations.length !== 1 || invokeOp.type !== 'invokeHostFunction') {
|
|
2222
|
+
throw new Error('Soroban transactions must contain exactly one invokeHostFunction operation');
|
|
2223
|
+
}
|
|
2224
|
+
const sourceAccount = await server.getAccount(source.publicKey());
|
|
2225
|
+
const signedTx = new stellarSdk.TransactionBuilder(sourceAccount, {
|
|
2226
|
+
fee: tx.fee,
|
|
2227
|
+
networkPassphrase,
|
|
2228
|
+
})
|
|
2229
|
+
.addOperation(stellarSdk.Operation.invokeHostFunction({
|
|
2230
|
+
func: invokeOp.func,
|
|
2231
|
+
auth: signedAuth,
|
|
2232
|
+
source: invokeOp.source,
|
|
2233
|
+
}))
|
|
2234
|
+
.setTimeout(300)
|
|
2235
|
+
.build();
|
|
2236
|
+
const enforcedSimulation = await server.simulateTransaction(signedTx);
|
|
2237
|
+
if (stellarSdk.rpc.Api.isSimulationError(enforcedSimulation)) {
|
|
2238
|
+
throw new Error(`signed authorization simulation failed: ${enforcedSimulation.error}`);
|
|
2239
|
+
}
|
|
2240
|
+
return stellarSdk.rpc.assembleTransaction(signedTx, enforcedSimulation).build();
|
|
2241
|
+
}
|
|
2242
|
+
async function simulateTransactionRecordingNonRoot(rpcUrl, transactionXdr) {
|
|
2243
|
+
const response = await fetch(rpcUrl, {
|
|
2244
|
+
method: 'POST',
|
|
2245
|
+
headers: { 'content-type': 'application/json' },
|
|
2246
|
+
body: JSON.stringify({
|
|
2247
|
+
jsonrpc: '2.0',
|
|
2248
|
+
id: 1,
|
|
2249
|
+
method: 'simulateTransaction',
|
|
2250
|
+
params: {
|
|
2251
|
+
transaction: transactionXdr,
|
|
2252
|
+
authMode: 'record_allow_nonroot',
|
|
2253
|
+
},
|
|
2254
|
+
}),
|
|
2255
|
+
});
|
|
2256
|
+
const body = await response.json();
|
|
2257
|
+
if (!response.ok || body.error) {
|
|
2258
|
+
throw new Error(`transaction simulation failed: ${body.error?.message ?? response.statusText}`);
|
|
2259
|
+
}
|
|
2260
|
+
if (!body.result) {
|
|
2261
|
+
throw new Error('transaction simulation failed: missing RPC result');
|
|
2262
|
+
}
|
|
2263
|
+
if (body.result.error) {
|
|
2264
|
+
throw new Error(`transaction simulation failed: ${body.result.error}`);
|
|
2265
|
+
}
|
|
2266
|
+
const latestLedger = Number(body.result.latestLedger);
|
|
2267
|
+
if (!Number.isFinite(latestLedger)) {
|
|
2268
|
+
throw new Error('transaction simulation failed: missing latest ledger');
|
|
2269
|
+
}
|
|
2270
|
+
return {
|
|
2271
|
+
latestLedger,
|
|
2272
|
+
auth: (body.result.results?.[0]?.auth ?? []).map((entry) => stellarSdk.xdr.SorobanAuthorizationEntry.fromXDR(entry, 'base64')),
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
async function waitForTransaction(server, hash) {
|
|
2276
|
+
for (let attempt = 0; attempt < 60; attempt++) {
|
|
2277
|
+
const result = await server.getTransaction(hash);
|
|
2278
|
+
const status = String(result.status);
|
|
2279
|
+
if (status === 'SUCCESS') {
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (status === 'FAILED') {
|
|
2283
|
+
throw new Error(`transaction failed: ${hash}`);
|
|
2284
|
+
}
|
|
2285
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2286
|
+
}
|
|
2287
|
+
throw new Error(`transaction did not confirm in time: ${hash}`);
|
|
2288
|
+
}
|
|
2289
|
+
async function submitApprovedKytPassage(params) {
|
|
2290
|
+
if (params.source.publicKey() !== params.owner) {
|
|
2291
|
+
throw new Error('KYT submit source keypair must match pool transact owner');
|
|
2292
|
+
}
|
|
2293
|
+
return submitTransaction({
|
|
2294
|
+
source: params.source,
|
|
2295
|
+
networkPassphrase: params.networkPassphrase,
|
|
2296
|
+
rpcUrl: params.rpcUrl,
|
|
2297
|
+
fee: '12000000',
|
|
2298
|
+
operations: [
|
|
2299
|
+
helperSubmitWithPassageOperation(params.kytSubmitHelperId, params, params.passageId, params.expiresAtLedger, params.signature),
|
|
2300
|
+
],
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2022
2304
|
/** Pool Merkle tree depth (matches `coin::TREE_DEPTH` / `Transaction` circuit). */
|
|
2023
2305
|
/** Default coin value in stroops (1 XLM); matches Rust `coin::COIN_VALUE`. */
|
|
2024
2306
|
const COIN_VALUE_STROOPS = 1000000000;
|
|
2025
2307
|
|
|
2308
|
+
function resolveApplicationIdFromCli(parsed) {
|
|
2309
|
+
return parsed['application-id'] ?? process.env.APPLICATION_ID ?? DEFAULT_APPLICATION_ID;
|
|
2310
|
+
}
|
|
2026
2311
|
async function main() {
|
|
2027
2312
|
const args = process.argv.slice(2);
|
|
2028
2313
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
@@ -2051,6 +2336,9 @@ async function main() {
|
|
|
2051
2336
|
else if (command === 'priv-scalar-from-signature') {
|
|
2052
2337
|
await handlePrivScalarFromSignature(args.slice(1));
|
|
2053
2338
|
}
|
|
2339
|
+
else if (command === 'kyt-submit-approved') {
|
|
2340
|
+
await handleKytSubmitApproved(args.slice(1));
|
|
2341
|
+
}
|
|
2054
2342
|
else {
|
|
2055
2343
|
console.error(`Unknown command: ${command}`);
|
|
2056
2344
|
printUsage();
|
|
@@ -2066,6 +2354,8 @@ Commands:
|
|
|
2066
2354
|
generate Generate a new coin
|
|
2067
2355
|
--output, -o <file> Output coin to file (default: stdout)
|
|
2068
2356
|
--amount <stroops> Coin value in stroops (u64); default: 1000000000 (1 XLM)
|
|
2357
|
+
--token <C...> Stellar asset contract id (or set TOKEN_ADDRESS)
|
|
2358
|
+
--application-id <dec> Application id (decimal Fr); default: APPLICATION_ID env or 101
|
|
2069
2359
|
--scalar <hex> 32-byte depositor ephemeral scalar (64 hex, optional 0x); coin.secret = Poseidon₁(scalar) (deposit.circom)
|
|
2070
2360
|
--stealth <stpl1...> With --scalar: ECDH(scalar, recipient) shared secret + aligned commitment (requires WASM ecdhSharedKey)
|
|
2071
2361
|
|
|
@@ -2081,6 +2371,19 @@ Commands:
|
|
|
2081
2371
|
|
|
2082
2372
|
priv-scalar-from-signature Print privKeyScalar (decimal Fr) from Stellar Ed25519 signature (SHA-256(sig) mod r)
|
|
2083
2373
|
|
|
2374
|
+
kyt-submit-approved Submit helper.register_passage + pool.transact with signed non-root auth
|
|
2375
|
+
--source-secret <S...> Secret seed for the depositing/withdrawing Stellar account (or SOURCE_SECRET env)
|
|
2376
|
+
--owner <G...> Same account address passed to pool.transact as from
|
|
2377
|
+
--pool <C...> Privacy pool contract id
|
|
2378
|
+
--helper <C...> KYT submit helper contract id
|
|
2379
|
+
--proof <hex> Groth16 proof bytes
|
|
2380
|
+
--public-signals <hex> Public signals bytes
|
|
2381
|
+
--passage-id <hex> KYT passage id
|
|
2382
|
+
--expires-at-ledger <u32> Passage expiration ledger
|
|
2383
|
+
--signature <hex|base64> KYT registry signature
|
|
2384
|
+
--rpc-url <url> Soroban RPC URL
|
|
2385
|
+
--network <testnet|public|local> or --network-passphrase <string>
|
|
2386
|
+
|
|
2084
2387
|
withdraw Generate a withdrawal proof
|
|
2085
2388
|
--coin <file> Path to coin JSON file
|
|
2086
2389
|
--state <file> Path to state JSON file
|
|
@@ -2090,15 +2393,20 @@ Commands:
|
|
|
2090
2393
|
--priv-key-scalar <dec> privKeyScalar (decimal Fr)
|
|
2091
2394
|
--ephemeral-x <hex> Depositor ECDH point x (64 hex chars, optional 0x)
|
|
2092
2395
|
--ephemeral-y <hex> Depositor ECDH point y (64 hex chars, optional 0x)
|
|
2396
|
+
--public-withdraw-stroops <u64> Optional: amount to withdraw publicly (must be < coin.value). Rest stays in pool as change.
|
|
2397
|
+
--change-stealth <stpl1...> Required with --public-withdraw-stroops: stealth for the change note (same as deposit)
|
|
2398
|
+
--application-id <dec> Application id (decimal Fr); default: APPLICATION_ID env or 101
|
|
2093
2399
|
--output-proof <file> Write proof hex to file
|
|
2094
2400
|
--output-public <file> Write public signals hex to file
|
|
2095
2401
|
|
|
2096
2402
|
deposit-proof Groth16 proof for deposit-only transact (2 dummy withdraws, 1 deposit + dummy)
|
|
2097
2403
|
--state-root <dec> Current Merkle root (decimal Fr), must match pool on submit
|
|
2098
2404
|
--stealth <stpl1...> Recipient stealth address (decodes to recipient public key)
|
|
2405
|
+
--token <C...> Asset contract id (or set TOKEN_ADDRESS)
|
|
2099
2406
|
--coin <file> With --ephemeral-scalar-hex: use coin nullifier/value (aligned with generate --scalar --stealth)
|
|
2100
2407
|
--ephemeral-scalar-hex <hex> Same 32-byte hex as deposit / generate --scalar
|
|
2101
2408
|
--value <dec> Deposit amount (stroops as decimal Fr); optional if --coin (must match coin)
|
|
2409
|
+
--application-id <dec> Application id (decimal Fr); default: APPLICATION_ID env or 101
|
|
2102
2410
|
--output-proof <file> Write proof hex to file
|
|
2103
2411
|
--output-public <file> Write public signals hex to file
|
|
2104
2412
|
|
|
@@ -2106,6 +2414,39 @@ Output (withdraw, deposit-proof):
|
|
|
2106
2414
|
Prints proof_hex on first line and public_hex on second line to stdout.
|
|
2107
2415
|
`);
|
|
2108
2416
|
}
|
|
2417
|
+
function requireParsedArg(parsed, name) {
|
|
2418
|
+
const value = parsed[name]?.trim();
|
|
2419
|
+
if (!value) {
|
|
2420
|
+
console.error(`Error: --${name} is required`);
|
|
2421
|
+
process.exit(1);
|
|
2422
|
+
}
|
|
2423
|
+
return value;
|
|
2424
|
+
}
|
|
2425
|
+
function networkPassphraseFromArgs(parsed) {
|
|
2426
|
+
if (parsed['network-passphrase']) {
|
|
2427
|
+
return parsed['network-passphrase'];
|
|
2428
|
+
}
|
|
2429
|
+
const network = parsed['network'] ?? 'testnet';
|
|
2430
|
+
if (network === 'testnet') {
|
|
2431
|
+
return stellarSdk.Networks.TESTNET;
|
|
2432
|
+
}
|
|
2433
|
+
if (network === 'public') {
|
|
2434
|
+
return stellarSdk.Networks.PUBLIC;
|
|
2435
|
+
}
|
|
2436
|
+
if (network === 'local') {
|
|
2437
|
+
return 'Standalone Network ; February 2017';
|
|
2438
|
+
}
|
|
2439
|
+
console.error('Error: --network must be testnet, public, or local');
|
|
2440
|
+
process.exit(1);
|
|
2441
|
+
}
|
|
2442
|
+
function hexToBuffer(label, value) {
|
|
2443
|
+
const hex = value.trim().replace(/^0x/i, '');
|
|
2444
|
+
if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {
|
|
2445
|
+
console.error(`Error: --${label} must be even-length hex`);
|
|
2446
|
+
process.exit(1);
|
|
2447
|
+
}
|
|
2448
|
+
return Buffer.from(hex, 'hex');
|
|
2449
|
+
}
|
|
2109
2450
|
function parseArgs(args) {
|
|
2110
2451
|
const parsed = {};
|
|
2111
2452
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -2176,6 +2517,28 @@ async function handlePrivScalarFromSignature(args) {
|
|
|
2176
2517
|
function handleRandomScalar() {
|
|
2177
2518
|
console.log(PrivacyPoolSDK.generateRandomScalarHex32());
|
|
2178
2519
|
}
|
|
2520
|
+
async function handleKytSubmitApproved(args) {
|
|
2521
|
+
const parsed = parseArgs(args);
|
|
2522
|
+
const sourceSecret = parsed['source-secret']?.trim() ?? process.env.SOURCE_SECRET?.trim();
|
|
2523
|
+
if (!sourceSecret) {
|
|
2524
|
+
console.error('Error: --source-secret <S...> or SOURCE_SECRET env is required');
|
|
2525
|
+
process.exit(1);
|
|
2526
|
+
}
|
|
2527
|
+
const txHash = await submitApprovedKytPassage({
|
|
2528
|
+
source: stellarSdk.Keypair.fromSecret(sourceSecret),
|
|
2529
|
+
networkPassphrase: networkPassphraseFromArgs(parsed),
|
|
2530
|
+
rpcUrl: parsed['rpc-url'] ?? 'https://soroban-testnet.stellar.org',
|
|
2531
|
+
poolContract: requireParsedArg(parsed, 'pool'),
|
|
2532
|
+
owner: requireParsedArg(parsed, 'owner'),
|
|
2533
|
+
proofBytes: hexToBuffer('proof', requireParsedArg(parsed, 'proof')),
|
|
2534
|
+
publicSignalsBytes: hexToBuffer('public-signals', requireParsedArg(parsed, 'public-signals')),
|
|
2535
|
+
kytSubmitHelperId: requireParsedArg(parsed, 'helper'),
|
|
2536
|
+
passageId: requireParsedArg(parsed, 'passage-id'),
|
|
2537
|
+
expiresAtLedger: Number(requireParsedArg(parsed, 'expires-at-ledger')),
|
|
2538
|
+
signature: requireParsedArg(parsed, 'signature'),
|
|
2539
|
+
});
|
|
2540
|
+
console.log(txHash);
|
|
2541
|
+
}
|
|
2179
2542
|
function parseStroopsU64(label, raw, defaultStroops) {
|
|
2180
2543
|
if (raw === undefined) {
|
|
2181
2544
|
return defaultStroops;
|
|
@@ -2203,6 +2566,13 @@ async function handleGenerate(args) {
|
|
|
2203
2566
|
const scalar = parsed['scalar'];
|
|
2204
2567
|
const stealth = parsed['stealth'];
|
|
2205
2568
|
const amount = parseStroopsU64('--amount', parsed['amount'], BigInt(COIN_VALUE_STROOPS));
|
|
2569
|
+
const applicationId = resolveApplicationIdFromCli(parsed);
|
|
2570
|
+
const token = parsed['token'] ?? process.env.TOKEN_ADDRESS;
|
|
2571
|
+
if (!token) {
|
|
2572
|
+
console.error('Error: --token <C...> or TOKEN_ADDRESS is required');
|
|
2573
|
+
process.exit(1);
|
|
2574
|
+
}
|
|
2575
|
+
const [assetHi, assetLo] = stellarContractAddressToAssetFrDecimals(token);
|
|
2206
2576
|
if (stealth && !scalar) {
|
|
2207
2577
|
console.error('Error: --stealth requires --scalar');
|
|
2208
2578
|
process.exit(1);
|
|
@@ -2211,13 +2581,13 @@ async function handleGenerate(args) {
|
|
|
2211
2581
|
if (scalar && stealth) {
|
|
2212
2582
|
const { x, y } = decodeStealthAddress(stealth);
|
|
2213
2583
|
const shared = sdk.ecdhSharedKey(scalar, x, y);
|
|
2214
|
-
coin = sdk.generateCoinForDepositWithSharedHex(scalar, shared.x, shared.y, amount);
|
|
2584
|
+
coin = sdk.generateCoinForDepositWithSharedHex(scalar, shared.x, shared.y, amount, assetHi, assetLo, applicationId);
|
|
2215
2585
|
}
|
|
2216
2586
|
else if (scalar) {
|
|
2217
|
-
coin = sdk.generateCoinFromDepositEphemeralScalarHex(scalar, amount);
|
|
2587
|
+
coin = sdk.generateCoinFromDepositEphemeralScalarHex(scalar, amount, assetHi, assetLo, applicationId);
|
|
2218
2588
|
}
|
|
2219
2589
|
else {
|
|
2220
|
-
coin = sdk.generateCoin(amount);
|
|
2590
|
+
coin = sdk.generateCoin(amount, assetHi, assetLo, applicationId);
|
|
2221
2591
|
}
|
|
2222
2592
|
const json = JSON.stringify(coin, null, 2);
|
|
2223
2593
|
if (parsed['output']) {
|
|
@@ -2262,13 +2632,33 @@ async function handleWithdraw(args) {
|
|
|
2262
2632
|
const coinFile = JSON.parse(fs__namespace.readFileSync(parsed['coin'], 'utf-8'));
|
|
2263
2633
|
const coin = coinFile.coin || coinFile;
|
|
2264
2634
|
const state = JSON.parse(fs__namespace.readFileSync(parsed['state'], 'utf-8'));
|
|
2635
|
+
const pubW = parsed['public-withdraw-stroops'];
|
|
2636
|
+
const changeStealth = parsed['change-stealth'];
|
|
2637
|
+
if (pubW !== undefined && changeStealth === undefined) {
|
|
2638
|
+
console.error('Error: --change-stealth <stpl1...> is required when using --public-withdraw-stroops');
|
|
2639
|
+
process.exit(1);
|
|
2640
|
+
}
|
|
2641
|
+
if (pubW === undefined && changeStealth !== undefined) {
|
|
2642
|
+
console.error('Error: --change-stealth is only valid with --public-withdraw-stroops');
|
|
2643
|
+
process.exit(1);
|
|
2644
|
+
}
|
|
2265
2645
|
const sdk = await PrivacyPoolSDK.init();
|
|
2646
|
+
const applicationId = resolveApplicationIdFromCli(parsed);
|
|
2647
|
+
const audit = resolveTransactionAuditParams(applicationId);
|
|
2266
2648
|
const result = await sdk.proveWithdrawal(coin, state, {
|
|
2267
2649
|
withdrawAddressHi: withdrawHi,
|
|
2268
2650
|
withdrawAddressLo: withdrawLo,
|
|
2269
2651
|
privKeyScalar: parsed['priv-key-scalar'],
|
|
2270
2652
|
ephemeralXHex: parsed['ephemeral-x'],
|
|
2271
2653
|
ephemeralYHex: parsed['ephemeral-y'],
|
|
2654
|
+
applicationId,
|
|
2655
|
+
audit,
|
|
2656
|
+
...(pubW !== undefined
|
|
2657
|
+
? {
|
|
2658
|
+
publicWithdrawStroops: parseStroopsU64('--public-withdraw-stroops', pubW, 0n),
|
|
2659
|
+
changeRecipientStealthAddress: changeStealth,
|
|
2660
|
+
}
|
|
2661
|
+
: {}),
|
|
2272
2662
|
});
|
|
2273
2663
|
// Output proof and public hex to stdout (newline-separated)
|
|
2274
2664
|
console.log(result.proof_hex);
|
|
@@ -2293,7 +2683,15 @@ async function handleDepositProof(args) {
|
|
|
2293
2683
|
console.error('Error: --stealth <stpl1...> is required');
|
|
2294
2684
|
process.exit(1);
|
|
2295
2685
|
}
|
|
2686
|
+
const token = parsed['token'] ?? process.env.TOKEN_ADDRESS;
|
|
2687
|
+
if (!token) {
|
|
2688
|
+
console.error('Error: --token <C...> or TOKEN_ADDRESS is required');
|
|
2689
|
+
process.exit(1);
|
|
2690
|
+
}
|
|
2691
|
+
const [assetHi, assetLo] = stellarContractAddressToAssetFrDecimals(token);
|
|
2296
2692
|
const sdk = await PrivacyPoolSDK.init();
|
|
2693
|
+
const applicationId = resolveApplicationIdFromCli(parsed);
|
|
2694
|
+
const audit = resolveTransactionAuditParams(applicationId);
|
|
2297
2695
|
const recipientPublicKeys = recipientPublicKeysDecimalFromStealthAddress(parsed['stealth']);
|
|
2298
2696
|
const coinPath = parsed['coin'];
|
|
2299
2697
|
const ephemeralScalarHex = parsed['ephemeral-scalar-hex'];
|
|
@@ -2310,11 +2708,15 @@ async function handleDepositProof(args) {
|
|
|
2310
2708
|
console.error('Error: --value must match coin.value when using --coin');
|
|
2311
2709
|
process.exit(1);
|
|
2312
2710
|
}
|
|
2711
|
+
const ah = c.asset_hi ?? assetHi;
|
|
2712
|
+
const al = c.asset_lo ?? assetLo;
|
|
2313
2713
|
deposit = {
|
|
2314
2714
|
value,
|
|
2315
2715
|
nullifier: c.nullifier,
|
|
2316
2716
|
ephemeralKeyScalar: scalarHexToFrDecimal(ephemeralScalarHex),
|
|
2317
2717
|
recipientPublicKeys,
|
|
2718
|
+
asset: [ah, al],
|
|
2719
|
+
applicationId,
|
|
2318
2720
|
};
|
|
2319
2721
|
}
|
|
2320
2722
|
else {
|
|
@@ -2327,14 +2729,22 @@ async function handleDepositProof(args) {
|
|
|
2327
2729
|
nullifier: randomFrDecimal(),
|
|
2328
2730
|
ephemeralKeyScalar: randomFrDecimal253(),
|
|
2329
2731
|
recipientPublicKeys,
|
|
2732
|
+
asset: [assetHi, assetLo],
|
|
2733
|
+
applicationId,
|
|
2330
2734
|
};
|
|
2331
2735
|
}
|
|
2736
|
+
const publicLegs = {
|
|
2737
|
+
publicWithdrawnAssets: [['0', '0']],
|
|
2738
|
+
publicDepositedAssets: [[deposit.asset[0], deposit.asset[1]]],
|
|
2739
|
+
publicDeposits: [deposit.value],
|
|
2740
|
+
publicWithdrawals: ['0'],
|
|
2741
|
+
};
|
|
2332
2742
|
const result = await sdk.proveTransaction({
|
|
2333
2743
|
stateRoot: parsed['state-root'],
|
|
2334
2744
|
withdrawAddressHi: '0',
|
|
2335
2745
|
withdrawAddressLo: '0',
|
|
2336
2746
|
privKeyScalar: randomFrDecimal253(),
|
|
2337
|
-
}, ['dummy', 'dummy'], [deposit, 'dummy']);
|
|
2747
|
+
}, publicLegs, ['dummy', 'dummy'], [deposit, 'dummy'], audit);
|
|
2338
2748
|
console.log(result.proof_hex);
|
|
2339
2749
|
console.log(result.public_hex);
|
|
2340
2750
|
if (parsed['output-proof']) {
|