@auditable/privacy-pool-zk-sdk 0.8.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/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import * as snarkjs from 'snarkjs';
2
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
 
@@ -355,6 +354,38 @@ function proofToHex(proof_json) {
355
354
  }
356
355
  }
357
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
+
358
389
  /**
359
390
  * Convert snarkjs public signals JSON to hex bytes for Soroban contract.
360
391
  * @param {string} public_json
@@ -565,6 +596,13 @@ function isLikeNone(x) {
565
596
  return x === undefined || x === null;
566
597
  }
567
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
+
568
606
  function passStringToWasm0(arg, malloc, realloc) {
569
607
  if (realloc === undefined) {
570
608
  const buf = cachedTextEncoder.encode(arg);
@@ -741,15 +779,16 @@ var wasmBindings = /*#__PURE__*/Object.freeze({
741
779
  generateCoinWithOwnerPubHex: generateCoinWithOwnerPubHex,
742
780
  initSync: initSync,
743
781
  proofToHex: proofToHex,
782
+ proveGroth16: proveGroth16,
744
783
  publicToHex: publicToHex
745
784
  });
746
785
 
747
- const isNode$2 = typeof process !== 'undefined' && !!process.versions?.node;
786
+ const isNode$1 = typeof process !== 'undefined' && !!process.versions?.node;
748
787
  let wasmModule = null;
749
788
  async function loadWasm(wasmBinary) {
750
789
  if (wasmModule)
751
790
  return wasmModule;
752
- if (isNode$2) {
791
+ if (isNode$1) {
753
792
  if (wasmBinary) {
754
793
  initSync({ module: wasmBinary });
755
794
  }
@@ -776,468 +815,301 @@ async function loadWasm(wasmBinary) {
776
815
  return wasmModule;
777
816
  }
778
817
 
779
- function getDefaultExportFromCjs (x) {
780
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
781
- }
782
-
783
- var witness_calculator = async function builder(code, options) {
784
-
785
- options = options || {};
786
-
787
- let wasmModule;
788
- try {
789
- wasmModule = await WebAssembly.compile(code);
790
- } catch (err) {
791
- console.log(err);
792
- console.log("\nTry to run circom --c in order to generate c++ code instead\n");
793
- throw new Error(err);
794
- }
795
-
796
- let wc;
797
-
798
- let errStr = "";
799
- let msgStr = "";
800
-
801
- const instance = await WebAssembly.instantiate(wasmModule, {
802
- runtime: {
803
- exceptionHandler : function(code) {
804
- let err;
805
- if (code == 1) {
806
- err = "Signal not found.\n";
807
- } else if (code == 2) {
808
- err = "Too many signals set.\n";
809
- } else if (code == 3) {
810
- err = "Signal already set.\n";
811
- } else if (code == 4) {
812
- err = "Assert Failed.\n";
813
- } else if (code == 5) {
814
- err = "Not enough memory.\n";
815
- } else if (code == 6) {
816
- err = "Input signal array access exceeds the size.\n";
817
- } else if (code == 7) {
818
- err = "Out of bounds array access.\n";
819
- } else {
820
- err = "Unknown error.\n";
821
- }
822
- throw new Error(err + errStr);
823
- },
824
- printErrorMessage : function() {
825
- errStr += getMessage() + "\n";
826
- // console.error(getMessage());
827
- },
828
- writeBufferMessage : function() {
829
- const msg = getMessage();
830
- // Any calls to `log()` will always end with a `\n`, so that's when we print and reset
831
- if (msg === "\n") {
832
- console.log(msgStr);
833
- msgStr = "";
834
- } else {
835
- // If we've buffered other content, put a space in between the items
836
- if (msgStr !== "") {
837
- msgStr += " ";
838
- }
839
- // Then append the message to the message we are creating
840
- msgStr += msg;
841
- }
842
- },
843
- showSharedRWMemory : function() {
844
- printSharedRWMemory ();
845
- }
846
-
847
- }
848
- });
849
-
850
- const sanityCheck =
851
- options;
852
- // options &&
853
- // (
854
- // options.sanityCheck ||
855
- // options.logGetSignal ||
856
- // options.logSetSignal ||
857
- // options.logStartComponent ||
858
- // options.logFinishComponent
859
- // );
860
-
861
-
862
- wc = new WitnessCalculator(instance, sanityCheck);
863
- return wc;
864
-
865
- function getMessage() {
866
- var message = "";
867
- var c = instance.exports.getMessageChar();
868
- while ( c != 0 ) {
869
- message += String.fromCharCode(c);
870
- c = instance.exports.getMessageChar();
871
- }
872
- return message;
873
- }
874
-
875
- function printSharedRWMemory () {
876
- const shared_rw_memory_size = instance.exports.getFieldNumLen32();
877
- const arr = new Uint32Array(shared_rw_memory_size);
878
- for (let j=0; j<shared_rw_memory_size; j++) {
879
- arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
880
- }
881
-
882
- // If we've buffered other content, put a space in between the items
883
- if (msgStr !== "") {
884
- msgStr += " ";
885
- }
886
- // Then append the value to the message we are creating
887
- msgStr += (fromArray32(arr).toString());
888
- }
889
-
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
+ },
890
852
  };
891
-
892
- class WitnessCalculator {
893
- constructor(instance, sanityCheck) {
894
- this.instance = instance;
895
-
896
- this.version = this.instance.exports.getVersion();
897
- this.n32 = this.instance.exports.getFieldNumLen32();
898
-
899
- this.instance.exports.getRawPrime();
900
- const arr = new Uint32Array(this.n32);
901
- for (let i=0; i<this.n32; i++) {
902
- arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
903
- }
904
- this.prime = fromArray32(arr);
905
-
906
- this.witnessSize = this.instance.exports.getWitnessSize();
907
-
908
- this.sanityCheck = sanityCheck;
909
- }
910
-
911
- circom_version() {
912
- return this.instance.exports.getVersion();
913
- }
914
-
915
- async _doCalculateWitness(input_orig, sanityCheck) {
916
- //input is assumed to be a map from signals to arrays of bigints
917
- this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
918
- let prefix = "";
919
- var input = new Object();
920
- //console.log("Input: ", input_orig);
921
- qualify_input(prefix,input_orig,input);
922
- //console.log("Input after: ",input);
923
- const keys = Object.keys(input);
924
- var input_counter = 0;
925
- keys.forEach( (k) => {
926
- const h = fnvHash(k);
927
- const hMSB = parseInt(h.slice(0,8), 16);
928
- const hLSB = parseInt(h.slice(8,16), 16);
929
- const fArr = flatArray(input[k]);
930
- let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
931
- if (signalSize < 0){
932
- throw new Error(`Signal ${k} not found\n`);
933
- }
934
- if (fArr.length < signalSize) {
935
- throw new Error(`Not enough values for input signal ${k}\n`);
936
- }
937
- if (fArr.length > signalSize) {
938
- throw new Error(`Too many values for input signal ${k}\n`);
939
- }
940
- for (let i=0; i<fArr.length; i++) {
941
- const arrFr = toArray32(normalize(fArr[i],this.prime),this.n32);
942
- for (let j=0; j<this.n32; j++) {
943
- this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
944
- }
945
- try {
946
- this.instance.exports.setInputSignal(hMSB, hLSB,i);
947
- input_counter++;
948
- } catch (err) {
949
- // console.log(`After adding signal ${i} of ${k}`)
950
- throw new Error(err);
951
- }
952
- }
953
-
954
- });
955
- if (input_counter < this.instance.exports.getInputSize()) {
956
- throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
957
- }
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()}`);
958
862
  }
959
-
960
- async calculateWitness(input, sanityCheck) {
961
-
962
- const w = [];
963
- await this._doCalculateWitness(input, sanityCheck);
964
-
965
- for (let i=0; i<this.witnessSize; i++) {
966
- this.instance.exports.getWitness(i);
967
- const arr = new Uint32Array(this.n32);
968
- for (let j=0; j<this.n32; j++) {
969
- arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
970
- }
971
- w.push(fromArray32(arr));
972
- }
973
-
974
- 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
+ };
975
874
  }
976
-
977
-
978
- async calculateBinWitness(input, sanityCheck) {
979
-
980
- const buff32 = new Uint32Array(this.witnessSize*this.n32);
981
- const buff = new Uint8Array( buff32.buffer);
982
- await this._doCalculateWitness(input, sanityCheck);
983
-
984
- for (let i=0; i<this.witnessSize; i++) {
985
- this.instance.exports.getWitness(i);
986
- const pos = i*this.n32;
987
- for (let j=0; j<this.n32; j++) {
988
- buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
989
- }
990
- }
991
-
992
- return buff;
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);
993
933
  }
994
-
995
-
996
- async calculateWTNSBin(input, sanityCheck) {
997
-
998
- const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
999
- const buff = new Uint8Array( buff32.buffer);
1000
- await this._doCalculateWitness(input, sanityCheck);
1001
-
1002
- //"wtns"
1003
- buff[0] = "w".charCodeAt(0);
1004
- buff[1] = "t".charCodeAt(0);
1005
- buff[2] = "n".charCodeAt(0);
1006
- buff[3] = "s".charCodeAt(0);
1007
-
1008
- //version 2
1009
- buff32[1] = 2;
1010
-
1011
- //number of sections: 2
1012
- buff32[2] = 2;
1013
-
1014
- //id section 1
1015
- buff32[3] = 1;
1016
-
1017
- const n8 = this.n32*4;
1018
- //id section 1 length in 64bytes
1019
- const idSection1length = 8 + n8;
1020
- const idSection1lengthHex = idSection1length.toString(16);
1021
- buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
1022
- buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
1023
-
1024
- //this.n32
1025
- buff32[6] = n8;
1026
-
1027
- //prime number
1028
- this.instance.exports.getRawPrime();
1029
-
1030
- var pos = 7;
1031
- for (let j=0; j<this.n32; j++) {
1032
- buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
1033
- }
1034
- pos += this.n32;
1035
-
1036
- // witness size
1037
- buff32[pos] = this.witnessSize;
1038
- pos++;
1039
-
1040
- //id section 2
1041
- buff32[pos] = 2;
1042
- pos++;
1043
-
1044
- // section 2 length
1045
- const idSection2length = n8*this.witnessSize;
1046
- const idSection2lengthHex = idSection2length.toString(16);
1047
- buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
1048
- buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
1049
-
1050
- pos += 2;
1051
- for (let i=0; i<this.witnessSize; i++) {
1052
- this.instance.exports.getWitness(i);
1053
- for (let j=0; j<this.n32; j++) {
1054
- buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
1055
- }
1056
- pos += this.n32;
1057
- }
1058
-
1059
- 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;
1060
939
  }
1061
-
940
+ return layout.outputNoteOffset;
1062
941
  }
1063
-
1064
-
1065
- function qualify_input_list(prefix,input,input1){
1066
- if (Array.isArray(input)) {
1067
- for (let i = 0; i<input.length; i++) {
1068
- let new_prefix = prefix + "[" + i + "]";
1069
- qualify_input_list(new_prefix,input[i],input1);
1070
- }
1071
- } else {
1072
- qualify_input(prefix,input,input1);
1073
- }
1074
- }
1075
-
1076
- function qualify_input(prefix,input,input1) {
1077
- if (Array.isArray(input)) {
1078
- let a = flatArray(input);
1079
- if (a.length > 0) {
1080
- let t = typeof a[0];
1081
- for (let i = 1; i<a.length; i++) {
1082
- if (typeof a[i] != t){
1083
- throw new Error(`Types are not the same in the key ${prefix}`);
1084
- }
1085
- }
1086
- if (t == "object") {
1087
- qualify_input_list(prefix,input,input1);
1088
- } else {
1089
- input1[prefix] = input;
1090
- }
1091
- } else {
1092
- input1[prefix] = input;
1093
- }
1094
- } else if (typeof input == "object") {
1095
- const keys = Object.keys(input);
1096
- keys.forEach( (k) => {
1097
- let new_prefix = prefix == ""? k : prefix + "." + k;
1098
- qualify_input(new_prefix,input[k],input1);
1099
- });
1100
- } else {
1101
- input1[prefix] = input;
942
+ function publicOutputsLen(layout) {
943
+ if (ciphertextsInPublicSignals(layout)) {
944
+ return layout.outputNoteOffset + layout.nOuts * layout.noteOutputLen + layout.nOuts;
1102
945
  }
946
+ return layout.outputNoteOffset + layout.nOuts;
1103
947
  }
1104
-
1105
- function toArray32(rem,size) {
1106
- const res = []; //new Uint32Array(size); //has no unshift
1107
- const radix = BigInt(0x100000000);
1108
- while (rem) {
1109
- res.unshift( Number(rem % radix));
1110
- rem = rem / radix;
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();
1111
977
  }
1112
- if (size) {
1113
- var i = size - res.length;
1114
- while (i>0) {
1115
- res.unshift(0);
1116
- i--;
1117
- }
978
+ if (nonce === COMMITMENT_V2_ZK_NONCE) {
979
+ return standardLayout();
1118
980
  }
1119
- return res;
1120
- }
1121
-
1122
- function fromArray32(arr) { //returns a BigInt
1123
- var res = BigInt(0);
1124
- const radix = BigInt(0x100000000);
1125
- for (let i = 0; i<arr.length; i++) {
1126
- res = res*radix + BigInt(arr[i]);
981
+ if (nonce === BINDING_ZK_NONCE) {
982
+ return standardBindingLayout();
1127
983
  }
1128
- return res;
1129
- }
1130
-
1131
- function flatArray(a) {
1132
- var res = [];
1133
- fillArray(res, a);
1134
- return res;
1135
-
1136
- function fillArray(res, a) {
1137
- if (Array.isArray(a)) {
1138
- for (let i=0; i<a.length; i++) {
1139
- fillArray(res, a[i]);
1140
- }
1141
- } else {
1142
- res.push(a);
1143
- }
984
+ if (nonce === SIX_BY_SIX_ZK_NONCE) {
985
+ return sixBySixLayout();
1144
986
  }
987
+ if (nonce === SIX_BY_SIX_BINDING_ZK_NONCE) {
988
+ return sixBySixBindingLayout();
989
+ }
990
+ throw new Error(`unknown zk config nonce ${nonce.toString()}`);
1145
991
  }
1146
-
1147
- function normalize(n, prime) {
1148
- let res = BigInt(n) % prime;
1149
- if (res < 0) res += prime;
1150
- return res
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}`);
1151
1013
  }
1152
-
1153
- function fnvHash(str) {
1154
- const uint64_max = BigInt(2) ** BigInt(64);
1155
- let hash = BigInt("0xCBF29CE484222325");
1156
- for (var i = 0; i < str.length; i++) {
1157
- hash ^= BigInt(str[i].charCodeAt());
1158
- hash *= BigInt(0x100000001B3);
1159
- 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');
1160
1018
  }
1161
- let shash = hash.toString(16);
1162
- let n = 16 - shash.length;
1163
- shash = '0'.repeat(n).concat(shash);
1164
- return shash;
1019
+ return circuitProfileFromName('2x2');
1165
1020
  }
1166
1021
 
1167
- var witnessCalculatorModule = /*@__PURE__*/getDefaultExportFromCjs(witness_calculator);
1168
-
1169
- const isNode$1 = typeof process !== 'undefined' && !!process.versions?.node;
1170
- async function generateWitness(input, circuitWasm) {
1171
- let wasmBuffer;
1172
- let wc;
1173
- if (isNode$1) {
1174
- const fs = await import('fs');
1175
- const nodePath = await import('path');
1176
- const { fileURLToPath } = await import('url');
1177
- const dir = nodePath.dirname(fileURLToPath(import.meta.url));
1178
- const assetsDir = nodePath.resolve(dir, '..', 'assets');
1179
- wc = { default: witnessCalculatorModule };
1180
- if (circuitWasm) {
1181
- wasmBuffer = circuitWasm;
1182
- }
1183
- else {
1184
- const wasmPath = nodePath.resolve(assetsDir, 'main.wasm');
1185
- if (!fs.existsSync(wasmPath)) {
1186
- throw new Error(`Circuit WASM not found at ${wasmPath}. Run build.sh first.`);
1187
- }
1188
- wasmBuffer = fs.readFileSync(wasmPath);
1189
- }
1190
- }
1191
- else {
1192
- if (!circuitWasm) {
1193
- throw new Error('In browser, you must pass circuitWasm to PrivacyPoolSDK.init(). ' +
1194
- 'Load the circuit WASM file via fetch() and pass the ArrayBuffer.');
1195
- }
1196
- wasmBuffer = circuitWasm;
1197
- 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);
1198
1032
  }
1199
- const calculator = await wc.default(wasmBuffer);
1200
- const wtns = await calculator.calculateWTNSBin(input, 0);
1201
- return wtns;
1033
+ return trimmed;
1202
1034
  }
1203
1035
 
1204
- // @ts-ignore - snarkjs types
1205
1036
  const isNode = typeof process !== 'undefined' && !!process.versions?.node;
1206
- /**
1207
- * Normalize BufferSource to Uint8Array. Required for @iden3/binfileutils/fastfile:
1208
- * they read via data.buffer and data.byteOffset, which ArrayBuffer does not have.
1209
- */
1210
1037
  function toUint8Array(src) {
1211
- if (src instanceof Uint8Array)
1038
+ if (src instanceof Uint8Array) {
1212
1039
  return src;
1213
- if (src instanceof ArrayBuffer)
1040
+ }
1041
+ if (src instanceof ArrayBuffer) {
1214
1042
  return new Uint8Array(src);
1043
+ }
1215
1044
  return new Uint8Array(src.buffer, src.byteOffset, src.byteLength);
1216
1045
  }
1217
- async function generateProof(wtns, zkey) {
1218
- let zkeyData;
1219
- if (zkey) {
1220
- zkeyData = toUint8Array(zkey);
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;
1221
1081
  }
1222
- else if (isNode) {
1223
- const fs = await import('fs');
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)) {
1224
1089
  const nodePath = await import('path');
1225
- const { fileURLToPath } = await import('url');
1226
- const dir = nodePath.dirname(fileURLToPath(import.meta.url));
1227
- const zkeyPath = nodePath.resolve(dir, '..', 'assets', 'main_final.zkey');
1228
- if (!fs.existsSync(zkeyPath)) {
1229
- throw new Error(`Proving key not found at ${zkeyPath}. Run build.sh first.`);
1230
- }
1231
- const zkeyBuffer = fs.readFileSync(zkeyPath);
1232
- zkeyData = toUint8Array(zkeyBuffer);
1090
+ return readNodeAssetFile(nodePath.resolve(base, fileName));
1233
1091
  }
1234
- else {
1235
- throw new Error('In browser, you must pass zkey to PrivacyPoolSDK.init(). ' +
1236
- 'Load the zkey file via fetch() and pass the ArrayBuffer.');
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
+ };
1237
1111
  }
1238
- const wtnsData = toUint8Array(wtns);
1239
- const { proof, publicSignals } = await snarkjs.groth16.prove({ type: 'mem', data: zkeyData }, { type: 'mem', data: wtnsData });
1240
- return { proof, publicSignals };
1112
+ return loadBundledCircuitArtifacts(config.circuit, artifactBaseUrl);
1241
1113
  }
1242
1114
 
1243
1115
  var dist = {};
@@ -1648,6 +1520,8 @@ const DOM_PADDING = 1347436868n;
1648
1520
  const DOM_NULLIFIER = 1346917708n;
1649
1521
  const DOM_ESCROW = 1346720579n;
1650
1522
  const DOM_ENC = 1346719299n;
1523
+ const DOM_AUDIT_TAG = 1346458695n;
1524
+ const DOM_OUTPUT_NOTE_TAG = 1347310663n;
1651
1525
  const BABYJUB_SUBGROUP_ORDER = 2736030358979909402780800718157159386076813972158567259200215660948447373041n;
1652
1526
  /**
1653
1527
  * Map any integer into `(0, l)` so it satisfies circom `LessThan(252)` vs
@@ -1801,37 +1675,23 @@ function resolveDepositSlot(slot) {
1801
1675
  }
1802
1676
  return slot;
1803
1677
  }
1804
- function buildUniformAuditParams(applicationId = DEFAULT_APPLICATION_ID, auditPublicKey) {
1678
+ function buildUniformAuditParams(applicationId = DEFAULT_APPLICATION_ID, auditPublicKey, _nAuditSlots = TRANSACTION_N_AUDIT_SLOTS) {
1805
1679
  return {
1806
1680
  applicationId,
1807
- noteAuditPublicKeys: [
1808
- auditPublicKey,
1809
- auditPublicKey,
1810
- auditPublicKey,
1811
- auditPublicKey,
1812
- ],
1813
- auditEphemeralScalars: [
1814
- randomFrDecimal253(),
1815
- randomFrDecimal253(),
1816
- randomFrDecimal253(),
1817
- randomFrDecimal253(),
1818
- ],
1681
+ noteAuditPublicKey: auditPublicKey,
1682
+ auditEphemeralScalar: randomFrDecimal253(),
1819
1683
  };
1820
1684
  }
1821
1685
  function resolveSlotApplicationIds(audit, withdrawSlots, depositSlots) {
1822
- const w0 = resolveWithdrawSlot(withdrawSlots[0]);
1823
- const w1 = resolveWithdrawSlot(withdrawSlots[1]);
1824
- const d0 = resolveDepositSlot(depositSlots[0]);
1825
- const d1 = resolveDepositSlot(depositSlots[1]);
1826
1686
  return {
1827
- inputApplicationIds: [
1828
- w0 && isActiveWithdraw(w0) ? audit.applicationId : '0',
1829
- w1 && isActiveWithdraw(w1) ? audit.applicationId : '0',
1830
- ],
1831
- outputApplicationIds: [
1832
- d0 && isActiveDeposit(d0) ? audit.applicationId : '0',
1833
- d1 && isActiveDeposit(d1) ? audit.applicationId : '0',
1834
- ],
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
+ }),
1835
1695
  };
1836
1696
  }
1837
1697
  function withApplicationIdOnDeposit(deposit, applicationId) {
@@ -1855,16 +1715,19 @@ function resolveAuditPublicKeyFromEnv(auditPublicKey) {
1855
1715
  }
1856
1716
  return DEMO_AUDIT_PUBLIC_KEY;
1857
1717
  }
1858
- function resolveTransactionAuditParams(applicationId, auditPublicKey) {
1859
- return buildUniformAuditParams(applicationId, resolveAuditPublicKeyFromEnv(auditPublicKey));
1718
+ function resolveTransactionAuditParams(applicationId, auditPublicKey, nAuditSlots = TRANSACTION_N_AUDIT_SLOTS) {
1719
+ return buildUniformAuditParams(applicationId, resolveAuditPublicKeyFromEnv(auditPublicKey), nAuditSlots);
1860
1720
  }
1861
1721
 
1862
1722
  /** Matches `Transaction(20, 2, 2, publicNInputs, publicNOutputs, 4, 12, 6)` in `circuits/main.circom`. */
1863
1723
  const TRANSACTION_TREE_DEPTH = 20;
1864
1724
  const TRANSACTION_N_INS = 2;
1865
1725
  const TRANSACTION_N_OUTS = 2;
1866
- const NOTE_AUDIT_LEN$1 = 12;
1726
+ const NOTE_AUDIT_LEN$2 = 12;
1867
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;
1868
1731
  /** BN254 scalar field modulus (ark `Fr`, circom signals). */
1869
1732
  const BN254_SCALAR_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
1870
1733
  /**
@@ -1872,6 +1735,29 @@ const BN254_SCALAR_MOD = 2188824287183927522224640574525727508854836440041603434
1872
1735
  * (matches `libs/cryptography` `scalar_mul_253`).
1873
1736
  */
1874
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
+ }
1875
1761
  function normalizeHex$1(hex) {
1876
1762
  const s = hex.trim().replace(/^0x/i, '');
1877
1763
  if (!/^[0-9a-fA-F]*$/.test(s)) {
@@ -1992,12 +1878,29 @@ function resolveDepositsForWitness(depositSlots, wasm) {
1992
1878
  resolveDeposit(depositSlots[1], wasm),
1993
1879
  ];
1994
1880
  }
1995
- function buildTransactionWitnessInput(publicParams, publicLegs, withdrawSlots, depositSlots, audit, wasm) {
1996
- const w0 = resolveWithdraw(withdrawSlots[0], wasm);
1997
- const w1 = resolveWithdraw(withdrawSlots[1], wasm);
1998
- const d0 = resolveDeposit(depositSlots[0], wasm);
1999
- const d1 = resolveDeposit(depositSlots[1], wasm);
2000
- const appIds = resolveSlotApplicationIds(audit, withdrawSlots, depositSlots);
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);
2001
1904
  return {
2002
1905
  stateRoot: publicParams.stateRoot,
2003
1906
  withdrawAddressHi: publicParams.withdrawAddressHi,
@@ -2006,35 +1909,32 @@ function buildTransactionWitnessInput(publicParams, publicLegs, withdrawSlots, d
2006
1909
  escrowRecipientLo: publicParams.escrowRecipientLo ?? '0',
2007
1910
  sweepOutputOwnerPubX: publicParams.sweepOutputOwnerPubX ?? '0',
2008
1911
  sweepOutputOwnerPubY: publicParams.sweepOutputOwnerPubY ?? '0',
2009
- privKeyScalars: [w0.privKeyScalar, w1.privKeyScalar],
2010
- ownerPubs: [w0.ownerPub, w1.ownerPub],
2011
- paddingRandoms: [w0.paddingRandom, w1.paddingRandom],
2012
- withdrawnValues: [w0.value, w1.value],
2013
- withdrawnNullifiers: [w0.nullifier, w1.nullifier],
2014
- withdrawnSecrets: [w0.secret, w1.secret],
2015
- withdrawnAssets: [w0.asset, w1.asset],
2016
- withdrawnEscrowNonces: [w0.escrowNonce, w1.escrowNonce],
2017
- inputRecipientStellar: [w0.recipientStellar, w1.recipientStellar],
2018
- stateSiblings: [w0.stateSiblings, w1.stateSiblings],
2019
- stateIndex: [w0.stateIndex, w1.stateIndex],
2020
- depositedValues: [d0.value, d1.value],
2021
- depositedNullifiers: [d0.nullifier, d1.nullifier],
2022
- depositedAssets: [d0.asset, d1.asset],
2023
- depositedEphemeralKeyScalars: [d0.ephemeralKeyScalar, d1.ephemeralKeyScalar],
2024
- depositedRecipientPublicKeys: [d0.recipientPublicKeys, d1.recipientPublicKeys],
2025
- depositedEscrowNonces: [d0.escrowNonce ?? '0', d1.escrowNonce ?? '0'],
2026
- outputRecipientStellar: [
2027
- d0.recipientStellar ?? ['0', '0'],
2028
- d1.recipientStellar ?? ['0', '0'],
2029
- ],
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']),
2030
1930
  inputApplicationIds: appIds.inputApplicationIds,
2031
1931
  outputApplicationIds: appIds.outputApplicationIds,
2032
- auditEphemeralScalars: audit.auditEphemeralScalars,
2033
- noteAuditPublicKeys: audit.noteAuditPublicKeys,
2034
- publicWithdrawnAssets: publicLegs.publicWithdrawnAssets,
2035
- publicDepositedAssets: publicLegs.publicDepositedAssets,
2036
- publicDeposits: publicLegs.publicDeposits,
2037
- publicWithdrawals: publicLegs.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,
2038
1938
  };
2039
1939
  }
2040
1940
  /** `stpl1…` stealth address → `[x, y]` as decimal field strings for `depositedRecipientPublicKeys`. */
@@ -2060,133 +1960,628 @@ function withdrawObjectFromMerkleWitness(witness, ownerPubHex, applicationId, pr
2060
1960
  };
2061
1961
  }
2062
1962
 
2063
- /** Stroops amount as a decimal field string. Reject JS `number` so 18-decimal values cannot silently narrow. */
2064
- function coinValueDecimal(amount) {
2065
- if (typeof amount === 'number') {
2066
- throw new TypeError('coin value must be bigint or decimal string, not number');
2067
- }
2068
- if (typeof amount === 'bigint') {
2069
- if (amount < 0n) {
2070
- throw new RangeError('amount must be a non-negative integer');
2071
- }
2072
- return amount.toString(10);
2073
- }
2074
- if (typeof amount !== 'string') {
2075
- throw new TypeError('coin value must be bigint or decimal string, not number');
2076
- }
2077
- const trimmed = amount.trim();
2078
- if (!/^[0-9]+$/.test(trimmed)) {
2079
- throw new TypeError('coin value decimal string must be a non-negative integer');
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;
2080
1976
  }
2081
- return trimmed;
1977
+ return buf;
2082
1978
  }
2083
- function generatedCoinFromWasm(value) {
2084
- const raw = value;
2085
- const precommitementHex = raw.precommitementHex ?? raw.precommitement_hex;
2086
- if (!precommitementHex) {
2087
- throw new Error('generated coin missing precommitement hex');
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, '')}`);
2088
1983
  }
2089
- return {
2090
- coin: raw.coin,
2091
- commitment_hex: raw.commitment_hex,
2092
- precommitement_hex: precommitementHex,
2093
- precommitementHex,
2094
- };
1984
+ return BigInt(trimmed);
2095
1985
  }
2096
- class PrivacyPoolSDK {
2097
- constructor(wasm, options) {
2098
- this.wasm = wasm;
2099
- this.options = options;
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');
2100
1997
  }
2101
- /**
2102
- * Initialize the SDK by loading WASM modules.
2103
- *
2104
- * In Node.js, assets are loaded from the filesystem automatically.
2105
- * In browser, pass pre-loaded buffers via options:
2106
- * ```ts
2107
- * const sdk = await PrivacyPoolSDK.init({
2108
- * wasmBinary: await fetch('/pkg/client_sdk_wasm_bg.wasm').then(r => r.arrayBuffer()),
2109
- * circuitWasm: await fetch('/assets/main.wasm').then(r => r.arrayBuffer()),
2110
- * zkey: await fetch('/assets/main_final.zkey').then(r => r.arrayBuffer()),
2111
- * });
2112
- * ```
2113
- */
2114
- static async init(options) {
2115
- const opts = options ?? {};
2116
- const wasm = await loadWasm(opts.wasmBinary);
2117
- return new PrivacyPoolSDK(wasm, opts);
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');
2118
2002
  }
2119
- /**
2120
- * Uniform 32-byte scalar as lowercase hex (Web Crypto). Same in browser and Node 19+.
2121
- */
2122
- static generateRandomScalarHex32() {
2123
- return generateRandomScalarHex32();
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;
2124
2008
  }
2125
- /**
2126
- * Text to sign with a Stellar wallet for spend-key derivation (UTF-8). No WASM required.
2127
- * The message binds network, pool, registry, and schema version (H6).
2128
- */
2129
- static buildStealthAddressSignMessage(address, domain, nonce = DEFAULT_STEALTH_SIGN_NONCE) {
2130
- return buildStealthAddressSignMessage(address, domain, nonce);
2009
+ return fields;
2010
+ }
2011
+ function encodeTransactionCiphertextBlob(parameters) {
2012
+ const expected = ciphertextFieldCount(parameters.layout);
2013
+ const fields = [];
2014
+ for (const slot of parameters.auditCiphertexts) {
2015
+ fields.push(...slot);
2131
2016
  }
2132
- /**
2133
- * Generate a new coin with random nullifier, secret, and random owner-pub field elements (dev / self-contained tests).
2134
- * @param amount Decimal field element as `bigint` or decimal `string` (never JS `number`).
2135
- * @param assetHiDecimal / assetLoDecimal Decimal Fr strings for Stellar asset contract id (two limbs).
2136
- */
2137
- generateCoin(amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
2138
- return generatedCoinFromWasm(this.wasm.generateCoin(coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2017
+ for (const note of parameters.outputNoteCiphertexts) {
2018
+ fields.push(...note);
2139
2019
  }
2140
- /**
2141
- * Generate a coin with the same commitment shape as on-chain deposit: pass owner public key (hex x, y).
2142
- * @param amount Decimal field element (`bigint` | `string`).
2143
- */
2144
- generateCoinWithOwnerPub(ownerPub, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
2145
- return generatedCoinFromWasm(this.wasm.generateCoinWithOwnerPubHex(ownerPub.x, ownerPub.y, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2020
+ if (fields.length !== expected) {
2021
+ throw new Error(`ciphertext blob expected ${String(expected)} fields, got ${String(fields.length)}`);
2146
2022
  }
2147
- /**
2148
- * Coin for a depositor `ephemeralKeyScalar` (32-byte hex): `coin.secret = Poseidon255(1)(scalar)` as in `deposit.circom`.
2149
- * @param amount Decimal field element (`bigint` | `string`).
2150
- */
2151
- generateCoinFromDepositEphemeralScalarHex(scalarHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
2152
- return generatedCoinFromWasm(this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2023
+ return encodeCiphertextBlob(fields);
2024
+ }
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)}`);
2153
2030
  }
2154
- /**
2155
- * Aligned deposit coin: `secret = Poseidon₁(scalar)` and owner pub from recipient hex coords.
2156
- * @param amount Decimal field element (`bigint` | `string`).
2157
- */
2158
- generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = '0') {
2159
- return generatedCoinFromWasm(this.wasm.generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
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;
2160
2037
  }
2161
- /**
2162
- * Merkle root, path, and coin fields for the first withdraw leg (Rust LeanIMT + Poseidon).
2163
- */
2164
- buildWithdrawMerkleWitness(coin, state) {
2165
- const coinJson = JSON.stringify(coin);
2166
- const stateJson = JSON.stringify(state);
2167
- const resultJson = this.wasm.buildWithdrawMerkleWitness(coinJson, stateJson);
2168
- return JSON.parse(resultJson);
2038
+ const outputNoteCiphertexts = [];
2039
+ for (let note = 0; note < nOuts; note += 1) {
2040
+ outputNoteCiphertexts.push(fields.slice(offset, offset + noteOutputLen));
2041
+ offset += noteOutputLen;
2169
2042
  }
2170
- /**
2171
- * Full `Transaction(20,2,2)` withdrawal proof: one real withdraw + dummies, using coin/state and depositor ECDH point (hex).
2172
- *
2173
- * Optional **partial public withdraw**: spend the full coin commitment `coin.value` (V), send `publicWithdrawStroops` (W) to the
2043
+ return { auditCiphertexts, outputNoteCiphertexts };
2044
+ }
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;
2063
+ }
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;
2080
+ }
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
+ };
2444
+ }
2445
+
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");
2463
+ }
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
+ };
2478
+ }
2479
+ class PrivacyPoolSDK {
2480
+ constructor(wasm, options, resolved) {
2481
+ this.wasm = wasm;
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;
2492
+ }
2493
+ /**
2494
+ * Initialize the SDK by loading WASM modules.
2495
+ *
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:
2499
+ * ```ts
2500
+ * const sdk = await PrivacyPoolSDK.init({
2501
+ * wasmBinary: await fetch('/pkg/client_sdk_wasm_bg.wasm').then(r => r.arrayBuffer()),
2502
+ * zkArtifactBaseUrl: 'https://github.com/Polynom-Labs/stellar-privacy-layer-contracts/releases/download/v0.8.0',
2503
+ * });
2504
+ * ```
2505
+ */
2506
+ static async init(options) {
2507
+ const opts = options ?? {};
2508
+ const wasm = await loadWasm(opts.wasmBinary);
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;
2513
+ }
2514
+ /**
2515
+ * Uniform 32-byte scalar as lowercase hex (Web Crypto). Same in browser and Node 19+.
2516
+ */
2517
+ static generateRandomScalarHex32() {
2518
+ return generateRandomScalarHex32();
2519
+ }
2520
+ /**
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).
2523
+ */
2524
+ static buildStealthAddressSignMessage(address, domain, nonce = DEFAULT_STEALTH_SIGN_NONCE) {
2525
+ return buildStealthAddressSignMessage(address, domain, nonce);
2526
+ }
2527
+ /**
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`).
2530
+ * @param assetHiDecimal / assetLoDecimal Decimal Fr strings for Stellar asset contract id (two limbs).
2531
+ */
2532
+ generateCoin(amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
2533
+ return generatedCoinFromWasm(this.wasm.generateCoin(coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2534
+ }
2535
+ /**
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`).
2538
+ */
2539
+ generateCoinWithOwnerPub(ownerPub, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
2540
+ return generatedCoinFromWasm(this.wasm.generateCoinWithOwnerPubHex(ownerPub.x, ownerPub.y, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2541
+ }
2542
+ /**
2543
+ * Coin for a depositor `ephemeralKeyScalar` (32-byte hex): `coin.secret = Poseidon255(1)(scalar)` as in `deposit.circom`.
2544
+ * @param amount Decimal field element (`bigint` | `string`).
2545
+ */
2546
+ generateCoinFromDepositEphemeralScalarHex(scalarHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
2547
+ return generatedCoinFromWasm(this.wasm.generateCoinFromDepositEphemeralScalarHex(scalarHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2548
+ }
2549
+ /**
2550
+ * Aligned deposit coin: `secret = Poseidon₁(scalar)` and owner pub from recipient hex coords.
2551
+ * @param amount Decimal field element (`bigint` | `string`).
2552
+ */
2553
+ generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, amount, assetHiDecimal, assetLoDecimal, applicationIdDecimal = "0") {
2554
+ return generatedCoinFromWasm(this.wasm.generateCoinForDepositWithOwnerPubHex(scalarHex, ownerXHex, ownerYHex, coinValueDecimal(amount), assetHiDecimal, assetLoDecimal, applicationIdDecimal));
2555
+ }
2556
+ /**
2557
+ * Merkle root, path, and coin fields for the first withdraw leg (Rust LeanIMT + Poseidon).
2558
+ */
2559
+ buildWithdrawMerkleWitness(coin, state) {
2560
+ const coinJson = JSON.stringify(coin);
2561
+ const stateJson = JSON.stringify(state);
2562
+ const resultJson = this.wasm.buildWithdrawMerkleWitness(coinJson, stateJson);
2563
+ return JSON.parse(resultJson);
2564
+ }
2565
+ /**
2566
+ * Full `Transaction(20,2,2)` withdrawal proof: one real withdraw + dummies, using coin/state and depositor ECDH point (hex).
2567
+ *
2568
+ * Optional **partial public withdraw**: spend the full coin commitment `coin.value` (V), send `publicWithdrawStroops` (W) to the
2174
2569
  * Stellar receiver, and re-deposit the remainder (V−W) as a new private note to `changeRecipientStealthAddress` (same circuit balance).
2175
2570
  */
2176
2571
  async proveWithdrawal(coin, state, params) {
2177
2572
  const witness = this.buildWithdrawMerkleWitness(coin, state);
2178
- const ownerPubHex = this.ecdhEphemeralPublicKeyFromScalarHex(BigInt(params.privKeyScalar).toString(16).padStart(64, '0'));
2179
- const w0 = withdrawObjectFromMerkleWitness(witness, ownerPubHex, params.applicationId ?? coin.application_id ?? '0', params.privKeyScalar);
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);
2180
2575
  const fullV = BigInt(coin.value);
2181
2576
  let publicWithdrawals;
2182
2577
  let deposits;
2183
2578
  if (params.publicWithdrawStroops !== undefined) {
2184
2579
  const W = params.publicWithdrawStroops;
2185
2580
  if (params.changeRecipientStealthAddress === undefined) {
2186
- throw new Error('proveWithdrawal: changeRecipientStealthAddress is required when publicWithdrawStroops is set');
2581
+ throw new Error("proveWithdrawal: changeRecipientStealthAddress is required when publicWithdrawStroops is set");
2187
2582
  }
2188
2583
  if (W <= 0n || W >= fullV) {
2189
- throw new Error('proveWithdrawal: publicWithdrawStroops must be strictly between 0 and coin.value');
2584
+ throw new Error("proveWithdrawal: publicWithdrawStroops must be strictly between 0 and coin.value");
2190
2585
  }
2191
2586
  const change = fullV - W;
2192
2587
  publicWithdrawals = [W.toString()];
@@ -2195,29 +2590,29 @@ class PrivacyPoolSDK {
2195
2590
  nullifier: randomFrDecimal(),
2196
2591
  ephemeralKeyScalar: randomFrDecimal253(),
2197
2592
  asset: [coin.asset_hi, coin.asset_lo],
2198
- applicationId: params.applicationId ?? '0',
2593
+ applicationId: params.applicationId ?? "0",
2199
2594
  recipientPublicKeys: recipientPublicKeysDecimalFromStealthAddress(params.changeRecipientStealthAddress),
2200
2595
  };
2201
- deposits = [changeDeposit, 'dummy'];
2596
+ deposits = [changeDeposit];
2202
2597
  }
2203
2598
  else {
2204
2599
  publicWithdrawals = [coin.value];
2205
- deposits = ['dummy', 'dummy'];
2600
+ deposits = [];
2206
2601
  }
2207
2602
  // On-chain `transact` sends `publicWithdrawals` / `publicWithdrawnAssets` to the withdraw account.
2208
2603
  const publicLegs = {
2209
2604
  publicWithdrawnAssets: [[coin.asset_hi, coin.asset_lo]],
2210
- publicDepositedAssets: [['0', '0']],
2211
- publicDeposits: ['0'],
2605
+ publicDepositedAssets: [["0", "0"]],
2606
+ publicDeposits: ["0"],
2212
2607
  publicWithdrawals,
2213
2608
  };
2214
- return this.proveTransaction({
2609
+ return this.proveTransactionSlots({
2215
2610
  stateRoot: witness.stateRoot,
2216
2611
  withdrawAddressHi: params.withdrawAddressHi,
2217
2612
  withdrawAddressLo: params.withdrawAddressLo,
2218
2613
  privKeyScalar: params.privKeyScalar,
2219
- }, publicLegs, [w0, 'dummy'], deposits, params.audit
2220
- ?? resolveTransactionAuditParams(params.applicationId ?? coin.application_id ?? DEFAULT_APPLICATION_ID, DEMO_AUDIT_PUBLIC_KEY));
2614
+ }, publicLegs, [w0], deposits, params.audit ??
2615
+ resolveTransactionAuditParams(params.applicationId ?? coin.application_id ?? DEFAULT_APPLICATION_ID, DEMO_AUDIT_PUBLIC_KEY, this.layout.nAuditSlots));
2221
2616
  }
2222
2617
  /**
2223
2618
  * Convert a snarkjs proof JSON to hex bytes for Soroban.
@@ -2242,23 +2637,33 @@ class PrivacyPoolSDK {
2242
2637
  return this.wasm.ecdhSharedKey(scalarHex, recipientPubXHex, recipientPubYHex);
2243
2638
  }
2244
2639
  /**
2245
- * `Transaction(20,2,2)` proof from high-level legs: maps to witness input (incl. `"dummy"` ECDH via WASM), then Groth16 → Soroban hex.
2246
- *
2247
- * @param publicParams Public inputs: `stateRoot`, `withdrawAddressHi` / `withdrawAddressLo`, `privKeyScalar` (decimal field strings).
2248
- * @param publicLegs Public token legs (`publicWithdrawnAssets`, `publicDepositedAssets`, `publicDeposits`, `publicWithdrawals`).
2249
- * @param withdraws Exactly two withdraw slots (`WithdrawObject` or `"dummy"`).
2250
- * @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).
2251
2642
  */
2252
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) {
2253
2647
  const wasmEcdh = {
2254
2648
  ecdhEphemeralPublicKeyFromScalarHex: (h) => this.wasm.ecdhEphemeralPublicKeyFromScalarHex(h),
2255
2649
  };
2256
- const witnessInput = buildTransactionWitnessInput(publicParams, publicLegs, withdraws, deposits, audit, wasmEcdh);
2257
- const wtns = await generateWitness(witnessInput, this.options.circuitWasm);
2258
- const { proof, publicSignals } = await generateProof(wtns, this.options.zkey);
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
+ });
2259
2662
  return {
2260
- proof_hex: this.proofToHex(proof),
2261
- public_hex: this.publicToHex(publicSignals),
2663
+ proof_hex: proved.proof_hex,
2664
+ public_hex: proved.public_hex,
2665
+ ciphertext_hex: encrypted.ciphertextHex,
2666
+ output_note_ephemeral_scalars: witnessInput.depositedEphemeralKeyScalars,
2262
2667
  };
2263
2668
  }
2264
2669
  /**
@@ -2297,6 +2702,11 @@ class PrivacyPoolSDK {
2297
2702
  }
2298
2703
  }
2299
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
+
2300
2710
  /** Pool Merkle tree depth (matches `coin::TREE_DEPTH` / `Transaction` circuit). */
2301
2711
  const POOL_MERKLE_TREE_DEPTH = 20;
2302
2712
  /** Default coin value in stroops (1 XLM); matches Rust `coin::COIN_VALUE`. */
@@ -2904,14 +3314,14 @@ const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(),
2904
3314
 
2905
3315
  const KYT_PASSAGE_AUTH_DOMAIN = 'privacy-pool-kyt-passage-v1';
2906
3316
  const PUBLIC_LEG_CONTEXT_DOMAIN = 'privacy-pool-public-leg-context-v1';
2907
- const PASSAGE_ID_DOMAIN = 'privacy-pool-passage-id-v2';
3317
+ const PASSAGE_ID_DOMAIN = 'privacy-pool-passage-id-v3';
3318
+ const PASSAGE_ID_DOMAIN_V2 = 'privacy-pool-passage-id-v2';
2908
3319
  const PASSAGE_ID_DOMAIN_V1 = 'privacy-pool-passage-id-v1';
2909
3320
  const NOTE_AUDIT_LEN = 12;
2910
3321
  const TOTAL_PUBLIC_SIGNALS = 93;
2911
3322
  const NOTE_AUDIT_PLAINTEXT_LEN = 12;
2912
3323
  const FIELD_BYTES = 32;
2913
3324
  const LENGTH_PREFIX_BYTES = 4;
2914
- const STATE_ROOT_SIGNAL_INDEX = 82;
2915
3325
  function sha256Hex(data) {
2916
3326
  return bytesToHex(sha256(data));
2917
3327
  }
@@ -2921,14 +3331,10 @@ function publicSignalsBuffer(publicSignalsBytes) {
2921
3331
  : publicSignalsBytes;
2922
3332
  }
2923
3333
  function expectedSignalCountForNonce(zkConfigNonce = 0n) {
2924
- if (zkConfigNonce !== 0n) {
2925
- throw new Error(`unknown zk config nonce ${zkConfigNonce.toString()}`);
2926
- }
2927
- return TOTAL_PUBLIC_SIGNALS;
3334
+ return totalPublicSignals(layoutForKnownNonce(zkConfigNonce));
2928
3335
  }
2929
3336
  function stateRootIndexForNonce(zkConfigNonce = 0n) {
2930
- expectedSignalCountForNonce(zkConfigNonce);
2931
- return STATE_ROOT_SIGNAL_INDEX;
3337
+ return stateRootIndex(layoutForKnownNonce(zkConfigNonce));
2932
3338
  }
2933
3339
  function parsePublicSignalsBytes(publicSignalsBytes, zkConfigNonce = 0n) {
2934
3340
  const expectedCount = expectedSignalCountForNonce(zkConfigNonce);
@@ -3066,9 +3472,19 @@ function hashPublicLegContext(layout, input) {
3066
3472
  }
3067
3473
  return sha256Hex(Buffer.concat(parts));
3068
3474
  }
3069
- function derivePassageId(owner, nonce, publicSignalHash, publicLegContextHash) {
3475
+ function derivePassageId(owner, nonce, publicSignalHash, publicLegContextHash, ciphertextHash) {
3476
+ const parts = [];
3477
+ appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN, 'utf8'));
3478
+ appendAddressStrkey(parts, owner);
3479
+ appendU64BE(parts, nonce);
3480
+ appendBytes32(parts, publicSignalHash);
3481
+ appendBytes32(parts, publicLegContextHash);
3482
+ appendBytes32(parts, ciphertextHash);
3483
+ return sha256Hex(Buffer.concat(parts));
3484
+ }
3485
+ function derivePassageIdV2(owner, nonce, publicSignalHash, publicLegContextHash) {
3070
3486
  const parts = [];
3071
- appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN, 'utf8'));
3487
+ appendBytes(parts, Buffer.from(PASSAGE_ID_DOMAIN_V2, 'utf8'));
3072
3488
  appendAddressStrkey(parts, owner);
3073
3489
  appendU64BE(parts, nonce);
3074
3490
  appendBytes32(parts, publicSignalHash);
@@ -3106,27 +3522,90 @@ function derivePassageFromTransactContext(input) {
3106
3522
  const nonce = BigInt(input.nonce ?? 0);
3107
3523
  const publicSignalHash = hashPublicSignalBytes(input.publicSignalsBytes, nonce);
3108
3524
  const publicLegContextHash = hashPublicLegContext(layout, input.publicLegContext);
3109
- const passageId = derivePassageId(input.owner, nonce, publicSignalHash, publicLegContextHash);
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)));
3110
3529
  return {
3111
3530
  publicSignalHash,
3112
3531
  publicLegContextHash,
3113
3532
  passageId,
3114
3533
  };
3115
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
+ }
3116
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
+ };
3117
3596
  const response = await fetch(`${backendUrl.replace(/\/$/, '')}/api/kyt/passages/inspect`, {
3118
3597
  method: 'POST',
3119
3598
  headers: { 'content-type': 'application/json' },
3120
- body: JSON.stringify(request),
3599
+ body: JSON.stringify(body),
3121
3600
  });
3122
3601
  if (!response.ok) {
3123
3602
  throw new Error(`KYT inspect failed: ${response.status} ${await response.text()}`);
3124
3603
  }
3125
- const body = await response.json();
3126
- if (body.status !== 'approved') {
3127
- throw new Error(`KYT inspect rejected: ${JSON.stringify(body)}`);
3604
+ const payload = await response.json();
3605
+ if (payload.status !== 'approved') {
3606
+ throw new Error(`KYT inspect rejected: ${JSON.stringify(payload)}`);
3128
3607
  }
3129
- return body;
3608
+ return payload;
3130
3609
  }
3131
3610
  function signatureBase64ToBytes(signature) {
3132
3611
  const value = signature.trim();
@@ -3143,6 +3622,7 @@ var kytPassage = /*#__PURE__*/Object.freeze({
3143
3622
  NOTE_AUDIT_PLAINTEXT_LEN: NOTE_AUDIT_PLAINTEXT_LEN,
3144
3623
  PASSAGE_ID_DOMAIN: PASSAGE_ID_DOMAIN,
3145
3624
  PASSAGE_ID_DOMAIN_V1: PASSAGE_ID_DOMAIN_V1,
3625
+ PASSAGE_ID_DOMAIN_V2: PASSAGE_ID_DOMAIN_V2,
3146
3626
  PUBLIC_LEG_CONTEXT_DOMAIN: PUBLIC_LEG_CONTEXT_DOMAIN,
3147
3627
  TOTAL_PUBLIC_SIGNALS: TOTAL_PUBLIC_SIGNALS,
3148
3628
  addressToCanonicalXdr: addressToCanonicalXdr,
@@ -3150,11 +3630,15 @@ var kytPassage = /*#__PURE__*/Object.freeze({
3150
3630
  derivePassageFromTransactContext: derivePassageFromTransactContext,
3151
3631
  derivePassageId: derivePassageId,
3152
3632
  derivePassageIdV1: derivePassageIdV1,
3633
+ derivePassageIdV2: derivePassageIdV2,
3634
+ hashCiphertextBytes: hashCiphertextBytes,
3153
3635
  hashPublicLegContext: hashPublicLegContext,
3154
3636
  hashPublicSignalBytes: hashPublicSignalBytes,
3155
3637
  inspectKytPassage: inspectKytPassage,
3156
3638
  signatureBase64ToBytes: signatureBase64ToBytes,
3157
- zeroAddressSentinelBuffer: zeroAddressSentinelBuffer
3639
+ spliceBindingTagsIntoPublicSignals: spliceBindingTagsIntoPublicSignals,
3640
+ zeroAddressSentinelBuffer: zeroAddressSentinelBuffer,
3641
+ zeroBindingTagsInPublicSignals: zeroBindingTagsInPublicSignals
3158
3642
  });
3159
3643
 
3160
3644
  function addressToScVal(address) {
@@ -3196,6 +3680,7 @@ function poolTransactOperation(params) {
3196
3680
  nativeToScVal(params.nonce, { type: 'u64' }),
3197
3681
  xdr.ScVal.scvBytes(params.proofBytes),
3198
3682
  xdr.ScVal.scvBytes(params.publicSignalsBytes),
3683
+ xdr.ScVal.scvBytes(params.ciphertextBytes ?? Buffer.alloc(0)),
3199
3684
  kytAuthorizationOptionToScVal(params.kytAuthorization),
3200
3685
  ],
3201
3686
  });
@@ -3340,6 +3825,7 @@ async function registerKytPassage(params) {
3340
3825
  owner: params.owner,
3341
3826
  nonce: params.nonce,
3342
3827
  publicSignalsBytes: params.publicSignalsBytes,
3828
+ ciphertextBytes: params.ciphertextBytes,
3343
3829
  publicLegContext: params.publicLegContext,
3344
3830
  });
3345
3831
  await submitTransaction({
@@ -3389,6 +3875,7 @@ async function submitWithKytPassage(params) {
3389
3875
  owner: params.owner,
3390
3876
  nonce: params.nonce,
3391
3877
  publicSignalsBytes: params.publicSignalsBytes,
3878
+ ciphertextBytes: params.ciphertextBytes,
3392
3879
  publicLegContext: params.publicLegContext,
3393
3880
  });
3394
3881
  if (approved.passageId.replace(/^0x/, '') !== derivation.passageId) {
@@ -3465,259 +3952,5 @@ async function sampleDerivedEscrowKey(recipientHi, recipientLo, startNonce = 1n,
3465
3952
  throw new Error(OUT_OF_RANGE_MESSAGE);
3466
3953
  }
3467
3954
 
3468
- const NOTE_OUTPUT_LEN = 6;
3469
- const OUTPUT_NOTE_TAG_DOMAIN = 3n;
3470
- const STREAM_DOMAIN = 1n;
3471
- const FR_SIZE = 32;
3472
- const FR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
3473
- const ONE = 1n;
3474
- const ZERO = 0n;
3475
- const BABYJUB_SCALAR_BITS = 253n;
3476
- const SCALAR_253_MASK = (ONE << BABYJUB_SCALAR_BITS) - ONE;
3477
- let primitivesPromise;
3478
- function primitives() {
3479
- primitivesPromise ?? (primitivesPromise = Promise.all([
3480
- buildBabyjub(),
3481
- buildMimc7(),
3482
- buildPoseidon(),
3483
- ]).then(([babyJub, mimc7, poseidon]) => ({ babyJub, mimc7, poseidon })));
3484
- return primitivesPromise;
3485
- }
3486
- function normalizeHex(value) {
3487
- return value.trim().replace(/^0x/i, '').padStart(FR_SIZE * 2, '0');
3488
- }
3489
- function bigintFromHex(value) {
3490
- return BigInt(`0x${normalizeHex(value)}`);
3491
- }
3492
- function bigintFromDecimal(value) {
3493
- const parsed = BigInt(value);
3494
- return parsed % FR_MODULUS;
3495
- }
3496
- function bigintToFrHex(value) {
3497
- let v = moduleFr(value);
3498
- const buf = Buffer.alloc(FR_SIZE);
3499
- for (let index = FR_SIZE - 1; index >= 0; index -= 1) {
3500
- buf[index] = Number(v & 0xffn);
3501
- v >>= 8n;
3502
- }
3503
- return buf.toString('hex');
3504
- }
3505
- function frDecimalToHex(value) {
3506
- return bigintToFrHex(bigintFromDecimal(value));
3507
- }
3508
- function frDecimalFromBigint(value) {
3509
- const reduced = value >= ZERO ? value % FR_MODULUS : (value % FR_MODULUS) + FR_MODULUS;
3510
- return reduced.toString(10);
3511
- }
3512
- function fieldBigint(field, value) {
3513
- return BigInt(field.toString(value));
3514
- }
3515
- function moduleFr(value) {
3516
- const reduced = value % FR_MODULUS;
3517
- return reduced >= ZERO ? reduced : reduced + FR_MODULUS;
3518
- }
3519
- function mimcHash(parameters) {
3520
- return fieldBigint(parameters.mimc7.F, parameters.mimc7.hash(parameters.mimc7.F.e(parameters.message), parameters.mimc7.F.e(parameters.key)));
3521
- }
3522
- function poseidonHash(poseidon, inputs) {
3523
- return fieldBigint(poseidon.F, poseidon(inputs.map((input) => poseidon.F.e(input))));
3524
- }
3525
- function deriveEncCipherKey(poseidon, sharedX) {
3526
- return poseidonHash(poseidon, [DOM_ENC, sharedX]);
3527
- }
3528
- function sharedKeyFromDepositor(parameters) {
3529
- const scalarHex = frDecimalToHex(parameters.ephemeralKeyScalarDecimal);
3530
- const shared = parameters.ecdhShared(scalarHex, frDecimalToHex(parameters.recipientPublicKey[0]), frDecimalToHex(parameters.recipientPublicKey[1]));
3531
- return [bigintFromHex(shared.x), bigintFromHex(shared.y)];
3532
- }
3533
- function sharedKeyFromRecipient(parameters) {
3534
- const { babyJub, recipientScalarHex, ephemeralPublicKey } = parameters;
3535
- const point = [
3536
- babyJub.F.e(bigintFromHex(ephemeralPublicKey[0])),
3537
- babyJub.F.e(bigintFromHex(ephemeralPublicKey[1])),
3538
- ];
3539
- if (!babyJub.inCurve(point)) {
3540
- throw new Error('Created ephemeral public key is not on BabyJubJub curve');
3541
- }
3542
- const product = babyJub.mulPointEscalar(point, bigintFromHex(recipientScalarHex) & SCALAR_253_MASK);
3543
- return [
3544
- fieldBigint(babyJub.F, product[0]),
3545
- fieldBigint(babyJub.F, product[1]),
3546
- ];
3547
- }
3548
- function outputNoteTag(parameters) {
3549
- let accumulator = poseidonHash(parameters.poseidon, [
3550
- parameters.nonce,
3551
- OUTPUT_NOTE_TAG_DOMAIN,
3552
- BigInt(parameters.plaintext.length),
3553
- ]);
3554
- for (const field of parameters.plaintext) {
3555
- accumulator = poseidonHash(parameters.poseidon, [accumulator, field]);
3556
- }
3557
- return mimcHash({
3558
- mimc7: parameters.mimc7,
3559
- message: accumulator,
3560
- key: parameters.key,
3561
- });
3562
- }
3563
- function encryptFields(parameters) {
3564
- return parameters.plaintext.map((field, index) => {
3565
- const seed = poseidonHash(parameters.poseidon, [
3566
- parameters.nonce,
3567
- STREAM_DOMAIN,
3568
- BigInt(index),
3569
- ]);
3570
- const stream = mimcHash({
3571
- mimc7: parameters.mimc7,
3572
- message: seed,
3573
- key: parameters.key,
3574
- });
3575
- return moduleFr(field + stream);
3576
- });
3577
- }
3578
- function decryptFields(parameters) {
3579
- return parameters.ciphertext.map((field, index) => {
3580
- const seed = poseidonHash(parameters.poseidon, [
3581
- parameters.nonce,
3582
- STREAM_DOMAIN,
3583
- BigInt(index),
3584
- ]);
3585
- const stream = mimcHash({
3586
- mimc7: parameters.mimc7,
3587
- message: seed,
3588
- key: parameters.key,
3589
- });
3590
- return moduleFr(bigintFromHex(field) - stream);
3591
- });
3592
- }
3593
- function plaintextBigints(plaintext) {
3594
- return [
3595
- bigintFromDecimal(plaintext.value),
3596
- bigintFromDecimal(plaintext.assetHi),
3597
- bigintFromDecimal(plaintext.assetLo),
3598
- bigintFromDecimal(plaintext.nullifier),
3599
- bigintFromDecimal(plaintext.secret),
3600
- bigintFromDecimal(plaintext.applicationId),
3601
- ];
3602
- }
3603
- function plaintextFromBigints(fields) {
3604
- return {
3605
- value: frDecimalFromBigint(fields[0] ?? ZERO),
3606
- assetHi: frDecimalFromBigint(fields[1] ?? ZERO),
3607
- assetLo: frDecimalFromBigint(fields[2] ?? ZERO),
3608
- nullifier: frDecimalFromBigint(fields[3] ?? ZERO),
3609
- secret: frDecimalFromBigint(fields[4] ?? ZERO),
3610
- applicationId: frDecimalFromBigint(fields[5] ?? ZERO),
3611
- };
3612
- }
3613
- async function secretFromDepositEphemeralScalarDecimal(ephemeralKeyScalarDecimal) {
3614
- const { poseidon } = await primitives();
3615
- const secret = poseidonHash(poseidon, [bigintFromDecimal(ephemeralKeyScalarDecimal)]);
3616
- return frDecimalFromBigint(secret);
3617
- }
3618
- async function encryptOutputNoteForDeposit(parameters) {
3619
- const { babyJub, mimc7, poseidon } = await primitives();
3620
- const secret = await secretFromDepositEphemeralScalarDecimal(parameters.deposit.ephemeralKeyScalar);
3621
- const plaintext = {
3622
- value: parameters.deposit.value,
3623
- assetHi: parameters.deposit.asset[0],
3624
- assetLo: parameters.deposit.asset[1],
3625
- nullifier: parameters.deposit.nullifier,
3626
- secret,
3627
- applicationId: parameters.deposit.applicationId,
3628
- };
3629
- const [sharedX, sharedY] = sharedKeyFromDepositor({
3630
- babyJub,
3631
- ecdhShared: parameters.ecdhShared,
3632
- ephemeralKeyScalarDecimal: parameters.deposit.ephemeralKeyScalar,
3633
- recipientPublicKey: parameters.deposit.recipientPublicKeys,
3634
- });
3635
- const key = deriveEncCipherKey(poseidon, sharedX);
3636
- const nonce = sharedY;
3637
- const message = plaintextBigints(plaintext);
3638
- const ciphertext = encryptFields({ mimc7, poseidon, key, nonce, plaintext: message });
3639
- const tag = outputNoteTag({ mimc7, poseidon, key, nonce, plaintext: message });
3640
- return {
3641
- ciphertext: ciphertext.map((field) => frDecimalFromBigint(field)),
3642
- tag: frDecimalFromBigint(tag),
3643
- };
3644
- }
3645
- function ownerPubFromRecipientScalar(babyJub, recipientScalarHex) {
3646
- const product = babyJub.mulPointEscalar(babyJub.Base8, bigintFromHex(recipientScalarHex) & SCALAR_253_MASK);
3647
- return [
3648
- fieldBigint(babyJub.F, product[0]),
3649
- fieldBigint(babyJub.F, product[1]),
3650
- ];
3651
- }
3652
- function commitmentFromPlaintextAndOwnerPub(parameters) {
3653
- const ownerKeyHash = poseidonHash(parameters.poseidon, [
3654
- parameters.ownerPub[0],
3655
- parameters.ownerPub[1],
3656
- ]);
3657
- const precommitment = poseidonHash(parameters.poseidon, [
3658
- bigintFromDecimal(parameters.plaintext.secret),
3659
- parameters.ownerMode,
3660
- ownerKeyHash,
3661
- ]);
3662
- const balance = poseidonHash(parameters.poseidon, [
3663
- bigintFromDecimal(parameters.plaintext.value),
3664
- bigintFromDecimal(parameters.plaintext.assetHi),
3665
- bigintFromDecimal(parameters.plaintext.assetLo),
3666
- ]);
3667
- const appCommitment = poseidonHash(parameters.poseidon, [
3668
- bigintFromDecimal(parameters.plaintext.applicationId),
3669
- balance,
3670
- precommitment,
3671
- ]);
3672
- const commitment = poseidonHash(parameters.poseidon, [
3673
- bigintFromDecimal(parameters.plaintext.nullifier),
3674
- appCommitment,
3675
- ]);
3676
- return normalizeHex(commitment.toString(16));
3677
- }
3678
- async function decryptOutputNoteEvent(input) {
3679
- const { babyJub, mimc7, poseidon } = await primitives();
3680
- const [sharedX, sharedY] = sharedKeyFromRecipient({
3681
- babyJub,
3682
- recipientScalarHex: input.recipientScalarHex,
3683
- ephemeralPublicKey: input.createdEphemeralKey,
3684
- });
3685
- const key = deriveEncCipherKey(poseidon, sharedX);
3686
- const nonce = sharedY;
3687
- const decrypted = decryptFields({
3688
- mimc7,
3689
- poseidon,
3690
- key,
3691
- nonce,
3692
- ciphertext: input.ciphertext,
3693
- });
3694
- const expectedTag = outputNoteTag({
3695
- mimc7,
3696
- poseidon,
3697
- key,
3698
- nonce,
3699
- plaintext: decrypted,
3700
- });
3701
- if (expectedTag !== bigintFromHex(input.tag)) {
3702
- throw new Error('Output note tag verification failed');
3703
- }
3704
- const plaintext = plaintextFromBigints(decrypted);
3705
- const commitmentHex = commitmentFromPlaintextAndOwnerPub({
3706
- poseidon,
3707
- plaintext,
3708
- ownerPub: ownerPubFromRecipientScalar(babyJub, input.recipientScalarHex),
3709
- ownerMode: input.ownerMode ?? 0n,
3710
- });
3711
- const commitmentMatches = normalizeHex(input.commitmentHashHex) === commitmentHex;
3712
- if (!commitmentMatches) {
3713
- throw new Error('Output note commitment checksum mismatch');
3714
- }
3715
- return {
3716
- ...plaintext,
3717
- commitmentHex,
3718
- commitmentMatches,
3719
- };
3720
- }
3721
-
3722
- export { BABYJUB_SUBGROUP_ORDER, 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, DOM_ENC, DOM_ESCROW, DOM_NULLIFIER, DOM_PADDING, NOTE_AUDIT_LEN as KYT_NOTE_AUDIT_LEN, NOTE_AUDIT_LEN$1 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, SPEND_SCALAR_DOMAIN_TAG, STEALTH_ADDRESS_HRP, TOTAL_PUBLIC_SIGNALS, TRANSACTION_N_AUDIT_SLOTS, TRANSACTION_N_INS, TRANSACTION_N_OUTS, TRANSACTION_TREE_DEPTH, addressToCanonicalXdr, buildKytPassageAuthorization, buildStealthAddressSignMessage, buildTransactionWitnessInput, buildUniformAuditParams, canonicalBabyJubScalarFromInteger, coordHexToDecimal, decodeDecodedEphemeralKey, decodeDepositorSharedSecretPreimage, decodeStealthAddress, decryptOutputNoteEvent, derivePassageFromTransactContext, derivePassageId, derivePassageIdV1, derivedEscrowKey, ed25519PubkeyPayloadHexToWithdrawFrDecimals, encodeDecodedEphemeralKey, encodeDepositorSharedSecretPreimage, encodeStealthAddress, encryptOutputNoteForDeposit, generateRandomScalarHex32, generateWitness, hashPublicLegContext, hashPublicSignalBytes, inspectKytPassage, localSignKytPassageAuthorization, planKytSubmit, privKeyScalarDecimalFromStellarSignature, randomFrDecimal, randomFrDecimal253, recipientPublicKeysDecimalFromStealthAddress, registerKytPassage, resolveAuditPublicKeyFromEnv, resolveDepositsForWitness, resolveSlotApplicationIds, resolveSpendScalarSchemaVersion, resolveTransactionAuditParams, sampleDerivedEscrowKey, scalarHexToFrDecimal, secretFromDepositEphemeralScalarDecimal, sharedSecretFromDepositorPreimage, sharedSecretFromRecipientPreimage, spendScalarHexFromStellarSignature, stellarContractAddressToAssetFrDecimals, submitApprovedKytPassage, submitPoolTransact, submitWithKytPassage, withApplicationIdOnDeposit, withApplicationIdOnWithdraw, withdrawObjectFromMerkleWitness, zeroAddressSentinelBuffer };
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 };
3723
3956
  //# sourceMappingURL=index.mjs.map