@aztec/bb-prover 0.41.0 → 0.43.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 (59) 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 +30 -1
  4. package/dest/bb/execute.d.ts.map +1 -1
  5. package/dest/bb/execute.js +252 -60
  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 +2 -8
  16. package/dest/prover/bb_native_proof_creator.d.ts.map +1 -1
  17. package/dest/prover/bb_native_proof_creator.js +37 -79
  18. package/dest/prover/bb_prover.d.ts +33 -32
  19. package/dest/prover/bb_prover.d.ts.map +1 -1
  20. package/dest/prover/bb_prover.js +231 -162
  21. package/dest/stats.d.ts.map +1 -1
  22. package/dest/stats.js +8 -2
  23. package/dest/test/index.d.ts +1 -0
  24. package/dest/test/index.d.ts.map +1 -1
  25. package/dest/test/index.js +2 -1
  26. package/dest/test/test_circuit_prover.d.ts +9 -7
  27. package/dest/test/test_circuit_prover.d.ts.map +1 -1
  28. package/dest/test/test_circuit_prover.js +32 -16
  29. package/dest/test/test_verifier.d.ts +7 -0
  30. package/dest/test/test_verifier.d.ts.map +1 -0
  31. package/dest/test/test_verifier.js +10 -0
  32. package/dest/verification_key/verification_key_data.d.ts +8 -0
  33. package/dest/verification_key/verification_key_data.d.ts.map +1 -0
  34. package/dest/verification_key/verification_key_data.js +24 -0
  35. package/dest/verifier/bb_verifier.d.ts +18 -0
  36. package/dest/verifier/bb_verifier.d.ts.map +1 -0
  37. package/dest/verifier/bb_verifier.js +90 -0
  38. package/dest/verifier/index.d.ts +2 -0
  39. package/dest/verifier/index.d.ts.map +1 -0
  40. package/dest/verifier/index.js +2 -0
  41. package/package.json +6 -6
  42. package/src/bb/cli.ts +36 -1
  43. package/src/bb/execute.ts +340 -67
  44. package/src/config.ts +9 -0
  45. package/src/index.ts +2 -0
  46. package/src/mappings/mappings.ts +38 -12
  47. package/src/prover/bb_native_proof_creator.ts +49 -91
  48. package/src/prover/bb_prover.ts +396 -221
  49. package/src/stats.ts +7 -1
  50. package/src/test/index.ts +1 -0
  51. package/src/test/test_circuit_prover.ts +85 -23
  52. package/src/test/test_verifier.ts +12 -0
  53. package/src/verification_key/verification_key_data.ts +35 -0
  54. package/src/verifier/bb_verifier.ts +156 -0
  55. package/src/verifier/index.ts +1 -0
  56. package/dest/prover/verification_key_data.d.ts +0 -16
  57. package/dest/prover/verification_key_data.d.ts.map +0 -1
  58. package/dest/prover/verification_key_data.js +0 -5
  59. 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,8 @@ export type BBFailure = {
32
39
 
33
40
  export type BBResult = BBSuccess | BBFailure;
34
41
 
42
+ export type VerificationFunction = typeof verifyProof | typeof verifyAvmProof;
43
+
35
44
  type BBExecResult = {
36
45
  status: BB_RESULT;
37
46
  exitCode: number;
@@ -56,8 +65,19 @@ export function executeBB(
56
65
  ): Promise<BBExecResult> {
57
66
  return new Promise<BBExecResult>(resolve => {
58
67
  // spawn the bb process
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(' ')}`);
59
71
  const bb = proc.spawn(pathToBB, [command, ...args], {
60
- stdio: 'pipe',
72
+ env,
73
+ });
74
+ bb.stdout.on('data', data => {
75
+ const message = data.toString('utf-8').replace(/\n$/, '');
76
+ logger(message);
77
+ });
78
+ bb.stderr.on('data', data => {
79
+ const message = data.toString('utf-8').replace(/\n$/, '');
80
+ logger(message);
61
81
  });
62
82
  bb.on('close', (exitCode: number, signal?: string) => {
63
83
  if (resultParser(exitCode)) {
@@ -69,7 +89,6 @@ export function executeBB(
69
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,41 +120,101 @@ 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);
143
+
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();
126
154
 
127
- if (!mustRegenerate) {
128
- // No need to generate, early out
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
183
+ return res;
184
+ }
185
+
186
+ /**
187
+ * Used for generating proofs of noir circuits.
188
+ * It is assumed that the working directory is a temporary and/or random directory used solely for generating this proof.
189
+ * @param pathToBB - The full path to the bb binary
190
+ * @param workingDirectory - A working directory for use by bb
191
+ * @param circuitName - An identifier for the circuit
192
+ * @param bytecode - The compiled circuit bytecode
193
+ * @param inputWitnessFile - The circuit input witness
194
+ * @param log - A logging function
195
+ * @returns An object containing a result indication, the location of the proof and the duration taken
196
+ */
197
+ export async function generateProof(
198
+ pathToBB: string,
199
+ workingDirectory: string,
200
+ circuitName: string,
201
+ bytecode: Buffer,
202
+ inputWitnessFile: string,
203
+ log: LogFn,
204
+ ): Promise<BBFailure | BBSuccess> {
205
+ // Check that the working directory exists
206
+ try {
207
+ await fs.access(workingDirectory);
208
+ } catch (error) {
209
+ return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
210
+ }
211
+
212
+ // The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
213
+ const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
214
+
215
+ // The proof is written to e.g. /workingDirectory/proof
216
+ const outputPath = `${workingDirectory}`;
217
+
139
218
  const binaryPresent = await fs
140
219
  .access(pathToBB, fs.constants.R_OK)
141
220
  .then(_ => true)
@@ -144,38 +223,30 @@ export async function generateKeyForNoirCircuit(
144
223
  return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
145
224
  }
146
225
 
147
- // We are now going to generate the key
148
226
  try {
149
227
  // Write the bytecode to the working directory
150
228
  await fs.writeFile(bytecodePath, bytecode);
151
-
152
- // args are the output path and the input bytecode path
153
- const args = ['-o', outputPath, '-b', bytecodePath];
229
+ const args = ['-o', outputPath, '-b', bytecodePath, '-w', inputWitnessFile, '-v'];
154
230
  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.status == 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
- }
231
+ const logFunction = (message: string) => {
232
+ log(`${circuitName} BB out - ${message}`);
233
+ };
234
+ const result = await executeBB(pathToBB, 'prove_output_all', args, logFunction);
161
235
  const duration = timer.ms();
162
- // Cleanup the bytecode file
163
- await fs.rm(bytecodePath, { force: true });
236
+
164
237
  if (result.status == 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
238
  return {
168
239
  status: BB_RESULT.SUCCESS,
169
240
  duration,
170
- pkPath: key === 'pk' ? outputPath : undefined,
171
- vkPath: key === 'vk' ? outputPath : undefined,
172
- proofPath: undefined,
241
+ proofPath: `${outputPath}`,
242
+ pkPath: undefined,
243
+ vkPath: `${outputPath}`,
173
244
  };
174
245
  }
175
246
  // Not a great error message here but it is difficult to decipher what comes from bb
176
247
  return {
177
248
  status: BB_RESULT.FAILURE,
178
- reason: `Failed to generate key. Exit code: ${result.exitCode}. Signal ${result.signal}.`,
249
+ reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
179
250
  };
180
251
  } catch (error) {
181
252
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
@@ -183,22 +254,18 @@ export async function generateKeyForNoirCircuit(
183
254
  }
184
255
 
185
256
  /**
186
- * Used for generating proofs of noir circuits.
257
+ * Used for generating AVM proofs.
187
258
  * It is assumed that the working directory is a temporary and/or random directory used solely for generating this proof.
188
259
  * @param pathToBB - The full path to the bb binary
189
260
  * @param workingDirectory - A working directory for use by bb
190
- * @param circuitName - An identifier for the circuit
191
- * @param bytecode - The compiled circuit bytecode
192
- * @param inputWitnessFile - The circuit input witness
261
+ * @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
193
262
  * @param log - A logging function
194
263
  * @returns An object containing a result indication, the location of the proof and the duration taken
195
264
  */
196
- export async function generateProof(
265
+ export async function generateAvmProof(
197
266
  pathToBB: string,
198
267
  workingDirectory: string,
199
- circuitName: string,
200
- bytecode: Buffer,
201
- inputWitnessFile: string,
268
+ input: AvmCircuitInputs,
202
269
  log: LogFn,
203
270
  ): Promise<BBFailure | BBSuccess> {
204
271
  // Check that the working directory exists
@@ -208,39 +275,81 @@ export async function generateProof(
208
275
  return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
209
276
  }
210
277
 
211
- // The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
212
- const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
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');
213
283
 
214
284
  // The proof is written to e.g. /workingDirectory/proof
215
- const outputPath = `${workingDirectory}`;
285
+ const outputPath = workingDirectory;
216
286
 
217
- const binaryPresent = await fs
218
- .access(pathToBB, fs.constants.R_OK)
219
- .then(_ => true)
220
- .catch(_ => false);
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);
221
294
  if (!binaryPresent) {
222
295
  return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
223
296
  }
224
297
 
225
298
  try {
226
- // Write the bytecode to the working directory
227
- await fs.writeFile(bytecodePath, bytecode);
228
- const args = ['-o', outputPath, '-b', bytecodePath, '-w', inputWitnessFile, '-v'];
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
+ ];
229
339
  const timer = new Timer();
230
340
  const logFunction = (message: string) => {
231
- log(`${circuitName} BB out - ${message}`);
341
+ log(`AvmCircuit (prove) BB out - ${message}`);
232
342
  };
233
- const result = await executeBB(pathToBB, 'prove_output_all', args, logFunction);
343
+ const result = await executeBB(pathToBB, 'avm_prove', args, logFunction);
234
344
  const duration = timer.ms();
235
- // cleanup the bytecode
236
- await fs.rm(bytecodePath, { force: true });
345
+
237
346
  if (result.status == BB_RESULT.SUCCESS) {
238
347
  return {
239
348
  status: BB_RESULT.SUCCESS,
240
349
  duration,
241
- proofPath: `${outputPath}`,
350
+ proofPath: join(outputPath, PROOF_FILENAME),
242
351
  pkPath: undefined,
243
- vkPath: `${outputPath}`,
352
+ vkPath: outputPath,
244
353
  };
245
354
  }
246
355
  // Not a great error message here but it is difficult to decipher what comes from bb
@@ -266,6 +375,42 @@ export async function verifyProof(
266
375
  proofFullPath: string,
267
376
  verificationKeyPath: string,
268
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,
269
414
  ): Promise<BBFailure | BBSuccess> {
270
415
  const binaryPresent = await fs
271
416
  .access(pathToBB, fs.constants.R_OK)
@@ -278,7 +423,7 @@ export async function verifyProof(
278
423
  try {
279
424
  const args = ['-p', proofFullPath, '-k', verificationKeyPath];
280
425
  const timer = new Timer();
281
- const result = await executeBB(pathToBB, 'verify', args, log);
426
+ const result = await executeBB(pathToBB, command, args, log);
282
427
  const duration = timer.ms();
283
428
  if (result.status == BB_RESULT.SUCCESS) {
284
429
  return { status: BB_RESULT.SUCCESS, duration };
@@ -338,6 +483,7 @@ export async function writeVkAsFields(
338
483
  * @param pathToBB - The full path to the bb binary
339
484
  * @param proofPath - The directory containing the binary proof
340
485
  * @param proofFileName - The filename of the proof
486
+ * @param vkFileName - The filename of the verification key
341
487
  * @param log - A logging function
342
488
  * @returns An object containing a result indication and duration taken
343
489
  */
@@ -345,6 +491,7 @@ export async function writeProofAsFields(
345
491
  pathToBB: string,
346
492
  proofPath: string,
347
493
  proofFileName: string,
494
+ vkFilePath: string,
348
495
  log: LogFn,
349
496
  ): Promise<BBFailure | BBSuccess> {
350
497
  const binaryPresent = await fs
@@ -356,7 +503,7 @@ export async function writeProofAsFields(
356
503
  }
357
504
 
358
505
  try {
359
- const args = ['-p', `${proofPath}/${proofFileName}`, '-v'];
506
+ const args = ['-p', `${proofPath}/${proofFileName}`, '-k', vkFilePath, '-v'];
360
507
  const timer = new Timer();
361
508
  const result = await executeBB(pathToBB, 'proof_as_fields', args, log);
362
509
  const duration = timer.ms();
@@ -372,3 +519,129 @@ export async function writeProofAsFields(
372
519
  return { status: BB_RESULT.FAILURE, reason: `${error}` };
373
520
  }
374
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
  };