@@ -5,6 +5,14 @@ import { DEFAULT_TOML_CONFIG } from '../actions/constants'
55import  NoirParser  from  './noirParser' 
66import  {  extractNameFromKey  }  from  '@remix-ui/helper' 
77import  axios  from  'axios' 
8+ import  JSZip  from  'jszip' 
9+ import  {  VerifierInputs  }  from  '../types' 
10+ 
11+ interface  NoirAbi  { 
12+   parameters : {  name : string ,  type : any ,  visibility : 'public'  |  'private'  } [ ] 
13+   return_type ?: {  visibility : 'public'  |  'private'  } 
14+ } 
15+ 
816export  class  NoirPluginClient  extends  PluginClient  { 
917  public  internalEvents : EventManager 
1018  public  parser : NoirParser 
@@ -17,7 +25,7 @@ export class NoirPluginClient extends PluginClient {
1725
1826  constructor ( )  { 
1927    super ( ) 
20-     this . methods  =  [ 'init' ,  'parse' ,  'compile' ] 
28+     this . methods  =  [ 'init' ,  'parse' ,  'compile' ,   'generateProof' ] 
2129    createClient ( this ) 
2230    this . internalEvents  =  new  EventManager ( ) 
2331    this . parser  =  new  NoirParser ( ) 
@@ -71,6 +79,10 @@ export class NoirPluginClient extends PluginClient {
7179    } 
7280  } 
7381
82+   private  bytesToHex ( bytes : Uint8Array ) : string  { 
83+     return  Array . from ( bytes ,  byte  =>  byte . toString ( 16 ) . padStart ( 2 ,  '0' ) ) . join ( '' ) ; 
84+   } 
85+ 
7486  generateRequestID ( ) : string  { 
7587    const  timestamp  =  Math . floor ( Date . now ( )  /  1000 ) 
7688    const  random  =  Math . random ( ) . toString ( 36 ) . substring ( 2 ,  15 ) 
@@ -151,9 +163,10 @@ export class NoirPluginClient extends PluginClient {
151163          const  {  compiledJson,  proverToml }  =  response . data 
152164
153165          const  buildPath  =  projectRoot  ===  '/'  ? 'build'  : `${ projectRoot }  
154- 
155166          this . call ( 'fileManager' ,  'writeFile' ,  `${ buildPath }  ,  compiledJson ) 
156-           this . call ( 'fileManager' ,  'writeFile' ,  `${ buildPath }  ,  proverToml ) 
167+ 
168+           const  proverTomlPath  =  projectRoot  ===  '/'  ? 'Prover.toml'  : `${ projectRoot }  
169+           this . call ( 'fileManager' ,  'writeFile' ,  proverTomlPath ,  proverToml ) 
157170
158171          this . internalEvents . emit ( 'noir_compiling_done' ) 
159172          this . emit ( 'statusChanged' ,  {  key : 'succeed' ,  title : 'Noir circuit compiled successfully' ,  type : 'success'  } ) 
@@ -169,6 +182,140 @@ export class NoirPluginClient extends PluginClient {
169182    } 
170183  } 
171184
185+   async  generateProof ( path : string ) : Promise < void >  { 
186+     const  requestID  =  this . generateRequestID ( ) 
187+     console . log ( `[${ requestID } ${ path }  ) 
188+ 
189+     // this.internalEvents.emit('noir_proofing_start') 
190+     this . internalEvents . emit ( 'noir_proofing_start' ) 
191+     this . emit ( 'statusChanged' ,  {  key : 'loading' ,  title : 'Generating Proof...' ,  type : 'info'  } ) 
192+     this . call ( 'terminal' ,  'log' ,  {  type : 'log' ,  value : 'Generating proof for '  +  path  } ) 
193+ 
194+     let  projectRoot : string  |  null  =  null 
195+ 
196+     try  { 
197+       if  ( this . ws . readyState  !==  WebSocket . OPEN )  { 
198+         throw  new  Error ( 'WebSocket connection not open. Cannot generate proof.' ) 
199+       } 
200+       
201+       projectRoot  =  await  this . findProjectRoot ( path ) 
202+       if  ( projectRoot  ===  null )  { 
203+         throw  new  Error ( `Invalid project structure for '${ path }  ) 
204+       } 
205+ 
206+       // @ts -ignore 
207+       const  zippedProject : Blob  =  await  this . call ( 'fileManager' ,  'download' ,  projectRoot ,  false ) 
208+       const  formData  =  new  FormData ( ) 
209+       formData . append ( 'file' ,  zippedProject ,  `${ extractNameFromKey ( path ) }  ) 
210+ 
211+       this . ws . send ( JSON . stringify ( {  requestId : requestID  } ) ) 
212+     
213+       // @ts -ignore 
214+       const  response  =  await  axios . post ( `${ BASE_URL } ${ requestID }  ,  formData ,  { 
215+         responseType : 'blob' 
216+       } ) 
217+ 
218+       if  ( response . status  !==  200 )  { 
219+         try  { 
220+           const  errorJson  =  JSON . parse ( await  response . data . text ( ) ) 
221+           throw  new  Error ( errorJson . error  ||  `Backend returned status ${ response . status }  ) 
222+         }  catch  ( parseError )  { 
223+           throw  new  Error ( `Backend returned status ${ response . status } ${ response . statusText }  ) 
224+         } 
225+       } 
226+ 
227+       const  receivedBlob  =  response . data 
228+       this . call ( 'terminal' ,  'log' ,  {  type : 'log' ,  value : 'Received proof artifacts. Extracting files...'  } ) 
229+ 
230+       const  zip  =  await  JSZip . loadAsync ( receivedBlob ) 
231+       const  buildPath  =  projectRoot  ===  '/'  ? 'build'  : `${ projectRoot }  
232+       
233+       const  filesToSave  =  { 
234+         'proof' : {  path : `${ buildPath }  ,  type : 'hex'  } , 
235+         'vk' : {  path : `${ buildPath }  ,  type : 'hex'  } , 
236+         'verifier/solidity/Verifier.sol' : {  path : 'contracts/Verifier.sol' ,  type : 'string'  } , 
237+         'program.json' : {  path : `${ buildPath }  ,  type : 'string'  } , 
238+       } 
239+ 
240+       for  ( const  [ zipPath ,  info ]  of  Object . entries ( filesToSave ) )  { 
241+         const  file  =  zip . file ( zipPath ) 
242+         if  ( file )  { 
243+           let  content : string ; 
244+           if  ( info . type  ===  'hex' )  { 
245+             const  bytes  =  await  file . async ( 'uint8array' ) ; 
246+             content  =  this . bytesToHex ( bytes ) ; 
247+           }  else  { 
248+             content  =  await  file . async ( 'string' ) ; 
249+           } 
250+           await  this . call ( 'fileManager' ,  'writeFile' ,  info . path ,  content ) 
251+           // @ts -ignore 
252+           this . call ( 'terminal' ,  'log' ,  {  type : 'log' ,  value : `Wrote artifact: ${ info . path }   } ) 
253+         } 
254+       } 
255+ 
256+ 
257+       this . call ( 'terminal' ,  'log' ,  {  type : 'log' ,  value : 'Formatting Verifier.sol inputs...'  } ) 
258+       
259+       const  proofFile  =  zip . file ( 'formatted_proof.txt' ) ; 
260+       const  inputsFile  =  zip . file ( 'formatted_public_inputs.json' ) ; 
261+ 
262+       if  ( ! proofFile  ||  ! inputsFile )  { 
263+         throw  new  Error ( "Formatted proof or public inputs not found in zip response from backend." ) ; 
264+       } 
265+ 
266+       const  formattedProof  =  await  proofFile . async ( 'string' ) ; 
267+       const  formattedPublicInputsStr  =  await  inputsFile . async ( 'string' ) ; 
268+       const  formattedPublicInputs  =  JSON . parse ( formattedPublicInputsStr ) ; 
269+       
270+       const  verifierInputs : VerifierInputs  =  { 
271+         proof : formattedProof , 
272+         publicInputs : formattedPublicInputs 
273+       } 
274+ 
275+       this . internalEvents . emit ( 'noir_proofing_done' ,  verifierInputs ) 
276+ 
277+       this . emit ( 'statusChanged' ,  {  key : 'succeed' ,  title : 'Proof generated successfully' ,  type : 'success'  } ) 
278+       this . call ( 'terminal' ,  'log' ,  {  type : 'log' ,  value : 'Proof generation and file extraction complete.'  } ) 
279+       // this.internalEvents.emit('noir_proofing_done') 
280+ 
281+     }  catch  ( e )  { 
282+       console . error ( `[${ requestID }  ,  e ) 
283+       let  errorMsg  =  e . message  ||  'Unknown error during proof generation' 
284+ 
285+       if  ( e . response  &&  e . response . data )  { 
286+         try  { 
287+           let  errorData  =  e . response . data 
288+           
289+           if  ( e . response . data  instanceof  Blob )  { 
290+             const  errorText  =  await  e . response . data . text ( ) 
291+             errorData  =  JSON . parse ( errorText ) 
292+           } 
293+ 
294+           if  ( errorData . error )  { 
295+             errorMsg  =  errorData . error 
296+           }  else  if  ( typeof  errorData  ===  'string' )  { 
297+             errorMsg  =  errorData 
298+           } 
299+         }  catch  ( parseError )  { 
300+           console . error ( 'Failed to parse backend error response:' ,  parseError ) 
301+           errorMsg  =  e . response . statusText  ||  e . message 
302+         } 
303+       } 
304+       this . internalEvents . emit ( 'noir_proofing_errored' ,  e ) 
305+       this . call ( 'terminal' ,  'log' ,  {  type : 'error' ,  value : errorMsg  } ) 
306+       // this.internalEvents.emit('noir_proofing_errored', new Error(errorMsg)) 
307+ 
308+       if  ( projectRoot  !==  null )  { 
309+         try  { 
310+           const  buildPath  =  projectRoot  ===  '/'  ? 'build'  : `${ projectRoot }  
311+           await  this . call ( 'fileManager' ,  'writeFile' ,  `${ buildPath }  ,  errorMsg ) 
312+         }  catch  ( logError )  { 
313+           console . error ( 'Failed to write error log file:' ,  logError ) 
314+         } 
315+       } 
316+     } 
317+   } 
318+ 
172319  async  parse ( path : string ,  content ?: string ) : Promise < void >  { 
173320    if  ( ! content )  content  =  await  this . call ( 'fileManager' ,  'readFile' ,  path ) 
174321    const  result  =  this . parser . parseNoirCode ( content ) 
0 commit comments