@sip-protocol/sdk 0.6.5 → 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 +277 -245
- package/dist/browser.mjs +274 -242
- package/dist/chunk-25QRORO4.mjs +17031 -0
- package/dist/chunk-3INS3PR5.mjs +884 -0
- 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/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/proofs/noir.js +6 -6
- package/dist/proofs/noir.mjs +5 -5
- package/package.json +1 -1
- package/src/proofs/browser.ts +324 -277
- package/src/proofs/circuits/funding_proof.json +1 -1
- package/src/proofs/mock.ts +2 -2
- package/src/proofs/noir.ts +7 -5
- package/src/proofs/worker.ts +5 -3
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,74 +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
|
-
minimum_required:
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
percent: 30,
|
|
580
|
-
message: 'Generating witness...',
|
|
581
|
-
})
|
|
582
|
-
|
|
583
|
-
// Generate witness - circuit returns commitment hash as [u8; 32]
|
|
584
|
-
const { witness, returnValue } = await this.fundingNoir.execute(witnessInputs)
|
|
585
|
-
|
|
586
|
-
onProgress?.({
|
|
587
|
-
stage: 'proving',
|
|
588
|
-
percent: 50,
|
|
589
|
-
message: 'Generating proof (this may take a moment)...',
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
// Generate proof
|
|
593
|
-
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
|
+
}
|
|
594
582
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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
|
+
}
|
|
600
624
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
`0x${commitmentHashHex}`,
|
|
610
|
-
]
|
|
611
|
-
|
|
612
|
-
const proof: ZKProof = {
|
|
613
|
-
type: 'funding',
|
|
614
|
-
proof: `0x${bytesToHex(proofData.proof)}`,
|
|
615
|
-
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
|
+
)
|
|
616
633
|
}
|
|
617
|
-
|
|
618
|
-
return { proof, publicInputs }
|
|
619
|
-
} catch (error) {
|
|
620
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
621
|
-
throw new ProofGenerationError(
|
|
622
|
-
'funding',
|
|
623
|
-
`Failed to generate funding proof: ${message}`,
|
|
624
|
-
error instanceof Error ? error : undefined
|
|
625
|
-
)
|
|
626
|
-
}
|
|
634
|
+
})
|
|
627
635
|
}
|
|
628
636
|
|
|
629
637
|
/**
|
|
@@ -641,105 +649,108 @@ export class BrowserNoirProvider implements ProofProvider {
|
|
|
641
649
|
throw new ProofGenerationError('validity', 'Validity circuit not initialized')
|
|
642
650
|
}
|
|
643
651
|
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
intent_hash: intentHashField,
|
|
682
|
-
sender_commitment_x: commitmentX,
|
|
683
|
-
sender_commitment_y: commitmentY,
|
|
684
|
-
nullifier: nullifier,
|
|
685
|
-
timestamp: params.timestamp.toString(),
|
|
686
|
-
expiry: params.expiry.toString(),
|
|
687
|
-
sender_address: senderAddressField,
|
|
688
|
-
sender_blinding: senderBlindingField,
|
|
689
|
-
sender_secret: senderSecretField,
|
|
690
|
-
pub_key_x: pubKeyX,
|
|
691
|
-
pub_key_y: pubKeyY,
|
|
692
|
-
signature: signature,
|
|
693
|
-
message_hash: messageHash,
|
|
694
|
-
nonce: nonceField,
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
onProgress?.({
|
|
698
|
-
stage: 'witness',
|
|
699
|
-
percent: 30,
|
|
700
|
-
message: 'Generating witness...',
|
|
701
|
-
})
|
|
702
|
-
|
|
703
|
-
const { witness } = await this.validityNoir.execute(witnessInputs)
|
|
704
|
-
|
|
705
|
-
onProgress?.({
|
|
706
|
-
stage: 'proving',
|
|
707
|
-
percent: 50,
|
|
708
|
-
message: 'Generating validity proof...',
|
|
709
|
-
})
|
|
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
|
+
}
|
|
710
689
|
|
|
711
|
-
|
|
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
|
+
}
|
|
712
706
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
+
}
|
|
718
743
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const proof: ZKProof = {
|
|
729
|
-
type: 'validity',
|
|
730
|
-
proof: `0x${bytesToHex(proofData.proof)}`,
|
|
731
|
-
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
|
+
)
|
|
732
752
|
}
|
|
733
|
-
|
|
734
|
-
return { proof, publicInputs }
|
|
735
|
-
} catch (error) {
|
|
736
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
737
|
-
throw new ProofGenerationError(
|
|
738
|
-
'validity',
|
|
739
|
-
`Failed to generate validity proof: ${message}`,
|
|
740
|
-
error instanceof Error ? error : undefined
|
|
741
|
-
)
|
|
742
|
-
}
|
|
753
|
+
})
|
|
743
754
|
}
|
|
744
755
|
|
|
745
756
|
/**
|
|
@@ -757,109 +768,112 @@ export class BrowserNoirProvider implements ProofProvider {
|
|
|
757
768
|
throw new ProofGenerationError('fulfillment', 'Fulfillment circuit not initialized')
|
|
758
769
|
}
|
|
759
770
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
attestation.
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
stage: 'witness',
|
|
817
|
-
percent: 30,
|
|
818
|
-
message: 'Generating witness...',
|
|
819
|
-
})
|
|
820
|
-
|
|
821
|
-
const { witness } = await this.fulfillmentNoir.execute(witnessInputs)
|
|
822
|
-
|
|
823
|
-
onProgress?.({
|
|
824
|
-
stage: 'proving',
|
|
825
|
-
percent: 50,
|
|
826
|
-
message: 'Generating fulfillment proof...',
|
|
827
|
-
})
|
|
828
|
-
|
|
829
|
-
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
|
+
}
|
|
830
827
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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
|
+
}
|
|
836
866
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
`0x${params.expiry.toString(16).padStart(16, '0')}`,
|
|
846
|
-
]
|
|
847
|
-
|
|
848
|
-
const proof: ZKProof = {
|
|
849
|
-
type: 'fulfillment',
|
|
850
|
-
proof: `0x${bytesToHex(proofData.proof)}`,
|
|
851
|
-
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
|
+
)
|
|
852
875
|
}
|
|
853
|
-
|
|
854
|
-
return { proof, publicInputs }
|
|
855
|
-
} catch (error) {
|
|
856
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
857
|
-
throw new ProofGenerationError(
|
|
858
|
-
'fulfillment',
|
|
859
|
-
`Failed to generate fulfillment proof: ${message}`,
|
|
860
|
-
error instanceof Error ? error : undefined
|
|
861
|
-
)
|
|
862
|
-
}
|
|
876
|
+
})
|
|
863
877
|
}
|
|
864
878
|
|
|
865
879
|
/**
|
|
@@ -891,24 +905,29 @@ export class BrowserNoirProvider implements ProofProvider {
|
|
|
891
905
|
)
|
|
892
906
|
}
|
|
893
907
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
proof:
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
|
909
929
|
}
|
|
910
|
-
|
|
911
|
-
}
|
|
930
|
+
})
|
|
912
931
|
}
|
|
913
932
|
|
|
914
933
|
/**
|
|
@@ -939,6 +958,34 @@ export class BrowserNoirProvider implements ProofProvider {
|
|
|
939
958
|
|
|
940
959
|
// ─── Private Utility Methods ────────────────────────────────────────────────
|
|
941
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
|
+
|
|
942
989
|
private ensureReady(): void {
|
|
943
990
|
if (!this._isReady) {
|
|
944
991
|
throw new ProofError(
|