@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.
- package/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +268 -238
- package/dist/browser.mjs +267 -237
- package/dist/index-CLgrUxac.d.mts +11340 -0
- package/dist/index-Dm5Sdjkd.d.ts +11340 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/proofs/browser.ts +324 -279
package/src/proofs/browser.ts
CHANGED
|
@@ -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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
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
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
attestation.
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
proof:
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
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(
|