1- // bitwrap WASM prover — client-side Groth16 proving
2- // Loads prover.wasm and exposes async API over bitwrapProver global
1+ // bitwrap WASM prover — runs Groth16 proving in a Web Worker to avoid blocking UI.
2+ // Falls back to main-thread execution if Workers are unavailable.
33
4- let _ready = null ;
4+ let _worker = null ;
5+ let _pending = { } ;
6+ let _msgId = 0 ;
57let _keyCache = { } ;
8+ let _initPromise = null ;
9+
10+ function sendWorkerMessage ( type , payload ) {
11+ return new Promise ( ( resolve , reject ) => {
12+ const id = ++ _msgId ;
13+ _pending [ id ] = { resolve, reject } ;
14+ _worker . postMessage ( { id, type, payload } ) ;
15+ } ) ;
16+ }
617
7- // Initialize the WASM prover. Call once, awaits loading.
8- export async function initProver ( wasmUrl = './prover.wasm' , execUrl = './wasm_exec.js' ) {
9- if ( _ready ) return _ready ;
18+ // Initialize the prover (Web Worker with WASM).
19+ export async function initProver ( ) {
20+ if ( _initPromise ) return _initPromise ;
21+
22+ _initPromise = ( async ( ) => {
23+ if ( typeof Worker !== 'undefined' ) {
24+ try {
25+ _worker = new Worker ( './prover-worker.js' ) ;
26+ _worker . onmessage = ( e ) => {
27+ const { id, type, result, error } = e . data ;
28+ if ( id && _pending [ id ] ) {
29+ if ( type === 'error' ) {
30+ _pending [ id ] . reject ( new Error ( error ) ) ;
31+ } else {
32+ _pending [ id ] . resolve ( result ) ;
33+ }
34+ delete _pending [ id ] ;
35+ }
36+ } ;
37+ _worker . onerror = ( e ) => {
38+ console . warn ( 'Prover worker error, falling back to main thread:' , e . message ) ;
39+ _worker = null ;
40+ } ;
41+ // Give the worker a moment to initialize
42+ await new Promise ( r => setTimeout ( r , 100 ) ) ;
43+ return ;
44+ } catch ( e ) {
45+ console . warn ( 'Worker creation failed, falling back to main thread:' , e ) ;
46+ _worker = null ;
47+ }
48+ }
1049
11- _ready = ( async ( ) => {
12- // Load wasm_exec.js if Go runtime not present
50+ // Fallback: load on main thread (blocks UI during prove)
1351 if ( typeof Go === 'undefined' ) {
1452 await new Promise ( ( resolve , reject ) => {
1553 const script = document . createElement ( 'script' ) ;
16- script . src = execUrl ;
54+ script . src = './wasm_exec.js' ;
1755 script . onload = resolve ;
1856 script . onerror = ( ) => reject ( new Error ( 'Failed to load wasm_exec.js' ) ) ;
1957 document . head . appendChild ( script ) ;
2058 } ) ;
2159 }
2260
2361 const go = new Go ( ) ;
24- const result = await WebAssembly . instantiateStreaming ( fetch ( wasmUrl ) , go . importObject ) ;
25- go . run ( result . instance ) ; // starts the Go main() goroutine
62+ const result = await WebAssembly . instantiateStreaming ( fetch ( './prover.wasm' ) , go . importObject ) ;
63+ go . run ( result . instance ) ;
2664
27- // Wait for bitwrapProver to be set
2865 for ( let i = 0 ; i < 100 ; i ++ ) {
2966 if ( typeof bitwrapProver !== 'undefined' ) return ;
3067 await new Promise ( r => setTimeout ( r , 50 ) ) ;
3168 }
3269 throw new Error ( 'WASM prover did not initialize' ) ;
3370 } ) ( ) ;
3471
35- return _ready ;
72+ return _initPromise ;
3673}
3774
38- // Compile a circuit from scratch (slow — runs trusted setup in WASM).
39- // Returns { constraints, publicVars, privateVars } or throws.
4075export async function compileCircuit ( name ) {
4176 await initProver ( ) ;
77+ if ( _worker ) {
78+ return sendWorkerMessage ( 'compileCircuit' , { name } ) ;
79+ }
4280 const result = bitwrapProver . compileCircuit ( name ) ;
4381 if ( result . error ) throw new Error ( result . error ) ;
4482 return result ;
4583}
4684
47- // Load pre-compiled keys from the server (fast — skips compilation).
48- // Fetches .cs, .pk, .vk from keyUrl/{name}.cs etc.
4985export async function loadKeys ( name , keyUrl ) {
5086 await initProver ( ) ;
51-
5287 if ( _keyCache [ name ] ) return _keyCache [ name ] ;
5388
5489 const [ csResp , pkResp , vkResp ] = await Promise . all ( [
@@ -67,40 +102,53 @@ export async function loadKeys(name, keyUrl) {
67102 vkResp . arrayBuffer ( ) . then ( b => new Uint8Array ( b ) ) ,
68103 ] ) ;
69104
105+ if ( _worker ) {
106+ const result = await sendWorkerMessage ( 'loadKeys' , { name, csBytes, pkBytes, vkBytes } ) ;
107+ _keyCache [ name ] = result ;
108+ return result ;
109+ }
110+
70111 const result = bitwrapProver . loadKeys ( name , csBytes , pkBytes , vkBytes ) ;
71112 if ( result . error ) throw new Error ( result . error ) ;
72113 _keyCache [ name ] = result ;
73114 return result ;
74115}
75116
76- // Generate a Groth16 proof. Returns { proof: Uint8Array, publicWitness: Uint8Array }.
77- // witness is an object with string keys and string values (decimal field elements).
117+ // Generate a Groth16 proof — runs in Web Worker (non-blocking).
78118export async function prove ( circuitName , witness ) {
79119 await initProver ( ) ;
120+ if ( _worker ) {
121+ return sendWorkerMessage ( 'prove' , { circuit : circuitName , witness } ) ;
122+ }
123+ // Fallback: main thread (will block UI)
80124 const result = bitwrapProver . prove ( circuitName , JSON . stringify ( witness ) ) ;
81125 if ( result . error ) throw new Error ( result . error ) ;
82- return {
83- proof : result . proof ,
84- publicWitness : result . publicWitness ,
85- } ;
126+ return { proof : result . proof , publicWitness : result . publicWitness } ;
86127}
87128
88- // Verify a proof. Returns { valid: boolean, error?: string }.
89129export async function verify ( circuitName , proof , publicWitness ) {
90130 await initProver ( ) ;
131+ if ( _worker ) {
132+ return sendWorkerMessage ( 'verify' , { circuit : circuitName , proof, publicWitness } ) ;
133+ }
91134 return bitwrapProver . verify ( circuitName , proof , publicWitness ) ;
92135}
93136
94- // Compute MiMC hash (convenience — also available in mimc.js without WASM).
95137export async function mimcHash ( ...args ) {
96138 await initProver ( ) ;
139+ if ( _worker ) {
140+ const result = await sendWorkerMessage ( 'mimcHash' , { args : args . map ( String ) } ) ;
141+ return BigInt ( result ) ;
142+ }
97143 const result = bitwrapProver . mimcHash ( ...args . map ( String ) ) ;
98144 if ( typeof result === 'object' && result . error ) throw new Error ( result . error ) ;
99145 return BigInt ( result ) ;
100146}
101147
102- // List loaded circuits.
103148export async function listCircuits ( ) {
104149 await initProver ( ) ;
150+ if ( _worker ) {
151+ return sendWorkerMessage ( 'listCircuits' , { } ) ;
152+ }
105153 return bitwrapProver . listCircuits ( ) ;
106154}
0 commit comments