@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/ciphertext-blob.d.ts +17 -0
- package/dist/circuit-artifacts.d.ts +9 -0
- package/dist/cli.js +769 -591
- package/dist/cli.js.map +1 -1
- package/dist/domain-separators.d.ts +6 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.mjs +1122 -889
- package/dist/index.mjs.map +1 -1
- package/dist/kyt-flow.d.ts +2 -0
- package/dist/kyt-passage.d.ts +17 -3
- package/dist/output-note-encryption.d.ts +31 -2
- package/dist/prover.d.ts +3 -3
- package/dist/sdk.d.ts +22 -18
- package/dist/transaction-audit.d.ts +8 -12
- package/dist/types.d.ts +12 -4
- package/dist/withdrawal-transaction-input.d.ts +33 -42
- package/dist/witness.d.ts +2 -1
- package/dist/zk-artifact-url.d.ts +4 -0
- package/dist/zk-layout.d.ts +84 -0
- package/package.json +4 -9
- package/pkg/client_sdk_wasm.d.ts +6 -0
- package/pkg/client_sdk_wasm.js +39 -0
- package/pkg/client_sdk_wasm_bg.wasm +0 -0
- package/pkg/client_sdk_wasm_bg.wasm.d.ts +1 -0
- package/assets/main.wasm +0 -0
- package/assets/main_final.zkey +0 -0
- package/assets/witness_calculator.js +0 -383
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$
|
|
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$
|
|
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
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
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
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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 (
|
|
1113
|
-
|
|
1114
|
-
while (i>0) {
|
|
1115
|
-
res.unshift(0);
|
|
1116
|
-
i--;
|
|
1117
|
-
}
|
|
978
|
+
if (nonce === COMMITMENT_V2_ZK_NONCE) {
|
|
979
|
+
return standardLayout();
|
|
1118
980
|
}
|
|
1119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
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
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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
|
-
|
|
1223
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1808
|
-
|
|
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
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
outputApplicationIds:
|
|
1832
|
-
|
|
1833
|
-
|
|
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$
|
|
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
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
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:
|
|
2010
|
-
ownerPubs:
|
|
2011
|
-
paddingRandoms:
|
|
2012
|
-
withdrawnValues:
|
|
2013
|
-
withdrawnNullifiers:
|
|
2014
|
-
withdrawnSecrets:
|
|
2015
|
-
withdrawnAssets:
|
|
2016
|
-
withdrawnEscrowNonces:
|
|
2017
|
-
inputRecipientStellar:
|
|
2018
|
-
stateSiblings:
|
|
2019
|
-
stateIndex:
|
|
2020
|
-
depositedValues:
|
|
2021
|
-
depositedNullifiers:
|
|
2022
|
-
depositedAssets:
|
|
2023
|
-
depositedEphemeralKeyScalars:
|
|
2024
|
-
depositedRecipientPublicKeys:
|
|
2025
|
-
depositedEscrowNonces:
|
|
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
|
-
|
|
2033
|
-
|
|
2034
|
-
publicWithdrawnAssets:
|
|
2035
|
-
publicDepositedAssets:
|
|
2036
|
-
publicDeposits:
|
|
2037
|
-
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
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
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
|
|
1977
|
+
return buf;
|
|
2082
1978
|
}
|
|
2083
|
-
function
|
|
2084
|
-
const
|
|
2085
|
-
|
|
2086
|
-
|
|
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
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
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
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
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
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
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
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
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
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
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
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
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
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
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,
|
|
2179
|
-
const w0 = withdrawObjectFromMerkleWitness(witness, ownerPubHex, params.applicationId ?? coin.application_id ??
|
|
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(
|
|
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(
|
|
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 ??
|
|
2593
|
+
applicationId: params.applicationId ?? "0",
|
|
2199
2594
|
recipientPublicKeys: recipientPublicKeysDecimalFromStealthAddress(params.changeRecipientStealthAddress),
|
|
2200
2595
|
};
|
|
2201
|
-
deposits = [changeDeposit
|
|
2596
|
+
deposits = [changeDeposit];
|
|
2202
2597
|
}
|
|
2203
2598
|
else {
|
|
2204
2599
|
publicWithdrawals = [coin.value];
|
|
2205
|
-
deposits = [
|
|
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: [[
|
|
2211
|
-
publicDeposits: [
|
|
2605
|
+
publicDepositedAssets: [["0", "0"]],
|
|
2606
|
+
publicDeposits: ["0"],
|
|
2212
2607
|
publicWithdrawals,
|
|
2213
2608
|
};
|
|
2214
|
-
return this.
|
|
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
|
|
2220
|
-
|
|
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,
|
|
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
|
|
2257
|
-
const
|
|
2258
|
-
const
|
|
2650
|
+
const { nIns, nOuts, publicNInputs, publicNOutputs } = this.layout;
|
|
2651
|
+
const witnessInput = buildTransactionWitnessInput(publicParams, publicLegs, withdraws, deposits, audit, wasmEcdh, { nIns, nOuts, publicNInputs, publicNOutputs });
|
|
2652
|
+
const artifacts = this.circuitArtifacts;
|
|
2653
|
+
if (!artifacts) {
|
|
2654
|
+
throw new Error("PrivacyPoolSDK circuit artifacts are not loaded");
|
|
2655
|
+
}
|
|
2656
|
+
const proved = this.wasm.proveGroth16(artifacts.graph, artifacts.provingKey, artifacts.r1cs, JSON.stringify(witnessInput));
|
|
2657
|
+
const encrypted = await encryptTransactionCiphertextBlob({
|
|
2658
|
+
witness: witnessInput,
|
|
2659
|
+
layout: this.layout,
|
|
2660
|
+
ecdhShared: (priv, pubX, pubY) => this.wasm.ecdhSharedKey(priv, pubX, pubY),
|
|
2661
|
+
});
|
|
2259
2662
|
return {
|
|
2260
|
-
proof_hex:
|
|
2261
|
-
public_hex:
|
|
2663
|
+
proof_hex: proved.proof_hex,
|
|
2664
|
+
public_hex: proved.public_hex,
|
|
2665
|
+
ciphertext_hex: encrypted.ciphertextHex,
|
|
2666
|
+
output_note_ephemeral_scalars: witnessInput.depositedEphemeralKeyScalars,
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
3126
|
-
if (
|
|
3127
|
-
throw new Error(`KYT inspect rejected: ${JSON.stringify(
|
|
3604
|
+
const payload = await response.json();
|
|
3605
|
+
if (payload.status !== 'approved') {
|
|
3606
|
+
throw new Error(`KYT inspect rejected: ${JSON.stringify(payload)}`);
|
|
3128
3607
|
}
|
|
3129
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|