@sip-protocol/sdk 0.6.6 → 0.6.9

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/browser.mjs CHANGED
@@ -263,7 +263,7 @@ import {
263
263
  walletRegistry,
264
264
  withSecureBuffer,
265
265
  withSecureBufferSync
266
- } from "./chunk-25QRORO4.mjs";
266
+ } from "./chunk-O33JCQFJ.mjs";
267
267
  import {
268
268
  fulfillment_proof_default,
269
269
  funding_proof_default,
@@ -305,6 +305,8 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
305
305
  validityBackend = null;
306
306
  fulfillmentNoir = null;
307
307
  fulfillmentBackend = null;
308
+ // Mutex for WASM operations (prevents BorrowMutError from concurrent access)
309
+ wasmMutex = Promise.resolve();
308
310
  // Worker instance (optional)
309
311
  worker = null;
310
312
  workerPending = /* @__PURE__ */ new Map();
@@ -604,57 +606,59 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
604
606
  if (!this.fundingNoir || !this.fundingBackend) {
605
607
  throw new ProofGenerationError("funding", "Funding circuit not initialized");
606
608
  }
607
- try {
608
- onProgress?.({
609
- stage: "witness",
610
- percent: 10,
611
- message: "Preparing witness inputs..."
612
- });
613
- const blindingField = this.bytesToField(params.blindingFactor);
614
- const witnessInputs = {
615
- minimum_required: `0x${params.minimumRequired.toString(16)}`,
616
- asset_id: `0x${this.assetIdToField(params.assetId)}`,
617
- balance: `0x${params.balance.toString(16)}`,
618
- blinding: blindingField
619
- };
620
- onProgress?.({
621
- stage: "witness",
622
- percent: 30,
623
- message: "Generating witness..."
624
- });
625
- const { witness, returnValue } = await this.fundingNoir.execute(witnessInputs);
626
- onProgress?.({
627
- stage: "proving",
628
- percent: 50,
629
- message: "Generating proof (this may take a moment)..."
630
- });
631
- const proofData = await this.fundingBackend.generateProof(witness);
632
- onProgress?.({
633
- stage: "complete",
634
- percent: 100,
635
- message: "Proof generated successfully"
636
- });
637
- const commitmentHashBytes = returnValue;
638
- const commitmentHashHex = bytesToHex(new Uint8Array(commitmentHashBytes));
639
- const publicInputs = [
640
- `0x${params.minimumRequired.toString(16).padStart(64, "0")}`,
641
- `0x${this.assetIdToField(params.assetId)}`,
642
- `0x${commitmentHashHex}`
643
- ];
644
- const proof = {
645
- type: "funding",
646
- proof: `0x${bytesToHex(proofData.proof)}`,
647
- publicInputs
648
- };
649
- return { proof, publicInputs };
650
- } catch (error) {
651
- const message = error instanceof Error ? error.message : String(error);
652
- throw new ProofGenerationError(
653
- "funding",
654
- `Failed to generate funding proof: ${message}`,
655
- error instanceof Error ? error : void 0
656
- );
657
- }
609
+ return this.acquireWasmLock(async () => {
610
+ try {
611
+ onProgress?.({
612
+ stage: "witness",
613
+ percent: 10,
614
+ message: "Preparing witness inputs..."
615
+ });
616
+ const blindingField = this.bytesToField(params.blindingFactor);
617
+ const witnessInputs = {
618
+ minimum_required: `0x${params.minimumRequired.toString(16)}`,
619
+ asset_id: `0x${this.assetIdToField(params.assetId)}`,
620
+ balance: `0x${params.balance.toString(16)}`,
621
+ blinding: blindingField
622
+ };
623
+ onProgress?.({
624
+ stage: "witness",
625
+ percent: 30,
626
+ message: "Generating witness..."
627
+ });
628
+ const { witness, returnValue } = await this.fundingNoir.execute(witnessInputs);
629
+ onProgress?.({
630
+ stage: "proving",
631
+ percent: 50,
632
+ message: "Generating proof (this may take a moment)..."
633
+ });
634
+ const proofData = await this.fundingBackend.generateProof(witness);
635
+ onProgress?.({
636
+ stage: "complete",
637
+ percent: 100,
638
+ message: "Proof generated successfully"
639
+ });
640
+ const commitmentHashBytes = returnValue;
641
+ const commitmentHashHex = bytesToHex(new Uint8Array(commitmentHashBytes));
642
+ const publicInputs = [
643
+ `0x${params.minimumRequired.toString(16).padStart(64, "0")}`,
644
+ `0x${this.assetIdToField(params.assetId)}`,
645
+ `0x${commitmentHashHex}`
646
+ ];
647
+ const proof = {
648
+ type: "funding",
649
+ proof: `0x${bytesToHex(proofData.proof)}`,
650
+ publicInputs
651
+ };
652
+ return { proof, publicInputs };
653
+ } catch (error) {
654
+ const message = error instanceof Error ? error.message : String(error);
655
+ throw new ProofGenerationError(
656
+ "funding",
657
+ `Failed to generate funding proof: ${message}`,
658
+ error instanceof Error ? error : void 0
659
+ );
660
+ }
661
+ });
658
662
  }
659
663
  /**
660
664
  * Generate a Validity Proof
@@ -666,89 +670,91 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
666
670
  if (!this.validityNoir || !this.validityBackend) {
667
671
  throw new ProofGenerationError("validity", "Validity circuit not initialized");
668
672
  }
669
- try {
670
- onProgress?.({
671
- stage: "witness",
672
- percent: 10,
673
- message: "Preparing validity witness..."
674
- });
675
- const intentHashField = this.hexToField(params.intentHash);
676
- const senderAddressField = this.hexToField(params.senderAddress);
677
- const senderBlindingField = this.bytesToField(params.senderBlinding);
678
- const senderSecretField = this.bytesToField(params.senderSecret);
679
- const nonceField = this.bytesToField(params.nonce);
680
- const { commitmentX, commitmentY } = await this.computeSenderCommitment(
681
- senderAddressField,
682
- senderBlindingField
683
- );
684
- const nullifier = await this.computeNullifier(senderSecretField, intentHashField, nonceField);
685
- const signature = Array.from(params.authorizationSignature);
686
- const messageHash = this.fieldToBytes32(intentHashField);
687
- let pubKeyX;
688
- let pubKeyY;
689
- if (params.senderPublicKey) {
690
- pubKeyX = Array.from(params.senderPublicKey.x);
691
- pubKeyY = Array.from(params.senderPublicKey.y);
692
- } else {
693
- const coords = this.getPublicKeyCoordinates(params.senderSecret);
694
- pubKeyX = coords.x;
695
- pubKeyY = coords.y;
673
+ return this.acquireWasmLock(async () => {
674
+ try {
675
+ onProgress?.({
676
+ stage: "witness",
677
+ percent: 10,
678
+ message: "Preparing validity witness..."
679
+ });
680
+ const intentHashField = this.hexToField(params.intentHash);
681
+ const senderAddressField = this.hexToField(params.senderAddress);
682
+ const senderBlindingField = this.bytesToField(params.senderBlinding);
683
+ const senderSecretField = this.bytesToField(params.senderSecret);
684
+ const nonceField = this.bytesToField(params.nonce);
685
+ const { commitmentX, commitmentY } = await this.computeSenderCommitment(
686
+ senderAddressField,
687
+ senderBlindingField
688
+ );
689
+ const nullifier = await this.computeNullifier(senderSecretField, intentHashField, nonceField);
690
+ const signature = Array.from(params.authorizationSignature);
691
+ const messageHash = this.fieldToBytes32(intentHashField);
692
+ let pubKeyX;
693
+ let pubKeyY;
694
+ if (params.senderPublicKey) {
695
+ pubKeyX = Array.from(params.senderPublicKey.x);
696
+ pubKeyY = Array.from(params.senderPublicKey.y);
697
+ } else {
698
+ const coords = this.getPublicKeyCoordinates(params.senderSecret);
699
+ pubKeyX = coords.x;
700
+ pubKeyY = coords.y;
701
+ }
702
+ const witnessInputs = {
703
+ intent_hash: intentHashField,
704
+ sender_commitment_x: commitmentX,
705
+ sender_commitment_y: commitmentY,
706
+ nullifier,
707
+ timestamp: params.timestamp.toString(),
708
+ expiry: params.expiry.toString(),
709
+ sender_address: senderAddressField,
710
+ sender_blinding: senderBlindingField,
711
+ sender_secret: senderSecretField,
712
+ pub_key_x: pubKeyX,
713
+ pub_key_y: pubKeyY,
714
+ signature,
715
+ message_hash: messageHash,
716
+ nonce: nonceField
717
+ };
718
+ onProgress?.({
719
+ stage: "witness",
720
+ percent: 30,
721
+ message: "Generating witness..."
722
+ });
723
+ const { witness } = await this.validityNoir.execute(witnessInputs);
724
+ onProgress?.({
725
+ stage: "proving",
726
+ percent: 50,
727
+ message: "Generating validity proof..."
728
+ });
729
+ const proofData = await this.validityBackend.generateProof(witness);
730
+ onProgress?.({
731
+ stage: "complete",
732
+ percent: 100,
733
+ message: "Validity proof generated"
734
+ });
735
+ const publicInputs = [
736
+ `0x${intentHashField}`,
737
+ `0x${commitmentX}`,
738
+ `0x${commitmentY}`,
739
+ `0x${nullifier}`,
740
+ `0x${params.timestamp.toString(16).padStart(16, "0")}`,
741
+ `0x${params.expiry.toString(16).padStart(16, "0")}`
742
+ ];
743
+ const proof = {
744
+ type: "validity",
745
+ proof: `0x${bytesToHex(proofData.proof)}`,
746
+ publicInputs
747
+ };
748
+ return { proof, publicInputs };
749
+ } catch (error) {
750
+ const message = error instanceof Error ? error.message : String(error);
751
+ throw new ProofGenerationError(
752
+ "validity",
753
+ `Failed to generate validity proof: ${message}`,
754
+ error instanceof Error ? error : void 0
755
+ );
696
756
  }
697
- const witnessInputs = {
698
- intent_hash: intentHashField,
699
- sender_commitment_x: commitmentX,
700
- sender_commitment_y: commitmentY,
701
- nullifier,
702
- timestamp: params.timestamp.toString(),
703
- expiry: params.expiry.toString(),
704
- sender_address: senderAddressField,
705
- sender_blinding: senderBlindingField,
706
- sender_secret: senderSecretField,
707
- pub_key_x: pubKeyX,
708
- pub_key_y: pubKeyY,
709
- signature,
710
- message_hash: messageHash,
711
- nonce: nonceField
712
- };
713
- onProgress?.({
714
- stage: "witness",
715
- percent: 30,
716
- message: "Generating witness..."
717
- });
718
- const { witness } = await this.validityNoir.execute(witnessInputs);
719
- onProgress?.({
720
- stage: "proving",
721
- percent: 50,
722
- message: "Generating validity proof..."
723
- });
724
- const proofData = await this.validityBackend.generateProof(witness);
725
- onProgress?.({
726
- stage: "complete",
727
- percent: 100,
728
- message: "Validity proof generated"
729
- });
730
- const publicInputs = [
731
- `0x${intentHashField}`,
732
- `0x${commitmentX}`,
733
- `0x${commitmentY}`,
734
- `0x${nullifier}`,
735
- `0x${params.timestamp.toString(16).padStart(16, "0")}`,
736
- `0x${params.expiry.toString(16).padStart(16, "0")}`
737
- ];
738
- const proof = {
739
- type: "validity",
740
- proof: `0x${bytesToHex(proofData.proof)}`,
741
- publicInputs
742
- };
743
- return { proof, publicInputs };
744
- } catch (error) {
745
- const message = error instanceof Error ? error.message : String(error);
746
- throw new ProofGenerationError(
747
- "validity",
748
- `Failed to generate validity proof: ${message}`,
749
- error instanceof Error ? error : void 0
750
- );
751
- }
757
+ });
752
758
  }
753
759
  /**
754
760
  * Generate a Fulfillment Proof
@@ -760,95 +766,97 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
760
766
  if (!this.fulfillmentNoir || !this.fulfillmentBackend) {
761
767
  throw new ProofGenerationError("fulfillment", "Fulfillment circuit not initialized");
762
768
  }
763
- try {
764
- onProgress?.({
765
- stage: "witness",
766
- percent: 10,
767
- message: "Preparing fulfillment witness..."
768
- });
769
- const intentHashField = this.hexToField(params.intentHash);
770
- const recipientStealthField = this.hexToField(params.recipientStealth);
771
- const { commitmentX, commitmentY } = await this.computeOutputCommitment(
772
- params.outputAmount,
773
- params.outputBlinding
774
- );
775
- const solverSecretField = this.bytesToField(params.solverSecret);
776
- const solverId = await this.computeSolverId(solverSecretField);
777
- const outputBlindingField = this.bytesToField(params.outputBlinding);
778
- const attestation = params.oracleAttestation;
779
- const attestationRecipientField = this.hexToField(attestation.recipient);
780
- const attestationTxHashField = this.hexToField(attestation.txHash);
781
- const oracleSignature = Array.from(attestation.signature);
782
- const oracleMessageHash = await this.computeOracleMessageHash(
783
- attestation.recipient,
784
- attestation.amount,
785
- attestation.txHash,
786
- attestation.blockNumber
787
- );
788
- const oraclePubKeyX = this.config.oraclePublicKey?.x ?? new Array(32).fill(0);
789
- const oraclePubKeyY = this.config.oraclePublicKey?.y ?? new Array(32).fill(0);
790
- const witnessInputs = {
791
- intent_hash: intentHashField,
792
- output_commitment_x: commitmentX,
793
- output_commitment_y: commitmentY,
794
- recipient_stealth: recipientStealthField,
795
- min_output_amount: params.minOutputAmount.toString(),
796
- solver_id: solverId,
797
- fulfillment_time: params.fulfillmentTime.toString(),
798
- expiry: params.expiry.toString(),
799
- output_amount: params.outputAmount.toString(),
800
- output_blinding: outputBlindingField,
801
- solver_secret: solverSecretField,
802
- attestation_recipient: attestationRecipientField,
803
- attestation_amount: attestation.amount.toString(),
804
- attestation_tx_hash: attestationTxHashField,
805
- attestation_block: attestation.blockNumber.toString(),
806
- oracle_signature: oracleSignature,
807
- oracle_message_hash: oracleMessageHash,
808
- oracle_pub_key_x: oraclePubKeyX,
809
- oracle_pub_key_y: oraclePubKeyY
810
- };
811
- onProgress?.({
812
- stage: "witness",
813
- percent: 30,
814
- message: "Generating witness..."
815
- });
816
- const { witness } = await this.fulfillmentNoir.execute(witnessInputs);
817
- onProgress?.({
818
- stage: "proving",
819
- percent: 50,
820
- message: "Generating fulfillment proof..."
821
- });
822
- const proofData = await this.fulfillmentBackend.generateProof(witness);
823
- onProgress?.({
824
- stage: "complete",
825
- percent: 100,
826
- message: "Fulfillment proof generated"
827
- });
828
- const publicInputs = [
829
- `0x${intentHashField}`,
830
- `0x${commitmentX}`,
831
- `0x${commitmentY}`,
832
- `0x${recipientStealthField}`,
833
- `0x${params.minOutputAmount.toString(16).padStart(16, "0")}`,
834
- `0x${solverId}`,
835
- `0x${params.fulfillmentTime.toString(16).padStart(16, "0")}`,
836
- `0x${params.expiry.toString(16).padStart(16, "0")}`
837
- ];
838
- const proof = {
839
- type: "fulfillment",
840
- proof: `0x${bytesToHex(proofData.proof)}`,
841
- publicInputs
842
- };
843
- return { proof, publicInputs };
844
- } catch (error) {
845
- const message = error instanceof Error ? error.message : String(error);
846
- throw new ProofGenerationError(
847
- "fulfillment",
848
- `Failed to generate fulfillment proof: ${message}`,
849
- error instanceof Error ? error : void 0
850
- );
851
- }
769
+ return this.acquireWasmLock(async () => {
770
+ try {
771
+ onProgress?.({
772
+ stage: "witness",
773
+ percent: 10,
774
+ message: "Preparing fulfillment witness..."
775
+ });
776
+ const intentHashField = this.hexToField(params.intentHash);
777
+ const recipientStealthField = this.hexToField(params.recipientStealth);
778
+ const { commitmentX, commitmentY } = await this.computeOutputCommitment(
779
+ params.outputAmount,
780
+ params.outputBlinding
781
+ );
782
+ const solverSecretField = this.bytesToField(params.solverSecret);
783
+ const solverId = await this.computeSolverId(solverSecretField);
784
+ const outputBlindingField = this.bytesToField(params.outputBlinding);
785
+ const attestation = params.oracleAttestation;
786
+ const attestationRecipientField = this.hexToField(attestation.recipient);
787
+ const attestationTxHashField = this.hexToField(attestation.txHash);
788
+ const oracleSignature = Array.from(attestation.signature);
789
+ const oracleMessageHash = await this.computeOracleMessageHash(
790
+ attestation.recipient,
791
+ attestation.amount,
792
+ attestation.txHash,
793
+ attestation.blockNumber
794
+ );
795
+ const oraclePubKeyX = this.config.oraclePublicKey?.x ?? new Array(32).fill(0);
796
+ const oraclePubKeyY = this.config.oraclePublicKey?.y ?? new Array(32).fill(0);
797
+ const witnessInputs = {
798
+ intent_hash: intentHashField,
799
+ output_commitment_x: commitmentX,
800
+ output_commitment_y: commitmentY,
801
+ recipient_stealth: recipientStealthField,
802
+ min_output_amount: params.minOutputAmount.toString(),
803
+ solver_id: solverId,
804
+ fulfillment_time: params.fulfillmentTime.toString(),
805
+ expiry: params.expiry.toString(),
806
+ output_amount: params.outputAmount.toString(),
807
+ output_blinding: outputBlindingField,
808
+ solver_secret: solverSecretField,
809
+ attestation_recipient: attestationRecipientField,
810
+ attestation_amount: attestation.amount.toString(),
811
+ attestation_tx_hash: attestationTxHashField,
812
+ attestation_block: attestation.blockNumber.toString(),
813
+ oracle_signature: oracleSignature,
814
+ oracle_message_hash: oracleMessageHash,
815
+ oracle_pub_key_x: oraclePubKeyX,
816
+ oracle_pub_key_y: oraclePubKeyY
817
+ };
818
+ onProgress?.({
819
+ stage: "witness",
820
+ percent: 30,
821
+ message: "Generating witness..."
822
+ });
823
+ const { witness } = await this.fulfillmentNoir.execute(witnessInputs);
824
+ onProgress?.({
825
+ stage: "proving",
826
+ percent: 50,
827
+ message: "Generating fulfillment proof..."
828
+ });
829
+ const proofData = await this.fulfillmentBackend.generateProof(witness);
830
+ onProgress?.({
831
+ stage: "complete",
832
+ percent: 100,
833
+ message: "Fulfillment proof generated"
834
+ });
835
+ const publicInputs = [
836
+ `0x${intentHashField}`,
837
+ `0x${commitmentX}`,
838
+ `0x${commitmentY}`,
839
+ `0x${recipientStealthField}`,
840
+ `0x${params.minOutputAmount.toString(16).padStart(16, "0")}`,
841
+ `0x${solverId}`,
842
+ `0x${params.fulfillmentTime.toString(16).padStart(16, "0")}`,
843
+ `0x${params.expiry.toString(16).padStart(16, "0")}`
844
+ ];
845
+ const proof = {
846
+ type: "fulfillment",
847
+ proof: `0x${bytesToHex(proofData.proof)}`,
848
+ publicInputs
849
+ };
850
+ return { proof, publicInputs };
851
+ } catch (error) {
852
+ const message = error instanceof Error ? error.message : String(error);
853
+ throw new ProofGenerationError(
854
+ "fulfillment",
855
+ `Failed to generate fulfillment proof: ${message}`,
856
+ error instanceof Error ? error : void 0
857
+ );
858
+ }
859
+ });
852
860
  }
853
861
  /**
854
862
  * Verify a proof
@@ -875,22 +883,25 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
875
883
  "SIP_4004" /* PROOF_PROVIDER_NOT_READY */
876
884
  );
877
885
  }
878
- try {
879
- const proofHex = proof.proof.startsWith("0x") ? proof.proof.slice(2) : proof.proof;
880
- const proofBytes = hexToBytes(proofHex);
881
- const isValid = await backend.verifyProof({
882
- proof: proofBytes,
883
- publicInputs: proof.publicInputs.map(
884
- (input) => input.startsWith("0x") ? input.slice(2) : input
885
- )
886
- });
887
- return isValid;
888
- } catch (error) {
889
- if (this.config.verbose) {
890
- console.error("[BrowserNoirProvider] Verification error:", error);
886
+ const backendToUse = backend;
887
+ return this.acquireWasmLock(async () => {
888
+ try {
889
+ const proofHex = proof.proof.startsWith("0x") ? proof.proof.slice(2) : proof.proof;
890
+ const proofBytes = hexToBytes(proofHex);
891
+ const isValid = await backendToUse.verifyProof({
892
+ proof: proofBytes,
893
+ publicInputs: proof.publicInputs.map(
894
+ (input) => input.startsWith("0x") ? input.slice(2) : input
895
+ )
896
+ });
897
+ return isValid;
898
+ } catch (error) {
899
+ if (this.config.verbose) {
900
+ console.error("[BrowserNoirProvider] Verification error:", error);
901
+ }
902
+ return false;
891
903
  }
892
- return false;
893
- }
904
+ });
894
905
  }
895
906
  /**
896
907
  * Destroy the provider and free resources
@@ -918,6 +929,25 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
918
929
  this._isReady = false;
919
930
  }
920
931
  // ─── Private Utility Methods ────────────────────────────────────────────────
932
+ /**
933
+ * Acquire WASM mutex lock for exclusive access
934
+ *
935
+ * The ACVM WASM module uses Rust RefCell internally which panics on
936
+ * concurrent mutable borrows. This mutex ensures serial execution.
937
+ */
938
+ async acquireWasmLock(operation) {
939
+ const previousLock = this.wasmMutex;
940
+ let releaseLock;
941
+ this.wasmMutex = new Promise((resolve) => {
942
+ releaseLock = resolve;
943
+ });
944
+ try {
945
+ await previousLock;
946
+ return await operation();
947
+ } finally {
948
+ releaseLock();
949
+ }
950
+ }
921
951
  ensureReady() {
922
952
  if (!this._isReady) {
923
953
  throw new ProofError(