@sip-protocol/sdk 0.6.6 → 0.6.8

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.
@@ -156,6 +156,9 @@ export class BrowserNoirProvider implements ProofProvider {
156
156
  private fulfillmentNoir: Noir | null = null
157
157
  private fulfillmentBackend: UltraHonkBackend | null = null
158
158
 
159
+ // Mutex for WASM operations (prevents BorrowMutError from concurrent access)
160
+ private wasmMutex: Promise<void> = Promise.resolve()
161
+
159
162
  // Worker instance (optional)
160
163
  private worker: Worker | null = null
161
164
  private workerPending: Map<
@@ -556,76 +559,79 @@ export class BrowserNoirProvider implements ProofProvider {
556
559
  throw new ProofGenerationError('funding', 'Funding circuit not initialized')
557
560
  }
558
561
 
559
- try {
560
- onProgress?.({
561
- stage: 'witness',
562
- percent: 10,
563
- message: 'Preparing witness inputs...',
564
- })
565
-
566
- // Convert blinding factor to field element
567
- const blindingField = this.bytesToField(params.blindingFactor)
568
-
569
- // Circuit signature: (minimum_required: pub Field, asset_id: pub Field, balance: Field, blinding: Field) -> [u8; 32]
570
- // Using Field type for unlimited precision (handles NEAR's 24 decimals, etc.)
571
- const witnessInputs = {
572
- minimum_required: `0x${params.minimumRequired.toString(16)}`,
573
- asset_id: `0x${this.assetIdToField(params.assetId)}`,
574
- balance: `0x${params.balance.toString(16)}`,
575
- blinding: blindingField,
576
- }
577
-
578
- onProgress?.({
579
- stage: 'witness',
580
- percent: 30,
581
- message: 'Generating witness...',
582
- })
583
-
584
- // Generate witness - circuit returns commitment hash as [u8; 32]
585
- const { witness, returnValue } = await this.fundingNoir.execute(witnessInputs)
586
-
587
- onProgress?.({
588
- stage: 'proving',
589
- percent: 50,
590
- message: 'Generating proof (this may take a moment)...',
591
- })
592
-
593
- // Generate proof
594
- const proofData = await this.fundingBackend.generateProof(witness)
562
+ // Use mutex to prevent concurrent WASM access (causes BorrowMutError)
563
+ return this.acquireWasmLock(async () => {
564
+ try {
565
+ onProgress?.({
566
+ stage: 'witness',
567
+ percent: 10,
568
+ message: 'Preparing witness inputs...',
569
+ })
570
+
571
+ // Convert blinding factor to field element
572
+ const blindingField = this.bytesToField(params.blindingFactor)
573
+
574
+ // Circuit signature: (minimum_required: pub Field, asset_id: pub Field, balance: Field, blinding: Field) -> [u8; 32]
575
+ // Using Field type for unlimited precision (handles NEAR's 24 decimals, etc.)
576
+ const witnessInputs = {
577
+ minimum_required: `0x${params.minimumRequired.toString(16)}`,
578
+ asset_id: `0x${this.assetIdToField(params.assetId)}`,
579
+ balance: `0x${params.balance.toString(16)}`,
580
+ blinding: blindingField,
581
+ }
595
582
 
596
- onProgress?.({
597
- stage: 'complete',
598
- percent: 100,
599
- message: 'Proof generated successfully',
600
- })
583
+ onProgress?.({
584
+ stage: 'witness',
585
+ percent: 30,
586
+ message: 'Generating witness...',
587
+ })
588
+
589
+ // Generate witness - circuit returns commitment hash as [u8; 32]
590
+ const { witness, returnValue } = await this.fundingNoir!.execute(witnessInputs)
591
+
592
+ onProgress?.({
593
+ stage: 'proving',
594
+ percent: 50,
595
+ message: 'Generating proof (this may take a moment)...',
596
+ })
597
+
598
+ // Generate proof
599
+ const proofData = await this.fundingBackend!.generateProof(witness)
600
+
601
+ onProgress?.({
602
+ stage: 'complete',
603
+ percent: 100,
604
+ message: 'Proof generated successfully',
605
+ })
606
+
607
+ // Extract commitment hash from circuit return value
608
+ const commitmentHashBytes = returnValue as number[]
609
+ const commitmentHashHex = bytesToHex(new Uint8Array(commitmentHashBytes))
610
+
611
+ // Order: minimum_required, asset_id, commitment_hash (return value)
612
+ // Field values padded to 64 hex chars (32 bytes)
613
+ const publicInputs: `0x${string}`[] = [
614
+ `0x${params.minimumRequired.toString(16).padStart(64, '0')}`,
615
+ `0x${this.assetIdToField(params.assetId)}`,
616
+ `0x${commitmentHashHex}`,
617
+ ]
618
+
619
+ const proof: ZKProof = {
620
+ type: 'funding',
621
+ proof: `0x${bytesToHex(proofData.proof)}`,
622
+ publicInputs,
623
+ }
601
624
 
602
- // Extract commitment hash from circuit return value
603
- const commitmentHashBytes = returnValue as number[]
604
- const commitmentHashHex = bytesToHex(new Uint8Array(commitmentHashBytes))
605
-
606
- // Order: minimum_required, asset_id, commitment_hash (return value)
607
- // Field values padded to 64 hex chars (32 bytes)
608
- const publicInputs: `0x${string}`[] = [
609
- `0x${params.minimumRequired.toString(16).padStart(64, '0')}`,
610
- `0x${this.assetIdToField(params.assetId)}`,
611
- `0x${commitmentHashHex}`,
612
- ]
613
-
614
- const proof: ZKProof = {
615
- type: 'funding',
616
- proof: `0x${bytesToHex(proofData.proof)}`,
617
- publicInputs,
625
+ return { proof, publicInputs }
626
+ } catch (error) {
627
+ const message = error instanceof Error ? error.message : String(error)
628
+ throw new ProofGenerationError(
629
+ 'funding',
630
+ `Failed to generate funding proof: ${message}`,
631
+ error instanceof Error ? error : undefined
632
+ )
618
633
  }
619
-
620
- return { proof, publicInputs }
621
- } catch (error) {
622
- const message = error instanceof Error ? error.message : String(error)
623
- throw new ProofGenerationError(
624
- 'funding',
625
- `Failed to generate funding proof: ${message}`,
626
- error instanceof Error ? error : undefined
627
- )
628
- }
634
+ })
629
635
  }
630
636
 
631
637
  /**
@@ -643,105 +649,108 @@ export class BrowserNoirProvider implements ProofProvider {
643
649
  throw new ProofGenerationError('validity', 'Validity circuit not initialized')
644
650
  }
645
651
 
646
- try {
647
- onProgress?.({
648
- stage: 'witness',
649
- percent: 10,
650
- message: 'Preparing validity witness...',
651
- })
652
-
653
- // Convert inputs to field elements
654
- const intentHashField = this.hexToField(params.intentHash)
655
- const senderAddressField = this.hexToField(params.senderAddress)
656
- const senderBlindingField = this.bytesToField(params.senderBlinding)
657
- const senderSecretField = this.bytesToField(params.senderSecret)
658
- const nonceField = this.bytesToField(params.nonce)
659
-
660
- // Compute derived values
661
- const { commitmentX, commitmentY } = await this.computeSenderCommitment(
662
- senderAddressField,
663
- senderBlindingField
664
- )
665
- const nullifier = await this.computeNullifier(senderSecretField, intentHashField, nonceField)
666
-
667
- const signature = Array.from(params.authorizationSignature)
668
- const messageHash = this.fieldToBytes32(intentHashField)
669
-
670
- // Get public key
671
- let pubKeyX: number[]
672
- let pubKeyY: number[]
673
- if (params.senderPublicKey) {
674
- pubKeyX = Array.from(params.senderPublicKey.x)
675
- pubKeyY = Array.from(params.senderPublicKey.y)
676
- } else {
677
- const coords = this.getPublicKeyCoordinates(params.senderSecret)
678
- pubKeyX = coords.x
679
- pubKeyY = coords.y
680
- }
681
-
682
- const witnessInputs = {
683
- intent_hash: intentHashField,
684
- sender_commitment_x: commitmentX,
685
- sender_commitment_y: commitmentY,
686
- nullifier: nullifier,
687
- timestamp: params.timestamp.toString(),
688
- expiry: params.expiry.toString(),
689
- sender_address: senderAddressField,
690
- sender_blinding: senderBlindingField,
691
- sender_secret: senderSecretField,
692
- pub_key_x: pubKeyX,
693
- pub_key_y: pubKeyY,
694
- signature: signature,
695
- message_hash: messageHash,
696
- nonce: nonceField,
697
- }
698
-
699
- onProgress?.({
700
- stage: 'witness',
701
- percent: 30,
702
- message: 'Generating witness...',
703
- })
704
-
705
- const { witness } = await this.validityNoir.execute(witnessInputs)
706
-
707
- onProgress?.({
708
- stage: 'proving',
709
- percent: 50,
710
- message: 'Generating validity proof...',
711
- })
652
+ // Use mutex to prevent concurrent WASM access (causes BorrowMutError)
653
+ return this.acquireWasmLock(async () => {
654
+ try {
655
+ onProgress?.({
656
+ stage: 'witness',
657
+ percent: 10,
658
+ message: 'Preparing validity witness...',
659
+ })
660
+
661
+ // Convert inputs to field elements
662
+ const intentHashField = this.hexToField(params.intentHash)
663
+ const senderAddressField = this.hexToField(params.senderAddress)
664
+ const senderBlindingField = this.bytesToField(params.senderBlinding)
665
+ const senderSecretField = this.bytesToField(params.senderSecret)
666
+ const nonceField = this.bytesToField(params.nonce)
667
+
668
+ // Compute derived values
669
+ const { commitmentX, commitmentY } = await this.computeSenderCommitment(
670
+ senderAddressField,
671
+ senderBlindingField
672
+ )
673
+ const nullifier = await this.computeNullifier(senderSecretField, intentHashField, nonceField)
674
+
675
+ const signature = Array.from(params.authorizationSignature)
676
+ const messageHash = this.fieldToBytes32(intentHashField)
677
+
678
+ // Get public key
679
+ let pubKeyX: number[]
680
+ let pubKeyY: number[]
681
+ if (params.senderPublicKey) {
682
+ pubKeyX = Array.from(params.senderPublicKey.x)
683
+ pubKeyY = Array.from(params.senderPublicKey.y)
684
+ } else {
685
+ const coords = this.getPublicKeyCoordinates(params.senderSecret)
686
+ pubKeyX = coords.x
687
+ pubKeyY = coords.y
688
+ }
712
689
 
713
- const proofData = await this.validityBackend.generateProof(witness)
690
+ const witnessInputs = {
691
+ intent_hash: intentHashField,
692
+ sender_commitment_x: commitmentX,
693
+ sender_commitment_y: commitmentY,
694
+ nullifier: nullifier,
695
+ timestamp: params.timestamp.toString(),
696
+ expiry: params.expiry.toString(),
697
+ sender_address: senderAddressField,
698
+ sender_blinding: senderBlindingField,
699
+ sender_secret: senderSecretField,
700
+ pub_key_x: pubKeyX,
701
+ pub_key_y: pubKeyY,
702
+ signature: signature,
703
+ message_hash: messageHash,
704
+ nonce: nonceField,
705
+ }
714
706
 
715
- onProgress?.({
716
- stage: 'complete',
717
- percent: 100,
718
- message: 'Validity proof generated',
719
- })
707
+ onProgress?.({
708
+ stage: 'witness',
709
+ percent: 30,
710
+ message: 'Generating witness...',
711
+ })
712
+
713
+ const { witness } = await this.validityNoir!.execute(witnessInputs)
714
+
715
+ onProgress?.({
716
+ stage: 'proving',
717
+ percent: 50,
718
+ message: 'Generating validity proof...',
719
+ })
720
+
721
+ const proofData = await this.validityBackend!.generateProof(witness)
722
+
723
+ onProgress?.({
724
+ stage: 'complete',
725
+ percent: 100,
726
+ message: 'Validity proof generated',
727
+ })
728
+
729
+ const publicInputs: `0x${string}`[] = [
730
+ `0x${intentHashField}`,
731
+ `0x${commitmentX}`,
732
+ `0x${commitmentY}`,
733
+ `0x${nullifier}`,
734
+ `0x${params.timestamp.toString(16).padStart(16, '0')}`,
735
+ `0x${params.expiry.toString(16).padStart(16, '0')}`,
736
+ ]
737
+
738
+ const proof: ZKProof = {
739
+ type: 'validity',
740
+ proof: `0x${bytesToHex(proofData.proof)}`,
741
+ publicInputs,
742
+ }
720
743
 
721
- const publicInputs: `0x${string}`[] = [
722
- `0x${intentHashField}`,
723
- `0x${commitmentX}`,
724
- `0x${commitmentY}`,
725
- `0x${nullifier}`,
726
- `0x${params.timestamp.toString(16).padStart(16, '0')}`,
727
- `0x${params.expiry.toString(16).padStart(16, '0')}`,
728
- ]
729
-
730
- const proof: ZKProof = {
731
- type: 'validity',
732
- proof: `0x${bytesToHex(proofData.proof)}`,
733
- publicInputs,
744
+ return { proof, publicInputs }
745
+ } catch (error) {
746
+ const message = error instanceof Error ? error.message : String(error)
747
+ throw new ProofGenerationError(
748
+ 'validity',
749
+ `Failed to generate validity proof: ${message}`,
750
+ error instanceof Error ? error : undefined
751
+ )
734
752
  }
735
-
736
- return { proof, publicInputs }
737
- } catch (error) {
738
- const message = error instanceof Error ? error.message : String(error)
739
- throw new ProofGenerationError(
740
- 'validity',
741
- `Failed to generate validity proof: ${message}`,
742
- error instanceof Error ? error : undefined
743
- )
744
- }
753
+ })
745
754
  }
746
755
 
747
756
  /**
@@ -759,109 +768,112 @@ export class BrowserNoirProvider implements ProofProvider {
759
768
  throw new ProofGenerationError('fulfillment', 'Fulfillment circuit not initialized')
760
769
  }
761
770
 
762
- try {
763
- onProgress?.({
764
- stage: 'witness',
765
- percent: 10,
766
- message: 'Preparing fulfillment witness...',
767
- })
768
-
769
- const intentHashField = this.hexToField(params.intentHash)
770
- const recipientStealthField = this.hexToField(params.recipientStealth)
771
-
772
- const { commitmentX, commitmentY } = await this.computeOutputCommitment(
773
- params.outputAmount,
774
- params.outputBlinding
775
- )
776
-
777
- const solverSecretField = this.bytesToField(params.solverSecret)
778
- const solverId = await this.computeSolverId(solverSecretField)
779
- const outputBlindingField = this.bytesToField(params.outputBlinding)
780
-
781
- const attestation = params.oracleAttestation
782
- const attestationRecipientField = this.hexToField(attestation.recipient)
783
- const attestationTxHashField = this.hexToField(attestation.txHash)
784
- const oracleSignature = Array.from(attestation.signature)
785
- const oracleMessageHash = await this.computeOracleMessageHash(
786
- attestation.recipient,
787
- attestation.amount,
788
- attestation.txHash,
789
- attestation.blockNumber
790
- )
791
-
792
- const oraclePubKeyX = this.config.oraclePublicKey?.x ?? new Array(32).fill(0)
793
- const oraclePubKeyY = this.config.oraclePublicKey?.y ?? new Array(32).fill(0)
794
-
795
- const witnessInputs = {
796
- intent_hash: intentHashField,
797
- output_commitment_x: commitmentX,
798
- output_commitment_y: commitmentY,
799
- recipient_stealth: recipientStealthField,
800
- min_output_amount: params.minOutputAmount.toString(),
801
- solver_id: solverId,
802
- fulfillment_time: params.fulfillmentTime.toString(),
803
- expiry: params.expiry.toString(),
804
- output_amount: params.outputAmount.toString(),
805
- output_blinding: outputBlindingField,
806
- solver_secret: solverSecretField,
807
- attestation_recipient: attestationRecipientField,
808
- attestation_amount: attestation.amount.toString(),
809
- attestation_tx_hash: attestationTxHashField,
810
- attestation_block: attestation.blockNumber.toString(),
811
- oracle_signature: oracleSignature,
812
- oracle_message_hash: oracleMessageHash,
813
- oracle_pub_key_x: oraclePubKeyX,
814
- oracle_pub_key_y: oraclePubKeyY,
815
- }
816
-
817
- onProgress?.({
818
- stage: 'witness',
819
- percent: 30,
820
- message: 'Generating witness...',
821
- })
822
-
823
- const { witness } = await this.fulfillmentNoir.execute(witnessInputs)
824
-
825
- onProgress?.({
826
- stage: 'proving',
827
- percent: 50,
828
- message: 'Generating fulfillment proof...',
829
- })
830
-
831
- const proofData = await this.fulfillmentBackend.generateProof(witness)
771
+ // Use mutex to prevent concurrent WASM access (causes BorrowMutError)
772
+ return this.acquireWasmLock(async () => {
773
+ try {
774
+ onProgress?.({
775
+ stage: 'witness',
776
+ percent: 10,
777
+ message: 'Preparing fulfillment witness...',
778
+ })
779
+
780
+ const intentHashField = this.hexToField(params.intentHash)
781
+ const recipientStealthField = this.hexToField(params.recipientStealth)
782
+
783
+ const { commitmentX, commitmentY } = await this.computeOutputCommitment(
784
+ params.outputAmount,
785
+ params.outputBlinding
786
+ )
787
+
788
+ const solverSecretField = this.bytesToField(params.solverSecret)
789
+ const solverId = await this.computeSolverId(solverSecretField)
790
+ const outputBlindingField = this.bytesToField(params.outputBlinding)
791
+
792
+ const attestation = params.oracleAttestation
793
+ const attestationRecipientField = this.hexToField(attestation.recipient)
794
+ const attestationTxHashField = this.hexToField(attestation.txHash)
795
+ const oracleSignature = Array.from(attestation.signature)
796
+ const oracleMessageHash = await this.computeOracleMessageHash(
797
+ attestation.recipient,
798
+ attestation.amount,
799
+ attestation.txHash,
800
+ attestation.blockNumber
801
+ )
802
+
803
+ const oraclePubKeyX = this.config.oraclePublicKey?.x ?? new Array(32).fill(0)
804
+ const oraclePubKeyY = this.config.oraclePublicKey?.y ?? new Array(32).fill(0)
805
+
806
+ const witnessInputs = {
807
+ intent_hash: intentHashField,
808
+ output_commitment_x: commitmentX,
809
+ output_commitment_y: commitmentY,
810
+ recipient_stealth: recipientStealthField,
811
+ min_output_amount: params.minOutputAmount.toString(),
812
+ solver_id: solverId,
813
+ fulfillment_time: params.fulfillmentTime.toString(),
814
+ expiry: params.expiry.toString(),
815
+ output_amount: params.outputAmount.toString(),
816
+ output_blinding: outputBlindingField,
817
+ solver_secret: solverSecretField,
818
+ attestation_recipient: attestationRecipientField,
819
+ attestation_amount: attestation.amount.toString(),
820
+ attestation_tx_hash: attestationTxHashField,
821
+ attestation_block: attestation.blockNumber.toString(),
822
+ oracle_signature: oracleSignature,
823
+ oracle_message_hash: oracleMessageHash,
824
+ oracle_pub_key_x: oraclePubKeyX,
825
+ oracle_pub_key_y: oraclePubKeyY,
826
+ }
832
827
 
833
- onProgress?.({
834
- stage: 'complete',
835
- percent: 100,
836
- message: 'Fulfillment proof generated',
837
- })
828
+ onProgress?.({
829
+ stage: 'witness',
830
+ percent: 30,
831
+ message: 'Generating witness...',
832
+ })
833
+
834
+ const { witness } = await this.fulfillmentNoir!.execute(witnessInputs)
835
+
836
+ onProgress?.({
837
+ stage: 'proving',
838
+ percent: 50,
839
+ message: 'Generating fulfillment proof...',
840
+ })
841
+
842
+ const proofData = await this.fulfillmentBackend!.generateProof(witness)
843
+
844
+ onProgress?.({
845
+ stage: 'complete',
846
+ percent: 100,
847
+ message: 'Fulfillment proof generated',
848
+ })
849
+
850
+ const publicInputs: `0x${string}`[] = [
851
+ `0x${intentHashField}`,
852
+ `0x${commitmentX}`,
853
+ `0x${commitmentY}`,
854
+ `0x${recipientStealthField}`,
855
+ `0x${params.minOutputAmount.toString(16).padStart(16, '0')}`,
856
+ `0x${solverId}`,
857
+ `0x${params.fulfillmentTime.toString(16).padStart(16, '0')}`,
858
+ `0x${params.expiry.toString(16).padStart(16, '0')}`,
859
+ ]
860
+
861
+ const proof: ZKProof = {
862
+ type: 'fulfillment',
863
+ proof: `0x${bytesToHex(proofData.proof)}`,
864
+ publicInputs,
865
+ }
838
866
 
839
- const publicInputs: `0x${string}`[] = [
840
- `0x${intentHashField}`,
841
- `0x${commitmentX}`,
842
- `0x${commitmentY}`,
843
- `0x${recipientStealthField}`,
844
- `0x${params.minOutputAmount.toString(16).padStart(16, '0')}`,
845
- `0x${solverId}`,
846
- `0x${params.fulfillmentTime.toString(16).padStart(16, '0')}`,
847
- `0x${params.expiry.toString(16).padStart(16, '0')}`,
848
- ]
849
-
850
- const proof: ZKProof = {
851
- type: 'fulfillment',
852
- proof: `0x${bytesToHex(proofData.proof)}`,
853
- publicInputs,
867
+ return { proof, publicInputs }
868
+ } catch (error) {
869
+ const message = error instanceof Error ? error.message : String(error)
870
+ throw new ProofGenerationError(
871
+ 'fulfillment',
872
+ `Failed to generate fulfillment proof: ${message}`,
873
+ error instanceof Error ? error : undefined
874
+ )
854
875
  }
855
-
856
- return { proof, publicInputs }
857
- } catch (error) {
858
- const message = error instanceof Error ? error.message : String(error)
859
- throw new ProofGenerationError(
860
- 'fulfillment',
861
- `Failed to generate fulfillment proof: ${message}`,
862
- error instanceof Error ? error : undefined
863
- )
864
- }
876
+ })
865
877
  }
866
878
 
867
879
  /**
@@ -893,24 +905,29 @@ export class BrowserNoirProvider implements ProofProvider {
893
905
  )
894
906
  }
895
907
 
896
- try {
897
- const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
898
- const proofBytes = hexToBytes(proofHex)
899
-
900
- const isValid = await backend.verifyProof({
901
- proof: proofBytes,
902
- publicInputs: proof.publicInputs.map((input) =>
903
- input.startsWith('0x') ? input.slice(2) : input
904
- ),
905
- })
906
-
907
- return isValid
908
- } catch (error) {
909
- if (this.config.verbose) {
910
- console.error('[BrowserNoirProvider] Verification error:', error)
908
+ const backendToUse = backend
909
+
910
+ // Use mutex to prevent concurrent WASM access (causes BorrowMutError)
911
+ return this.acquireWasmLock(async () => {
912
+ try {
913
+ const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
914
+ const proofBytes = hexToBytes(proofHex)
915
+
916
+ const isValid = await backendToUse.verifyProof({
917
+ proof: proofBytes,
918
+ publicInputs: proof.publicInputs.map((input) =>
919
+ input.startsWith('0x') ? input.slice(2) : input
920
+ ),
921
+ })
922
+
923
+ return isValid
924
+ } catch (error) {
925
+ if (this.config.verbose) {
926
+ console.error('[BrowserNoirProvider] Verification error:', error)
927
+ }
928
+ return false
911
929
  }
912
- return false
913
- }
930
+ })
914
931
  }
915
932
 
916
933
  /**
@@ -941,6 +958,34 @@ export class BrowserNoirProvider implements ProofProvider {
941
958
 
942
959
  // ─── Private Utility Methods ────────────────────────────────────────────────
943
960
 
961
+ /**
962
+ * Acquire WASM mutex lock for exclusive access
963
+ *
964
+ * The ACVM WASM module uses Rust RefCell internally which panics on
965
+ * concurrent mutable borrows. This mutex ensures serial execution.
966
+ */
967
+ private async acquireWasmLock<T>(operation: () => Promise<T>): Promise<T> {
968
+ // Wait for any pending operation to complete
969
+ const previousLock = this.wasmMutex
970
+
971
+ // Create a new lock that will be released when our operation completes
972
+ let releaseLock: () => void
973
+ this.wasmMutex = new Promise<void>((resolve) => {
974
+ releaseLock = resolve
975
+ })
976
+
977
+ try {
978
+ // Wait for previous operation
979
+ await previousLock
980
+
981
+ // Execute our operation
982
+ return await operation()
983
+ } finally {
984
+ // Release the lock
985
+ releaseLock!()
986
+ }
987
+ }
988
+
944
989
  private ensureReady(): void {
945
990
  if (!this._isReady) {
946
991
  throw new ProofError(