@aztec/bb-prover 0.40.1 → 0.42.0

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.
Files changed (60) hide show
  1. package/dest/bb/cli.d.ts.map +1 -1
  2. package/dest/bb/cli.js +24 -2
  3. package/dest/bb/execute.d.ts +37 -2
  4. package/dest/bb/execute.d.ts.map +1 -1
  5. package/dest/bb/execute.js +276 -75
  6. package/dest/config.d.ts +9 -0
  7. package/dest/config.d.ts.map +1 -0
  8. package/dest/config.js +2 -0
  9. package/dest/index.d.ts +2 -0
  10. package/dest/index.d.ts.map +1 -1
  11. package/dest/index.js +3 -1
  12. package/dest/mappings/mappings.d.ts +1 -0
  13. package/dest/mappings/mappings.d.ts.map +1 -1
  14. package/dest/mappings/mappings.js +27 -8
  15. package/dest/prover/bb_native_proof_creator.d.ts +4 -9
  16. package/dest/prover/bb_native_proof_creator.d.ts.map +1 -1
  17. package/dest/prover/bb_native_proof_creator.js +77 -101
  18. package/dest/prover/bb_prover.d.ts +35 -23
  19. package/dest/prover/bb_prover.d.ts.map +1 -1
  20. package/dest/prover/bb_prover.js +247 -155
  21. package/dest/stats.d.ts +2 -5
  22. package/dest/stats.d.ts.map +1 -1
  23. package/dest/stats.js +27 -26
  24. package/dest/test/index.d.ts +1 -0
  25. package/dest/test/index.d.ts.map +1 -1
  26. package/dest/test/index.js +2 -1
  27. package/dest/test/test_circuit_prover.d.ts +9 -7
  28. package/dest/test/test_circuit_prover.d.ts.map +1 -1
  29. package/dest/test/test_circuit_prover.js +31 -15
  30. package/dest/test/test_verifier.d.ts +7 -0
  31. package/dest/test/test_verifier.d.ts.map +1 -0
  32. package/dest/test/test_verifier.js +10 -0
  33. package/dest/verification_key/verification_key_data.d.ts +8 -0
  34. package/dest/verification_key/verification_key_data.d.ts.map +1 -0
  35. package/dest/verification_key/verification_key_data.js +24 -0
  36. package/dest/verifier/bb_verifier.d.ts +18 -0
  37. package/dest/verifier/bb_verifier.d.ts.map +1 -0
  38. package/dest/verifier/bb_verifier.js +90 -0
  39. package/dest/verifier/index.d.ts +2 -0
  40. package/dest/verifier/index.d.ts.map +1 -0
  41. package/dest/verifier/index.js +2 -0
  42. package/package.json +6 -6
  43. package/src/bb/cli.ts +36 -1
  44. package/src/bb/execute.ts +371 -83
  45. package/src/config.ts +9 -0
  46. package/src/index.ts +2 -0
  47. package/src/mappings/mappings.ts +38 -12
  48. package/src/prover/bb_native_proof_creator.ts +139 -119
  49. package/src/prover/bb_prover.ts +454 -242
  50. package/src/stats.ts +30 -45
  51. package/src/test/index.ts +1 -0
  52. package/src/test/test_circuit_prover.ts +84 -21
  53. package/src/test/test_verifier.ts +12 -0
  54. package/src/verification_key/verification_key_data.ts +35 -0
  55. package/src/verifier/bb_verifier.ts +156 -0
  56. package/src/verifier/index.ts +1 -0
  57. package/dest/prover/verification_key_data.d.ts +0 -16
  58. package/dest/prover/verification_key_data.d.ts.map +0 -1
  59. package/dest/prover/verification_key_data.js +0 -5
  60. package/src/prover/verification_key_data.ts +0 -16
package/src/bb/execute.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type AvmCircuitInputs } from '@aztec/circuits.js';
1
2
  import { sha256 } from '@aztec/foundation/crypto';
2
3
  import { type LogFn } from '@aztec/foundation/log';
3
4
  import { Timer } from '@aztec/foundation/timer';
@@ -5,6 +6,7 @@ import { type NoirCompiledCircuit } from '@aztec/types/noir';
5
6
 
6
7
  import * as proc from 'child_process';
7
8
  import * as fs from 'fs/promises';
9
+ import { basename, dirname, join } from 'path';
8
10
 
9
11
  export const VK_FILENAME = 'vk';
10
12
  export const VK_FIELDS_FILENAME = 'vk_fields.json';
@@ -20,9 +22,14 @@ export enum BB_RESULT {
20
22
  export type BBSuccess = {
21
23
  status: BB_RESULT.SUCCESS | BB_RESULT.ALREADY_PRESENT;
22
24
  duration: number;
25
+ /** Full path of the public key. */
23
26
  pkPath?: string;
27
+ /** Base directory for the VKs (raw, fields). */
24
28
  vkPath?: string;
29
+ /** Full path of the proof. */
25
30
  proofPath?: string;
31
+ /** Full path of the contract. */
32
+ contractPath?: string;
26
33
  };
27
34
 
28
35
  export type BBFailure = {
@@ -32,6 +39,14 @@ export type BBFailure = {
32
39
 
33
40
  export type BBResult = BBSuccess | BBFailure;
34
41
 
42
+ export type VerificationFunction = typeof verifyProof | typeof verifyAvmProof;
43
+
44
+ type BBExecResult = {
45
+ status: BB_RESULT;
46
+ exitCode: number;
47
+ signal: string | undefined;
48
+ };
49
+
35
50
  /**
36
51
  * Invokes the Barretenberg binary with the provided command and args
37
52
  * @param pathToBB - The path to the BB binary
@@ -47,10 +62,15 @@ export function executeBB(
47
62
  args: string[],
48
63
  logger: LogFn,
49
64
  resultParser = (code: number) => code === 0,
50
- ) {
51
- return new Promise<BB_RESULT.SUCCESS | BB_RESULT.FAILURE>((resolve, reject) => {
65
+ ): Promise<BBExecResult> {
66
+ return new Promise<BBExecResult>(resolve => {
52
67
  // spawn the bb process
53
- const bb = proc.spawn(pathToBB, [command, ...args]);
68
+ const { HARDWARE_CONCURRENCY: _, ...envWithoutConcurrency } = process.env;
69
+ const env = process.env.HARDWARE_CONCURRENCY ? process.env : envWithoutConcurrency;
70
+ logger(`Executing BB with: ${command} ${args.join(' ')}`);
71
+ const bb = proc.spawn(pathToBB, [command, ...args], {
72
+ env,
73
+ });
54
74
  bb.stdout.on('data', data => {
55
75
  const message = data.toString('utf-8').replace(/\n$/, '');
56
76
  logger(message);
@@ -59,17 +79,16 @@ export function executeBB(
59
79
  const message = data.toString('utf-8').replace(/\n$/, '');
60
80
  logger(message);
61
81
  });
62
- bb.on('close', (code: number) => {
63
- if (resultParser(code)) {
64
- resolve(BB_RESULT.SUCCESS);
82
+ bb.on('close', (exitCode: number, signal?: string) => {
83
+ if (resultParser(exitCode)) {
84
+ resolve({ status: BB_RESULT.SUCCESS, exitCode, signal });
65
85
  } else {
66
- reject();
86
+ resolve({ status: BB_RESULT.FAILURE, exitCode, signal });
67
87
  }
68
88
  });
69
- }).catch(_ => BB_RESULT.FAILURE);
89
+ }).catch(_ => ({ status: BB_RESULT.FAILURE, exitCode: -1, signal: undefined }));
70
90
  }
71
91
 
72
- const bytecodeHashFilename = 'bytecode_hash';
73
92
  const bytecodeFilename = 'bytecode';
74
93
 
75
94
  /**
@@ -101,82 +120,67 @@ export async function generateKeyForNoirCircuit(
101
120
  // The bytecode is written to e.g. /workingDirectory/pk/BaseParityArtifact/bytecode
102
121
  // The bytecode is removed after the key is generated, leaving just the hash file
103
122
  const circuitOutputDirectory = `${workingDirectory}/${key}/${circuitName}`;
104
- const bytecodeHashPath = `${circuitOutputDirectory}/${bytecodeHashFilename}`;
105
- const bytecodePath = `${circuitOutputDirectory}/${bytecodeFilename}`;
106
- const bytecodeHash = sha256(bytecode);
107
-
108
123
  const outputPath = `${circuitOutputDirectory}`;
124
+ const bytecodeHash = sha256(bytecode);
109
125
 
110
126
  // ensure the directory exists
111
127
  await fs.mkdir(circuitOutputDirectory, { recursive: true });
112
128
 
113
- // Generate the key if we have been told to, or there is no bytecode hash
114
- let mustRegenerate =
115
- force ||
116
- (await fs
117
- .access(bytecodeHashPath, fs.constants.R_OK)
118
- .then(_ => false)
119
- .catch(_ => true));
120
-
121
- if (!mustRegenerate) {
122
- // Check to see if the bytecode hash has changed from the stored value
123
- const data: Buffer = await fs.readFile(bytecodeHashPath).catch(_ => Buffer.alloc(0));
124
- mustRegenerate = data.length == 0 || !data.equals(bytecodeHash);
125
- }
129
+ const res = await fsCache<BBSuccess | BBFailure>(circuitOutputDirectory, bytecodeHash, log, force, async () => {
130
+ const binaryPresent = await fs
131
+ .access(pathToBB, fs.constants.R_OK)
132
+ .then(_ => true)
133
+ .catch(_ => false);
134
+ if (!binaryPresent) {
135
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
136
+ }
137
+
138
+ // We are now going to generate the key
139
+ try {
140
+ const bytecodePath = `${circuitOutputDirectory}/${bytecodeFilename}`;
141
+ // Write the bytecode to the working directory
142
+ await fs.writeFile(bytecodePath, bytecode);
126
143
 
127
- if (!mustRegenerate) {
128
- // No need to generate, early out
144
+ // args are the output path and the input bytecode path
145
+ const args = ['-o', `${outputPath}/${VK_FILENAME}`, '-b', bytecodePath];
146
+ const timer = new Timer();
147
+ let result = await executeBB(pathToBB, `write_${key}`, args, log);
148
+ // If we succeeded and the type of key if verification, have bb write the 'fields' version too
149
+ if (result.status == BB_RESULT.SUCCESS && key === 'vk') {
150
+ const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
151
+ result = await executeBB(pathToBB, `vk_as_fields`, asFieldsArgs, log);
152
+ }
153
+ const duration = timer.ms();
154
+
155
+ if (result.status == BB_RESULT.SUCCESS) {
156
+ return {
157
+ status: BB_RESULT.SUCCESS,
158
+ duration,
159
+ pkPath: key === 'pk' ? outputPath : undefined,
160
+ vkPath: key === 'vk' ? outputPath : undefined,
161
+ proofPath: undefined,
162
+ };
163
+ }
164
+ // Not a great error message here but it is difficult to decipher what comes from bb
165
+ return {
166
+ status: BB_RESULT.FAILURE,
167
+ reason: `Failed to generate key. Exit code: ${result.exitCode}. Signal ${result.signal}.`,
168
+ };
169
+ } catch (error) {
170
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
171
+ }
172
+ });
173
+
174
+ if (!res) {
129
175
  return {
130
176
  status: BB_RESULT.ALREADY_PRESENT,
131
177
  duration: 0,
132
178
  pkPath: key === 'pk' ? outputPath : undefined,
133
179
  vkPath: key === 'vk' ? outputPath : undefined,
134
- proofPath: undefined,
135
180
  };
136
181
  }
137
182
 
138
- // Check we have access to bb
139
- const binaryPresent = await fs
140
- .access(pathToBB, fs.constants.R_OK)
141
- .then(_ => true)
142
- .catch(_ => false);
143
- if (!binaryPresent) {
144
- return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
145
- }
146
-
147
- // We are now going to generate the key
148
- try {
149
- // Write the bytecode to the working directory
150
- await fs.writeFile(bytecodePath, bytecode);
151
-
152
- // args are the output path and the input bytecode path
153
- const args = ['-o', outputPath, '-b', bytecodePath];
154
- const timer = new Timer();
155
- let result = await executeBB(pathToBB, `write_${key}`, args, log);
156
- // If we succeeded and the type of key if verification, have bb write the 'fields' version too
157
- if (result == BB_RESULT.SUCCESS && key === 'vk') {
158
- const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
159
- result = await executeBB(pathToBB, `vk_as_fields`, asFieldsArgs, log);
160
- }
161
- const duration = timer.ms();
162
- // Cleanup the bytecode file
163
- await fs.rm(bytecodePath, { force: true });
164
- if (result == BB_RESULT.SUCCESS) {
165
- // Store the bytecode hash so we don't need to regenerate at a later time
166
- await fs.writeFile(bytecodeHashPath, bytecodeHash);
167
- return {
168
- status: BB_RESULT.SUCCESS,
169
- duration,
170
- pkPath: key === 'pk' ? outputPath : undefined,
171
- vkPath: key === 'vk' ? outputPath : undefined,
172
- proofPath: undefined,
173
- };
174
- }
175
- // Not a great error message here but it is difficult to decipher what comes from bb
176
- return { status: BB_RESULT.FAILURE, reason: `Failed to generate key` };
177
- } catch (error) {
178
- return { status: BB_RESULT.FAILURE, reason: `${error}` };
179
- }
183
+ return res;
180
184
  }
181
185
 
182
186
  /**
@@ -229,9 +233,8 @@ export async function generateProof(
229
233
  };
230
234
  const result = await executeBB(pathToBB, 'prove_output_all', args, logFunction);
231
235
  const duration = timer.ms();
232
- // cleanup the bytecode
233
- await fs.rm(bytecodePath, { force: true });
234
- if (result == BB_RESULT.SUCCESS) {
236
+
237
+ if (result.status == BB_RESULT.SUCCESS) {
235
238
  return {
236
239
  status: BB_RESULT.SUCCESS,
237
240
  duration,
@@ -241,7 +244,119 @@ export async function generateProof(
241
244
  };
242
245
  }
243
246
  // Not a great error message here but it is difficult to decipher what comes from bb
244
- return { status: BB_RESULT.FAILURE, reason: `Failed to generate proof` };
247
+ return {
248
+ status: BB_RESULT.FAILURE,
249
+ reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
250
+ };
251
+ } catch (error) {
252
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Used for generating AVM proofs.
258
+ * It is assumed that the working directory is a temporary and/or random directory used solely for generating this proof.
259
+ * @param pathToBB - The full path to the bb binary
260
+ * @param workingDirectory - A working directory for use by bb
261
+ * @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
262
+ * @param log - A logging function
263
+ * @returns An object containing a result indication, the location of the proof and the duration taken
264
+ */
265
+ export async function generateAvmProof(
266
+ pathToBB: string,
267
+ workingDirectory: string,
268
+ input: AvmCircuitInputs,
269
+ log: LogFn,
270
+ ): Promise<BBFailure | BBSuccess> {
271
+ // Check that the working directory exists
272
+ try {
273
+ await fs.access(workingDirectory);
274
+ } catch (error) {
275
+ return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
276
+ }
277
+
278
+ // Paths for the inputs
279
+ const bytecodePath = join(workingDirectory, 'avm_bytecode.bin');
280
+ const calldataPath = join(workingDirectory, 'avm_calldata.bin');
281
+ const publicInputsPath = join(workingDirectory, 'avm_public_inputs.bin');
282
+ const avmHintsPath = join(workingDirectory, 'avm_hints.bin');
283
+
284
+ // The proof is written to e.g. /workingDirectory/proof
285
+ const outputPath = workingDirectory;
286
+
287
+ const filePresent = async (file: string) =>
288
+ await fs
289
+ .access(file, fs.constants.R_OK)
290
+ .then(_ => true)
291
+ .catch(_ => false);
292
+
293
+ const binaryPresent = await filePresent(pathToBB);
294
+ if (!binaryPresent) {
295
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
296
+ }
297
+
298
+ try {
299
+ // Write the inputs to the working directory.
300
+ await fs.writeFile(bytecodePath, input.bytecode);
301
+ if (!filePresent(bytecodePath)) {
302
+ return { status: BB_RESULT.FAILURE, reason: `Could not write bytecode at ${bytecodePath}` };
303
+ }
304
+ await fs.writeFile(
305
+ calldataPath,
306
+ input.calldata.map(fr => fr.toBuffer()),
307
+ );
308
+ if (!filePresent(calldataPath)) {
309
+ return { status: BB_RESULT.FAILURE, reason: `Could not write calldata at ${calldataPath}` };
310
+ }
311
+
312
+ // public inputs are used directly as a vector of fields in C++,
313
+ // so we serialize them as such here instead of just using toBuffer
314
+ await fs.writeFile(
315
+ publicInputsPath,
316
+ input.publicInputs.toFields().map(fr => fr.toBuffer()),
317
+ );
318
+ if (!filePresent(publicInputsPath)) {
319
+ return { status: BB_RESULT.FAILURE, reason: `Could not write publicInputs at ${publicInputsPath}` };
320
+ }
321
+
322
+ await fs.writeFile(avmHintsPath, input.avmHints.toBuffer());
323
+ if (!filePresent(avmHintsPath)) {
324
+ return { status: BB_RESULT.FAILURE, reason: `Could not write avmHints at ${avmHintsPath}` };
325
+ }
326
+
327
+ const args = [
328
+ '--avm-bytecode',
329
+ bytecodePath,
330
+ '--avm-calldata',
331
+ calldataPath,
332
+ '--avm-public-inputs',
333
+ publicInputsPath,
334
+ '--avm-hints',
335
+ avmHintsPath,
336
+ '-o',
337
+ outputPath,
338
+ ];
339
+ const timer = new Timer();
340
+ const logFunction = (message: string) => {
341
+ log(`AvmCircuit (prove) BB out - ${message}`);
342
+ };
343
+ const result = await executeBB(pathToBB, 'avm_prove', args, logFunction);
344
+ const duration = timer.ms();
345
+
346
+ if (result.status == BB_RESULT.SUCCESS) {
347
+ return {
348
+ status: BB_RESULT.SUCCESS,
349
+ duration,
350
+ proofPath: join(outputPath, PROOF_FILENAME),
351
+ pkPath: undefined,
352
+ vkPath: outputPath,
353
+ };
354
+ }
355
+ // Not a great error message here but it is difficult to decipher what comes from bb
356
+ return {
357
+ status: BB_RESULT.FAILURE,
358
+ reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
359
+ };
245
360
  } catch (error) {
246
361
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
247
362
  }
@@ -260,6 +375,42 @@ export async function verifyProof(
260
375
  proofFullPath: string,
261
376
  verificationKeyPath: string,
262
377
  log: LogFn,
378
+ ): Promise<BBFailure | BBSuccess> {
379
+ return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'verify', log);
380
+ }
381
+
382
+ /**
383
+ * Used for verifying proofs of the AVM
384
+ * @param pathToBB - The full path to the bb binary
385
+ * @param proofFullPath - The full path to the proof to be verified
386
+ * @param verificationKeyPath - The full path to the circuit verification key
387
+ * @param log - A logging function
388
+ * @returns An object containing a result indication and duration taken
389
+ */
390
+ export async function verifyAvmProof(
391
+ pathToBB: string,
392
+ proofFullPath: string,
393
+ verificationKeyPath: string,
394
+ log: LogFn,
395
+ ): Promise<BBFailure | BBSuccess> {
396
+ return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', log);
397
+ }
398
+
399
+ /**
400
+ * Used for verifying proofs with BB
401
+ * @param pathToBB - The full path to the bb binary
402
+ * @param proofFullPath - The full path to the proof to be verified
403
+ * @param verificationKeyPath - The full path to the circuit verification key
404
+ * @param command - The BB command to execute (verify/avm_verify)
405
+ * @param log - A logging function
406
+ * @returns An object containing a result indication and duration taken
407
+ */
408
+ async function verifyProofInternal(
409
+ pathToBB: string,
410
+ proofFullPath: string,
411
+ verificationKeyPath: string,
412
+ command: 'verify' | 'avm_verify',
413
+ log: LogFn,
263
414
  ): Promise<BBFailure | BBSuccess> {
264
415
  const binaryPresent = await fs
265
416
  .access(pathToBB, fs.constants.R_OK)
@@ -272,13 +423,16 @@ export async function verifyProof(
272
423
  try {
273
424
  const args = ['-p', proofFullPath, '-k', verificationKeyPath];
274
425
  const timer = new Timer();
275
- const result = await executeBB(pathToBB, 'verify', args, log);
426
+ const result = await executeBB(pathToBB, command, args, log);
276
427
  const duration = timer.ms();
277
- if (result == BB_RESULT.SUCCESS) {
428
+ if (result.status == BB_RESULT.SUCCESS) {
278
429
  return { status: BB_RESULT.SUCCESS, duration };
279
430
  }
280
431
  // Not a great error message here but it is difficult to decipher what comes from bb
281
- return { status: BB_RESULT.FAILURE, reason: `Failed to verify proof` };
432
+ return {
433
+ status: BB_RESULT.FAILURE,
434
+ reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
435
+ };
282
436
  } catch (error) {
283
437
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
284
438
  }
@@ -311,11 +465,14 @@ export async function writeVkAsFields(
311
465
  const timer = new Timer();
312
466
  const result = await executeBB(pathToBB, 'vk_as_fields', args, log);
313
467
  const duration = timer.ms();
314
- if (result == BB_RESULT.SUCCESS) {
468
+ if (result.status == BB_RESULT.SUCCESS) {
315
469
  return { status: BB_RESULT.SUCCESS, duration, vkPath: verificationKeyPath };
316
470
  }
317
471
  // Not a great error message here but it is difficult to decipher what comes from bb
318
- return { status: BB_RESULT.FAILURE, reason: `Failed to create vk as fields` };
472
+ return {
473
+ status: BB_RESULT.FAILURE,
474
+ reason: `Failed to create vk as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
475
+ };
319
476
  } catch (error) {
320
477
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
321
478
  }
@@ -326,6 +483,7 @@ export async function writeVkAsFields(
326
483
  * @param pathToBB - The full path to the bb binary
327
484
  * @param proofPath - The directory containing the binary proof
328
485
  * @param proofFileName - The filename of the proof
486
+ * @param vkFileName - The filename of the verification key
329
487
  * @param log - A logging function
330
488
  * @returns An object containing a result indication and duration taken
331
489
  */
@@ -333,6 +491,7 @@ export async function writeProofAsFields(
333
491
  pathToBB: string,
334
492
  proofPath: string,
335
493
  proofFileName: string,
494
+ vkFilePath: string,
336
495
  log: LogFn,
337
496
  ): Promise<BBFailure | BBSuccess> {
338
497
  const binaryPresent = await fs
@@ -344,16 +503,145 @@ export async function writeProofAsFields(
344
503
  }
345
504
 
346
505
  try {
347
- const args = ['-p', `${proofPath}/${proofFileName}`, '-v'];
506
+ const args = ['-p', `${proofPath}/${proofFileName}`, '-k', vkFilePath, '-v'];
348
507
  const timer = new Timer();
349
508
  const result = await executeBB(pathToBB, 'proof_as_fields', args, log);
350
509
  const duration = timer.ms();
351
- if (result == BB_RESULT.SUCCESS) {
510
+ if (result.status == BB_RESULT.SUCCESS) {
352
511
  return { status: BB_RESULT.SUCCESS, duration, proofPath: proofPath };
353
512
  }
354
513
  // Not a great error message here but it is difficult to decipher what comes from bb
355
- return { status: BB_RESULT.FAILURE, reason: `Failed to create proof as fields` };
514
+ return {
515
+ status: BB_RESULT.FAILURE,
516
+ reason: `Failed to create proof as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
517
+ };
356
518
  } catch (error) {
357
519
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
358
520
  }
359
521
  }
522
+
523
+ export async function generateContractForVerificationKey(
524
+ pathToBB: string,
525
+ vkFilePath: string,
526
+ contractPath: string,
527
+ log: LogFn,
528
+ ): Promise<BBFailure | BBSuccess> {
529
+ const binaryPresent = await fs
530
+ .access(pathToBB, fs.constants.R_OK)
531
+ .then(_ => true)
532
+ .catch(_ => false);
533
+
534
+ if (!binaryPresent) {
535
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
536
+ }
537
+
538
+ const outputDir = dirname(contractPath);
539
+ const contractName = basename(contractPath);
540
+ // cache contract generation based on vk file and contract name
541
+ const cacheKey = sha256(Buffer.concat([Buffer.from(contractName), await fs.readFile(vkFilePath)]));
542
+
543
+ await fs.mkdir(outputDir, { recursive: true });
544
+
545
+ const res = await fsCache<BBSuccess | BBFailure>(outputDir, cacheKey, log, false, async () => {
546
+ try {
547
+ const args = ['-k', vkFilePath, '-o', contractPath, '-v'];
548
+ const timer = new Timer();
549
+ const result = await executeBB(pathToBB, 'contract', args, log);
550
+ const duration = timer.ms();
551
+ if (result.status == BB_RESULT.SUCCESS) {
552
+ return { status: BB_RESULT.SUCCESS, duration, contractPath };
553
+ }
554
+ // Not a great error message here but it is difficult to decipher what comes from bb
555
+ return {
556
+ status: BB_RESULT.FAILURE,
557
+ reason: `Failed to write verifier contract. Exit code ${result.exitCode}. Signal ${result.signal}.`,
558
+ };
559
+ } catch (error) {
560
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
561
+ }
562
+ });
563
+
564
+ if (!res) {
565
+ return {
566
+ status: BB_RESULT.ALREADY_PRESENT,
567
+ duration: 0,
568
+ contractPath,
569
+ };
570
+ }
571
+
572
+ return res;
573
+ }
574
+
575
+ export async function generateContractForCircuit(
576
+ pathToBB: string,
577
+ workingDirectory: string,
578
+ circuitName: string,
579
+ compiledCircuit: NoirCompiledCircuit,
580
+ contractName: string,
581
+ log: LogFn,
582
+ force = false,
583
+ ) {
584
+ const vkResult = await generateKeyForNoirCircuit(
585
+ pathToBB,
586
+ workingDirectory,
587
+ circuitName,
588
+ compiledCircuit,
589
+ 'vk',
590
+ log,
591
+ force,
592
+ );
593
+ if (vkResult.status === BB_RESULT.FAILURE) {
594
+ return vkResult;
595
+ }
596
+
597
+ return generateContractForVerificationKey(
598
+ pathToBB,
599
+ join(vkResult.vkPath!, VK_FILENAME),
600
+ join(workingDirectory, 'contract', circuitName, contractName),
601
+ log,
602
+ );
603
+ }
604
+
605
+ const CACHE_FILENAME = '.cache';
606
+ async function fsCache<T>(
607
+ dir: string,
608
+ expectedCacheKey: Buffer,
609
+ logger: LogFn,
610
+ force: boolean,
611
+ action: () => Promise<T>,
612
+ ): Promise<T | undefined> {
613
+ const cacheFilePath = join(dir, CACHE_FILENAME);
614
+
615
+ let run: boolean;
616
+ if (force) {
617
+ run = true;
618
+ } else {
619
+ try {
620
+ run = !expectedCacheKey.equals(await fs.readFile(cacheFilePath));
621
+ } catch (err: any) {
622
+ if (err && 'code' in err && err.code === 'ENOENT') {
623
+ // cache file doesn't exist, swallow error and run
624
+ run = true;
625
+ } else {
626
+ throw err;
627
+ }
628
+ }
629
+ }
630
+
631
+ let res: T | undefined;
632
+ if (run) {
633
+ logger(`Cache miss or forced run. Running operation in ${dir}...`);
634
+ res = await action();
635
+ } else {
636
+ logger(`Cache hit. Skipping operation in ${dir}...`);
637
+ }
638
+
639
+ try {
640
+ await fs.writeFile(cacheFilePath, expectedCacheKey);
641
+ } catch (err) {
642
+ logger(`Couldn't write cache data to ${cacheFilePath}. Skipping cache...`);
643
+ // ignore
644
+ }
645
+
646
+ return res;
647
+ }
package/src/config.ts ADDED
@@ -0,0 +1,9 @@
1
+ export interface BBConfig {
2
+ bbBinaryPath: string;
3
+ bbWorkingDirectory: string;
4
+ }
5
+
6
+ export interface ACVMConfig {
7
+ acvmBinaryPath: string;
8
+ acvmWorkingDirectory: string;
9
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './prover/index.js';
2
2
  export * from './test/index.js';
3
+ export * from './verifier/index.js';
4
+ export * from './config.js';
@@ -2,12 +2,18 @@ import { PublicKernelType } from '@aztec/circuit-types';
2
2
  import { type PublicKernelCircuitPrivateInputs, type PublicKernelCircuitPublicInputs } from '@aztec/circuits.js';
3
3
  import {
4
4
  type ServerProtocolArtifact,
5
- convertPublicInnerRollupInputsToWitnessMap,
6
- convertPublicInnerRollupOutputFromWitnessMap,
7
- convertPublicSetupRollupInputsToWitnessMap,
8
- convertPublicSetupRollupOutputFromWitnessMap,
9
- convertPublicTeardownRollupInputsToWitnessMap,
10
- convertPublicTeardownRollupOutputFromWitnessMap,
5
+ convertPublicInnerInputsToWitnessMap,
6
+ convertPublicInnerOutputFromWitnessMap,
7
+ convertPublicSetupInputsToWitnessMap,
8
+ convertPublicSetupOutputFromWitnessMap,
9
+ convertPublicTeardownInputsToWitnessMap,
10
+ convertPublicTeardownOutputFromWitnessMap,
11
+ convertSimulatedPublicInnerInputsToWitnessMap,
12
+ convertSimulatedPublicInnerOutputFromWitnessMap,
13
+ convertSimulatedPublicSetupInputsToWitnessMap,
14
+ convertSimulatedPublicSetupOutputFromWitnessMap,
15
+ convertSimulatedPublicTeardownInputsToWitnessMap,
16
+ convertSimulatedPublicTeardownOutputFromWitnessMap,
11
17
  } from '@aztec/noir-protocol-circuits-types';
12
18
 
13
19
  import { type WitnessMap } from '@noir-lang/types';
@@ -20,22 +26,42 @@ export type PublicKernelProvingOps = {
20
26
 
21
27
  export type KernelTypeToArtifact = Record<PublicKernelType, PublicKernelProvingOps | undefined>;
22
28
 
29
+ export const SimulatedPublicKernelArtifactMapping: KernelTypeToArtifact = {
30
+ [PublicKernelType.NON_PUBLIC]: undefined,
31
+ [PublicKernelType.APP_LOGIC]: {
32
+ artifact: 'PublicKernelAppLogicArtifact',
33
+ convertInputs: convertSimulatedPublicInnerInputsToWitnessMap,
34
+ convertOutputs: convertSimulatedPublicInnerOutputFromWitnessMap,
35
+ },
36
+ [PublicKernelType.SETUP]: {
37
+ artifact: 'PublicKernelSetupArtifact',
38
+ convertInputs: convertSimulatedPublicSetupInputsToWitnessMap,
39
+ convertOutputs: convertSimulatedPublicSetupOutputFromWitnessMap,
40
+ },
41
+ [PublicKernelType.TEARDOWN]: {
42
+ artifact: 'PublicKernelTeardownArtifact',
43
+ convertInputs: convertSimulatedPublicTeardownInputsToWitnessMap,
44
+ convertOutputs: convertSimulatedPublicTeardownOutputFromWitnessMap,
45
+ },
46
+ [PublicKernelType.TAIL]: undefined,
47
+ };
48
+
23
49
  export const PublicKernelArtifactMapping: KernelTypeToArtifact = {
24
50
  [PublicKernelType.NON_PUBLIC]: undefined,
25
51
  [PublicKernelType.APP_LOGIC]: {
26
52
  artifact: 'PublicKernelAppLogicArtifact',
27
- convertInputs: convertPublicInnerRollupInputsToWitnessMap,
28
- convertOutputs: convertPublicInnerRollupOutputFromWitnessMap,
53
+ convertInputs: convertPublicInnerInputsToWitnessMap,
54
+ convertOutputs: convertPublicInnerOutputFromWitnessMap,
29
55
  },
30
56
  [PublicKernelType.SETUP]: {
31
57
  artifact: 'PublicKernelSetupArtifact',
32
- convertInputs: convertPublicSetupRollupInputsToWitnessMap,
33
- convertOutputs: convertPublicSetupRollupOutputFromWitnessMap,
58
+ convertInputs: convertPublicSetupInputsToWitnessMap,
59
+ convertOutputs: convertPublicSetupOutputFromWitnessMap,
34
60
  },
35
61
  [PublicKernelType.TEARDOWN]: {
36
62
  artifact: 'PublicKernelTeardownArtifact',
37
- convertInputs: convertPublicTeardownRollupInputsToWitnessMap,
38
- convertOutputs: convertPublicTeardownRollupOutputFromWitnessMap,
63
+ convertInputs: convertPublicTeardownInputsToWitnessMap,
64
+ convertOutputs: convertPublicTeardownOutputFromWitnessMap,
39
65
  },
40
66
  [PublicKernelType.TAIL]: undefined,
41
67
  };