@aztec/bb-prover 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e

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 (62) hide show
  1. package/dest/avm_proving_tests/avm_proving_tester.d.ts +13 -8
  2. package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +1 -1
  3. package/dest/avm_proving_tests/avm_proving_tester.js +152 -110
  4. package/dest/bb/bb_js_backend.d.ts +196 -0
  5. package/dest/bb/bb_js_backend.d.ts.map +1 -0
  6. package/dest/bb/bb_js_backend.js +379 -0
  7. package/dest/bb/bb_js_debug.d.ts +52 -0
  8. package/dest/bb/bb_js_debug.d.ts.map +1 -0
  9. package/dest/bb/bb_js_debug.js +176 -0
  10. package/dest/bb/file_names.d.ts +4 -0
  11. package/dest/bb/file_names.d.ts.map +1 -0
  12. package/dest/bb/file_names.js +5 -0
  13. package/dest/config.d.ts +17 -1
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/index.d.ts +3 -2
  16. package/dest/index.d.ts.map +1 -1
  17. package/dest/index.js +2 -1
  18. package/dest/instrumentation.d.ts +1 -1
  19. package/dest/instrumentation.d.ts.map +1 -1
  20. package/dest/instrumentation.js +12 -4
  21. package/dest/prover/client/bb_private_kernel_prover.d.ts +10 -2
  22. package/dest/prover/client/bb_private_kernel_prover.d.ts.map +1 -1
  23. package/dest/prover/client/bb_private_kernel_prover.js +39 -5
  24. package/dest/prover/proof_utils.d.ts +11 -1
  25. package/dest/prover/proof_utils.d.ts.map +1 -1
  26. package/dest/prover/proof_utils.js +24 -1
  27. package/dest/prover/server/bb_prover.d.ts +4 -5
  28. package/dest/prover/server/bb_prover.d.ts.map +1 -1
  29. package/dest/prover/server/bb_prover.js +207 -78
  30. package/dest/verification_key/verification_key_data.js +1 -1
  31. package/dest/verifier/batch_chonk_verifier.d.ts +56 -0
  32. package/dest/verifier/batch_chonk_verifier.d.ts.map +1 -0
  33. package/dest/verifier/batch_chonk_verifier.js +384 -0
  34. package/dest/verifier/bb_verifier.d.ts +4 -1
  35. package/dest/verifier/bb_verifier.d.ts.map +1 -1
  36. package/dest/verifier/bb_verifier.js +134 -45
  37. package/dest/verifier/index.d.ts +2 -1
  38. package/dest/verifier/index.d.ts.map +1 -1
  39. package/dest/verifier/index.js +1 -0
  40. package/dest/verifier/queued_chonk_verifier.d.ts +2 -3
  41. package/dest/verifier/queued_chonk_verifier.d.ts.map +1 -1
  42. package/dest/verifier/queued_chonk_verifier.js +6 -5
  43. package/package.json +19 -17
  44. package/src/avm_proving_tests/avm_proving_tester.ts +53 -126
  45. package/src/bb/bb_js_backend.ts +435 -0
  46. package/src/bb/bb_js_debug.ts +227 -0
  47. package/src/bb/file_names.ts +6 -0
  48. package/src/config.ts +16 -0
  49. package/src/index.ts +2 -1
  50. package/src/instrumentation.ts +12 -4
  51. package/src/prover/client/bb_private_kernel_prover.ts +116 -4
  52. package/src/prover/proof_utils.ts +41 -1
  53. package/src/prover/server/bb_prover.ts +132 -137
  54. package/src/verification_key/verification_key_data.ts +1 -1
  55. package/src/verifier/batch_chonk_verifier.ts +415 -0
  56. package/src/verifier/bb_verifier.ts +66 -76
  57. package/src/verifier/index.ts +1 -0
  58. package/src/verifier/queued_chonk_verifier.ts +6 -7
  59. package/dest/bb/execute.d.ts +0 -107
  60. package/dest/bb/execute.d.ts.map +0 -1
  61. package/dest/bb/execute.js +0 -647
  62. package/src/bb/execute.ts +0 -678
package/src/index.ts CHANGED
@@ -2,7 +2,8 @@ export * from './prover/index.js';
2
2
  export * from './test/index.js';
3
3
  export * from './verifier/index.js';
4
4
  export * from './config.js';
5
- export * from './bb/execute.js';
5
+ export * from './bb/file_names.js';
6
+ export * from './bb/bb_js_backend.js';
6
7
  export * from './honk.js';
7
8
  export * from './verification_key/verification_key_data.js';
8
9
 
@@ -58,10 +58,18 @@ export class ProverInstrumentation {
58
58
  circuitName: CircuitName,
59
59
  timerOrMS: Timer | number,
60
60
  ) {
61
- const s = typeof timerOrMS === 'number' ? timerOrMS / 1000 : timerOrMS.s();
62
- this[metric].record(s, {
63
- [Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
64
- });
61
+ // Simulation duration is stored in ms, while the others are stored in seconds
62
+ if (metric === 'simulationDuration') {
63
+ const ms = typeof timerOrMS === 'number' ? timerOrMS : timerOrMS.ms();
64
+ this[metric].record(Math.trunc(ms), {
65
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
66
+ });
67
+ } else {
68
+ const s = typeof timerOrMS === 'number' ? timerOrMS / 1000 : timerOrMS.s();
69
+ this[metric].record(s, {
70
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
71
+ });
72
+ }
65
73
  }
66
74
 
67
75
  /**
@@ -5,8 +5,16 @@ import { serializeWitness } from '@aztec/noir-noirc_abi';
5
5
  import {
6
6
  convertHidingKernelPublicInputsToWitnessMapWithAbi,
7
7
  convertHidingKernelToRollupInputsToWitnessMapWithAbi,
8
+ convertPrivateKernelInit2InputsToWitnessMapWithAbi,
9
+ convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
10
+ convertPrivateKernelInit3InputsToWitnessMapWithAbi,
11
+ convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
8
12
  convertPrivateKernelInitInputsToWitnessMapWithAbi,
9
13
  convertPrivateKernelInitOutputsFromWitnessMapWithAbi,
14
+ convertPrivateKernelInner2InputsToWitnessMapWithAbi,
15
+ convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
16
+ convertPrivateKernelInner3InputsToWitnessMapWithAbi,
17
+ convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
10
18
  convertPrivateKernelInnerInputsToWitnessMapWithAbi,
11
19
  convertPrivateKernelInnerOutputsFromWitnessMapWithAbi,
12
20
  convertPrivateKernelResetInputsToWitnessMapWithAbi,
@@ -32,7 +40,11 @@ import type {
32
40
  HidingKernelToRollupPrivateInputs,
33
41
  PrivateExecutionStep,
34
42
  PrivateKernelCircuitPublicInputs,
43
+ PrivateKernelInit2CircuitPrivateInputs,
44
+ PrivateKernelInit3CircuitPrivateInputs,
35
45
  PrivateKernelInitCircuitPrivateInputs,
46
+ PrivateKernelInner2CircuitPrivateInputs,
47
+ PrivateKernelInner3CircuitPrivateInputs,
36
48
  PrivateKernelInnerCircuitPrivateInputs,
37
49
  PrivateKernelResetCircuitPrivateInputs,
38
50
  PrivateKernelSimulateOutput,
@@ -79,6 +91,50 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
79
91
  );
80
92
  }
81
93
 
94
+ public async generateInit2Output(
95
+ inputs: PrivateKernelInit2CircuitPrivateInputs,
96
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
97
+ return await this.generateCircuitOutput(
98
+ inputs,
99
+ 'PrivateKernelInit2Artifact',
100
+ convertPrivateKernelInit2InputsToWitnessMapWithAbi,
101
+ convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
102
+ );
103
+ }
104
+
105
+ public async simulateInit2(
106
+ inputs: PrivateKernelInit2CircuitPrivateInputs,
107
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
108
+ return await this.simulateCircuitOutput(
109
+ inputs,
110
+ 'PrivateKernelInit2Artifact',
111
+ convertPrivateKernelInit2InputsToWitnessMapWithAbi,
112
+ convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
113
+ );
114
+ }
115
+
116
+ public async generateInit3Output(
117
+ inputs: PrivateKernelInit3CircuitPrivateInputs,
118
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
119
+ return await this.generateCircuitOutput(
120
+ inputs,
121
+ 'PrivateKernelInit3Artifact',
122
+ convertPrivateKernelInit3InputsToWitnessMapWithAbi,
123
+ convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
124
+ );
125
+ }
126
+
127
+ public async simulateInit3(
128
+ inputs: PrivateKernelInit3CircuitPrivateInputs,
129
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
130
+ return await this.simulateCircuitOutput(
131
+ inputs,
132
+ 'PrivateKernelInit3Artifact',
133
+ convertPrivateKernelInit3InputsToWitnessMapWithAbi,
134
+ convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
135
+ );
136
+ }
137
+
82
138
  public async generateInnerOutput(
83
139
  inputs: PrivateKernelInnerCircuitPrivateInputs,
84
140
  ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
@@ -101,6 +157,50 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
101
157
  );
102
158
  }
103
159
 
160
+ public async generateInner2Output(
161
+ inputs: PrivateKernelInner2CircuitPrivateInputs,
162
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
163
+ return await this.generateCircuitOutput(
164
+ inputs,
165
+ 'PrivateKernelInner2Artifact',
166
+ convertPrivateKernelInner2InputsToWitnessMapWithAbi,
167
+ convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
168
+ );
169
+ }
170
+
171
+ public async simulateInner2(
172
+ inputs: PrivateKernelInner2CircuitPrivateInputs,
173
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
174
+ return await this.simulateCircuitOutput(
175
+ inputs,
176
+ 'PrivateKernelInner2Artifact',
177
+ convertPrivateKernelInner2InputsToWitnessMapWithAbi,
178
+ convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
179
+ );
180
+ }
181
+
182
+ public async generateInner3Output(
183
+ inputs: PrivateKernelInner3CircuitPrivateInputs,
184
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
185
+ return await this.generateCircuitOutput(
186
+ inputs,
187
+ 'PrivateKernelInner3Artifact',
188
+ convertPrivateKernelInner3InputsToWitnessMapWithAbi,
189
+ convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
190
+ );
191
+ }
192
+
193
+ public async simulateInner3(
194
+ inputs: PrivateKernelInner3CircuitPrivateInputs,
195
+ ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
196
+ return await this.simulateCircuitOutput(
197
+ inputs,
198
+ 'PrivateKernelInner3Artifact',
199
+ convertPrivateKernelInner3InputsToWitnessMapWithAbi,
200
+ convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
201
+ );
202
+ }
203
+
104
204
  public async generateResetOutput(
105
205
  inputs: PrivateKernelResetCircuitPrivateInputs,
106
206
  ): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
@@ -278,7 +378,7 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
278
378
  this.log.info(`Generating ClientIVC proof...`);
279
379
  const barretenberg = await Barretenberg.initSingleton({
280
380
  ...this.options,
281
- logger: this.options.logger?.[(process.env.LOG_LEVEL as LogLevel) || 'verbose'],
381
+ logger: this.options.logger?.verbose,
282
382
  });
283
383
  const backend = new AztecClientBackend(
284
384
  executionSteps.map(step => ungzip(step.bytecode)),
@@ -286,16 +386,28 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
286
386
  executionSteps.map(step => step.functionName),
287
387
  );
288
388
 
289
- const [proof] = await backend.prove(
389
+ // Use compressed prove path to get both proof fields and compressed proof bytes
390
+ const result = await backend.prove(
290
391
  executionSteps.map(step => ungzip(serializeWitness(step.witness))),
291
392
  executionSteps.map(step => step.vk),
393
+ { compress: true },
292
394
  );
293
395
  this.log.info(`Generated ClientIVC proof`, {
294
396
  eventName: 'client-ivc-proof-generation',
295
397
  duration: timer.ms(),
296
- proofSize: proof.length,
398
+ proofSize: result.proofFields.length,
399
+ compressedSize: result.compressedProof?.length,
297
400
  });
298
- return ChonkProofWithPublicInputs.fromBufferArray(proof);
401
+
402
+ // Create ChonkProofWithPublicInputs from the flat field elements
403
+ const proofWithPubInputs = ChonkProofWithPublicInputs.fromBufferArray(result.proofFields);
404
+
405
+ // Attach compressed proof bytes to the ChonkProof (without public inputs).
406
+ // The compressed bytes are for the full proof WITH public inputs from bb;
407
+ // when deserializing, the decompressor will strip them to match CHONK_PROOF_LENGTH.
408
+ proofWithPubInputs.compressedProof = result.compressedProof ? Buffer.from(result.compressedProof) : undefined;
409
+
410
+ return proofWithPubInputs;
299
411
  }
300
412
 
301
413
  public async computeGateCountForCircuit(_bytecode: Buffer, _circuitName: string): Promise<number> {
@@ -16,7 +16,7 @@ import assert from 'assert';
16
16
  import { promises as fs } from 'fs';
17
17
  import * as path from 'path';
18
18
 
19
- import { PROOF_FILENAME, PUBLIC_INPUTS_FILENAME } from '../bb/execute.js';
19
+ import { PROOF_FILENAME, PUBLIC_INPUTS_FILENAME } from '../bb/file_names.js';
20
20
 
21
21
  /**
22
22
  * Create a ChonkProof proof file.
@@ -113,3 +113,43 @@ export async function readProofsFromOutputDirectory<PROOF_LENGTH extends number>
113
113
  proofLength,
114
114
  );
115
115
  }
116
+
117
+ /**
118
+ * Construct a RecursiveProof from in-memory proof and public input field arrays
119
+ * returned by the bb.js circuitProve API, without reading from files.
120
+ *
121
+ * @param proofFields - Proof fields as 32-byte Uint8Arrays from circuitProve.
122
+ * @param publicInputFields - Public input fields as 32-byte Uint8Arrays from circuitProve.
123
+ * @param vkData - Verification key data for the circuit.
124
+ * @param proofLength - Expected proof field count.
125
+ */
126
+ export function constructRecursiveProofFromBuffers<PROOF_LENGTH extends number>(
127
+ proofFields: Uint8Array[],
128
+ publicInputFields: Uint8Array[],
129
+ vkData: VerificationKeyData,
130
+ proofLength: PROOF_LENGTH,
131
+ ): RecursiveProof<PROOF_LENGTH> {
132
+ assert(
133
+ proofLength == NESTED_RECURSIVE_PROOF_LENGTH ||
134
+ proofLength == NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH ||
135
+ proofLength == ULTRA_KECCAK_PROOF_LENGTH,
136
+ `Proof length must be one of the expected proof lengths, received ${proofLength}`,
137
+ );
138
+
139
+ // Convert Uint8Array fields to Fr instances
140
+ const proofFieldsFr = proofFields.map(f => Fr.fromBuffer(Buffer.from(f)));
141
+
142
+ assert(
143
+ proofFieldsFr.length == proofLength,
144
+ `Proof fields length mismatch: ${proofFieldsFr.length} != ${proofLength}`,
145
+ );
146
+
147
+ // Reconstruct the binary proof with public inputs prepended (same format as file-based path)
148
+ const binaryPublicInputs = Buffer.concat(publicInputFields.map(f => Buffer.from(f)));
149
+ const binaryProof = Buffer.concat(proofFields.map(f => Buffer.from(f)));
150
+ const binaryProofWithPublicInputs = Buffer.concat([binaryPublicInputs, binaryProof]);
151
+
152
+ const numPublicInputs = getNumCustomPublicInputs(proofLength, vkData);
153
+
154
+ return new RecursiveProof(proofFieldsFr, new Proof(binaryProofWithPublicInputs, numPublicInputs), true, proofLength);
155
+ }
@@ -9,7 +9,6 @@ import {
9
9
  import { Fr } from '@aztec/foundation/curves/bn254';
10
10
  import { runInDirectory } from '@aztec/foundation/fs';
11
11
  import { createLogger } from '@aztec/foundation/log';
12
- import { BufferReader } from '@aztec/foundation/serialize';
13
12
  import {
14
13
  type ServerProtocolArtifact,
15
14
  convertBlockMergeRollupOutputsFromWitnessMap,
@@ -88,24 +87,14 @@ import { VerificationKeyData } from '@aztec/stdlib/vks';
88
87
  import { Attributes, type TelemetryClient, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
89
88
 
90
89
  import { promises as fs } from 'fs';
90
+ import { ungzip } from 'pako';
91
91
  import * as path from 'path';
92
92
 
93
- import {
94
- type BBFailure,
95
- type BBSuccess,
96
- BB_RESULT,
97
- PROOF_FILENAME,
98
- PUBLIC_INPUTS_FILENAME,
99
- VK_FILENAME,
100
- generateAvmProof,
101
- generateProof,
102
- verifyAvmProof,
103
- verifyProof,
104
- } from '../../bb/execute.js';
93
+ import { BBJsFactory, type BBJsProofResult } from '../../bb/bb_js_backend.js';
105
94
  import type { ACVMConfig, BBConfig } from '../../config.js';
106
95
  import { getUltraHonkFlavorForCircuit } from '../../honk.js';
107
96
  import { ProverInstrumentation } from '../../instrumentation.js';
108
- import { readProofsFromOutputDirectory } from '../proof_utils.js';
97
+ import { constructRecursiveProofFromBuffers } from '../proof_utils.js';
109
98
 
110
99
  const logger = createLogger('bb-prover');
111
100
 
@@ -119,12 +108,14 @@ export interface BBProverConfig extends BBConfig, ACVMConfig {
119
108
  */
120
109
  export class BBNativeRollupProver implements ServerCircuitProver {
121
110
  private instrumentation: ProverInstrumentation;
111
+ private bbJsFactory: BBJsFactory;
122
112
 
123
113
  constructor(
124
114
  private config: BBProverConfig,
125
115
  telemetry: TelemetryClient,
126
116
  ) {
127
117
  this.instrumentation = new ProverInstrumentation(telemetry, 'BBNativeRollupProver');
118
+ this.bbJsFactory = new BBJsFactory(config.bbBinaryPath, { logger, debugDir: config.bbDebugOutputDir });
128
119
  }
129
120
 
130
121
  get tracer() {
@@ -136,7 +127,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
136
127
  await fs.mkdir(config.acvmWorkingDirectory, { recursive: true });
137
128
  await fs.access(config.bbBinaryPath, fs.constants.R_OK);
138
129
  await fs.mkdir(config.bbWorkingDirectory, { recursive: true });
139
- logger.info(`Using native BB at ${config.bbBinaryPath} and working directory ${config.bbWorkingDirectory}`);
130
+ logger.info(`Using bb.js API with binary at ${config.bbBinaryPath}`);
140
131
  logger.info(`Using native ACVM at ${config.acvmBinaryPath} and working directory ${config.acvmWorkingDirectory}`);
141
132
 
142
133
  return new BBNativeRollupProver(config, telemetry);
@@ -453,12 +444,11 @@ export class BBNativeRollupProver implements ServerCircuitProver {
453
444
  convertInput: (input: Input) => WitnessMap,
454
445
  convertOutput: (outputWitness: WitnessMap) => Output,
455
446
  workingDirectory: string,
456
- ): Promise<{ circuitOutput: Output; provingResult: BBSuccess }> {
457
- // Have the ACVM write the partial witness here
447
+ ): Promise<{ circuitOutput: Output; proofResult: BBJsProofResult }> {
448
+ // Have the ACVM write the partial witness here (still needs a temp directory)
458
449
  const outputWitnessFile = path.join(workingDirectory, 'partial-witness.gz');
459
450
 
460
451
  // Generate the partial witness using the ACVM
461
- // A further temp directory will be created beneath ours and then cleaned up after the partial witness has been copied to our specified location
462
452
  const simulator = new NativeACVMSimulator(
463
453
  this.config.acvmWorkingDirectory,
464
454
  this.config.acvmBinaryPath,
@@ -471,7 +461,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
471
461
  logger.debug(`Generating witness data for ${circuitType}`);
472
462
 
473
463
  const inputWitness = convertInput(input);
474
- const foreignCallHandler = undefined; // We don't handle foreign calls in the native ACVM simulator
464
+ const foreignCallHandler = undefined;
475
465
  const witnessResult = await simulator.executeProtocolCircuit(inputWitness, artifact, foreignCallHandler);
476
466
  const output = convertOutput(witnessResult.witness);
477
467
 
@@ -488,75 +478,80 @@ export class BBNativeRollupProver implements ServerCircuitProver {
488
478
  eventName: 'circuit-witness-generation',
489
479
  } satisfies CircuitWitnessGenerationStats);
490
480
 
491
- // Now prove the circuit from the generated witness
492
- logger.debug(`Proving ${circuitType}...`);
481
+ // Read and decompress the witness for bb.js
482
+ const witnessGz = await fs.readFile(outputWitnessFile);
483
+ const witness = ungzip(witnessGz);
493
484
 
494
- const provingResult = await generateProof(
495
- this.config.bbBinaryPath,
496
- workingDirectory,
497
- circuitType,
498
- Buffer.from(artifact.bytecode, 'base64'),
499
- this.getVerificationKeyDataForCircuit(circuitType).keyAsBytes,
500
- outputWitnessFile,
501
- getUltraHonkFlavorForCircuit(circuitType),
502
- logger,
503
- );
485
+ // Decompress bytecode for bb.js
486
+ const bytecode = ungzip(Buffer.from(artifact.bytecode, 'base64'));
504
487
 
505
- if (provingResult.status === BB_RESULT.FAILURE) {
506
- logger.error(`Failed to generate proof for ${circuitType}: ${provingResult.reason}`);
507
- throw new ProvingError(provingResult.reason, provingResult, provingResult.retry);
488
+ // Prove the circuit via bb.js API
489
+ logger.debug(`Proving ${circuitType} via bb.js...`);
490
+
491
+ let proofResult: BBJsProofResult;
492
+ try {
493
+ await using instance = await this.bbJsFactory.getInstance();
494
+ proofResult = await instance.generateProof(
495
+ circuitType,
496
+ bytecode,
497
+ this.getVerificationKeyDataForCircuit(circuitType).keyAsBytes,
498
+ witness,
499
+ getUltraHonkFlavorForCircuit(circuitType),
500
+ );
501
+ } catch (error) {
502
+ throw new ProvingError(`Failed to generate proof for ${circuitType}: ${error}`);
508
503
  }
509
504
 
510
505
  return {
511
506
  circuitOutput: output,
512
- provingResult,
507
+ proofResult,
513
508
  };
514
509
  }
515
510
 
516
- private async generateAvmProofWithBB(input: AvmCircuitInputs, workingDirectory: string): Promise<BBSuccess> {
517
- logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
518
-
519
- const provingResult = await generateAvmProof(this.config.bbBinaryPath, workingDirectory, input, logger);
520
-
521
- if (provingResult.status === BB_RESULT.FAILURE) {
522
- logger.error(`Failed to generate AVM proof for TX ${input.hints.tx.hash}: ${provingResult.reason}`);
523
- throw new ProvingError(provingResult.reason, provingResult, provingResult.retry);
524
- }
525
-
526
- return provingResult;
527
- }
528
-
529
511
  private async createAvmProof(
530
512
  input: AvmCircuitInputs,
531
513
  ): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
532
- const operation = async (bbWorkingDirectory: string) => {
533
- const provingResult = await this.generateAvmProofWithBB(input, bbWorkingDirectory);
514
+ logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
534
515
 
535
- const avmProof = await this.readAvmProofAsFields(provingResult.proofPath!);
516
+ const inputsBuffer = input.serializeWithMessagePack();
517
+ await using instance = await this.bbJsFactory.getInstance();
518
+ const { proof: proofFieldArrays, durationMs } = await instance.generateAvmProof(inputsBuffer);
536
519
 
537
- const circuitType = 'avm-circuit' as const;
538
- const appCircuitName = 'unknown' as const;
539
- this.instrumentation.recordAvmDuration('provingDuration', appCircuitName, provingResult.durationMs);
540
- this.instrumentation.recordAvmSize('proofSize', appCircuitName, avmProof.binaryProof.buffer.length);
520
+ // Convert Uint8Array[] (32-byte field elements) to Fr[]
521
+ const proofFields = proofFieldArrays.map(f => Fr.fromBuffer(Buffer.from(f)));
541
522
 
542
- logger.info(
543
- `Generated proof for ${circuitType}(${input.hints.tx.hash}) in ${Math.ceil(provingResult.durationMs)} ms`,
544
- {
545
- circuitName: circuitType,
546
- appCircuitName: input.hints.tx.hash,
547
- // does not include reading the proof from disk
548
- duration: provingResult.durationMs,
549
- proofSize: avmProof.binaryProof.buffer.length,
550
- eventName: 'circuit-proving',
551
- inputSize: input.serializeWithMessagePack().length,
552
- circuitSize: 1 << 21,
553
- numPublicInputs: 0,
554
- } satisfies CircuitProvingStats,
523
+ // Pad to fixed size (during development the proof length may vary)
524
+ if (proofFields.length > AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED) {
525
+ throw new Error(
526
+ `Proof has ${proofFields.length} fields, expected no more than ${AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED}.`,
555
527
  );
528
+ }
529
+ const proofFieldsPadded = proofFields.concat(
530
+ Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
531
+ );
556
532
 
557
- return avmProof;
558
- };
559
- return await this.runInDirectory(operation);
533
+ // Build the binary proof from the raw field data
534
+ const rawProofBuffer = Buffer.concat(proofFieldArrays.map(f => Buffer.from(f)));
535
+ const binaryProof = new Proof(rawProofBuffer, /*numPublicInputs=*/ 0);
536
+ const avmProof = new RecursiveProof(proofFieldsPadded, binaryProof, true, AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
537
+
538
+ const circuitType = 'avm-circuit' as const;
539
+ const appCircuitName = 'unknown' as const;
540
+ this.instrumentation.recordAvmDuration('provingDuration', appCircuitName, durationMs);
541
+ this.instrumentation.recordAvmSize('proofSize', appCircuitName, avmProof.binaryProof.buffer.length);
542
+
543
+ logger.info(`Generated proof for ${circuitType}(${input.hints.tx.hash}) in ${Math.ceil(durationMs)} ms`, {
544
+ circuitName: circuitType,
545
+ appCircuitName: input.hints.tx.hash,
546
+ duration: durationMs,
547
+ proofSize: avmProof.binaryProof.buffer.length,
548
+ eventName: 'circuit-proving',
549
+ inputSize: inputsBuffer.length,
550
+ circuitSize: 1 << 21,
551
+ numPublicInputs: 0,
552
+ } satisfies CircuitProvingStats);
553
+
554
+ return avmProof;
560
555
  }
561
556
 
562
557
  /**
@@ -579,33 +574,38 @@ export class BBNativeRollupProver implements ServerCircuitProver {
579
574
  convertInput: (input: CircuitInputType) => WitnessMap,
580
575
  convertOutput: (outputWitness: WitnessMap) => CircuitOutputType,
581
576
  ): Promise<{ circuitOutput: CircuitOutputType; proof: RecursiveProof<PROOF_LENGTH> }> {
582
- // this probably is gonna need to call chonk
583
- const operation = async (bbWorkingDirectory: string) => {
584
- const { provingResult, circuitOutput: output } = await this.generateProofWithBB(
577
+ // Still need runInDirectory for ACVM witness generation temp files
578
+ const operation = async (workingDirectory: string) => {
579
+ const { proofResult, circuitOutput: output } = await this.generateProofWithBB(
585
580
  input,
586
581
  circuitType,
587
582
  convertInput,
588
583
  convertOutput,
589
- bbWorkingDirectory,
584
+ workingDirectory,
590
585
  );
591
586
 
592
587
  const vkData = this.getVerificationKeyDataForCircuit(circuitType);
593
- // Read the proof as fields
594
- const proof = await readProofsFromOutputDirectory(provingResult.proofPath!, vkData, proofLength, logger);
588
+ // Construct proof from in-memory buffers (no file I/O needed)
589
+ const proof = constructRecursiveProofFromBuffers(
590
+ proofResult.proofFields,
591
+ proofResult.publicInputFields,
592
+ vkData,
593
+ proofLength,
594
+ );
595
595
 
596
596
  const circuitName = mapProtocolArtifactNameToCircuitName(circuitType);
597
- this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs);
597
+ this.instrumentation.recordDuration('provingDuration', circuitName, proofResult.durationMs);
598
598
  this.instrumentation.recordSize('proofSize', circuitName, proof.binaryProof.buffer.length);
599
599
  this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs);
600
600
  this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize);
601
601
  logger.info(
602
- `Generated proof for ${circuitType} in ${Math.ceil(provingResult.durationMs)} ms, size: ${
602
+ `Generated proof for ${circuitType} in ${Math.ceil(proofResult.durationMs)} ms, size: ${
603
603
  proof.proof.length
604
604
  } fields`,
605
605
  {
606
606
  circuitName,
607
607
  circuitSize: vkData.circuitSize,
608
- duration: provingResult.durationMs,
608
+ duration: proofResult.durationMs,
609
609
  inputSize: output.toBuffer().length,
610
610
  proofSize: proof.binaryProof.buffer.length,
611
611
  eventName: 'circuit-proving',
@@ -622,49 +622,58 @@ export class BBNativeRollupProver implements ServerCircuitProver {
622
622
  }
623
623
 
624
624
  /**
625
- * Verifies a proof, will generate the verification key if one is not cached internally
625
+ * Verifies a proof via bb.js API (no temp files needed).
626
626
  * @param circuitType - The type of circuit whose proof is to be verified
627
627
  * @param proof - The proof to be verified
628
628
  */
629
629
  public async verifyProof(circuitType: ServerProtocolArtifact, proof: Proof) {
630
630
  const verificationKey = this.getVerificationKeyDataForCircuit(circuitType);
631
- return await this.verifyInternal(proof, verificationKey, (proofPath, vkPath) =>
632
- verifyProof(this.config.bbBinaryPath, proofPath, vkPath, getUltraHonkFlavorForCircuit(circuitType), logger),
633
- );
631
+ const flavor = getUltraHonkFlavorForCircuit(circuitType);
632
+
633
+ // Split proof buffer into public input fields and proof fields (32-byte each)
634
+ const publicInputFields = splitBufferToFieldArrays(proof.buffer.subarray(0, proof.numPublicInputs * 32));
635
+ const proofFields = splitBufferToFieldArrays(proof.buffer.subarray(proof.numPublicInputs * 32));
636
+
637
+ let verified: boolean;
638
+ let durationMs: number;
639
+ try {
640
+ await using instance = await this.bbJsFactory.getInstance();
641
+ ({ verified, durationMs } = await instance.verifyProof(
642
+ proofFields,
643
+ verificationKey.keyAsBytes,
644
+ publicInputFields,
645
+ flavor,
646
+ ));
647
+ } catch (error) {
648
+ throw new ProvingError(`Failed to verify proof for ${circuitType}: ${error}`);
649
+ }
650
+
651
+ if (!verified) {
652
+ throw new ProvingError('Failed to verify proof from key!');
653
+ }
654
+
655
+ logger.info(`Successfully verified proof from key in ${durationMs} ms`);
634
656
  }
635
657
 
658
+ /** Verify an AVM proof via bb.js API. */
636
659
  public async verifyAvmProof(proof: Proof, publicInputs: AvmCircuitPublicInputs) {
637
- return await this.verifyInternal(proof, /*verificationKey=*/ undefined, (proofPath, /*unused*/ _vkPath) =>
638
- verifyAvmProof(this.config.bbBinaryPath, this.config.bbWorkingDirectory, proofPath, publicInputs, logger),
639
- );
640
- }
641
- private async verifyInternal(
642
- proof: Proof,
643
- verificationKey: { keyAsBytes: Buffer } | undefined,
644
- verificationFunction: (proofPath: string, vkPath: string) => Promise<BBFailure | BBSuccess>,
645
- ) {
646
- const operation = async (bbWorkingDirectory: string) => {
647
- const publicInputsFileName = path.join(bbWorkingDirectory, PUBLIC_INPUTS_FILENAME);
648
- const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME);
649
- const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME);
650
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/13189): Put this proof parsing logic in the proof class.
651
- await fs.writeFile(publicInputsFileName, proof.buffer.subarray(0, proof.numPublicInputs * 32));
652
- await fs.writeFile(proofFileName, proof.buffer.subarray(proof.numPublicInputs * 32));
653
- if (verificationKey !== undefined) {
654
- await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
655
- }
656
-
657
- const result = await verificationFunction(proofFileName, verificationKeyPath);
658
-
659
- if (result.status === BB_RESULT.FAILURE) {
660
- const errorMessage = `Failed to verify proof from key!`;
661
- throw new ProvingError(errorMessage, result, result.retry);
662
- }
663
-
664
- logger.info(`Successfully verified proof from key in ${result.durationMs} ms`);
665
- };
660
+ // For AVM proofs, numPublicInputs is 0, so the full buffer is the proof.
661
+ const proofBuffer = proof.buffer.subarray(proof.numPublicInputs * 32);
662
+ // Split the raw proof buffer into 32-byte field element arrays
663
+ const proofFields: Uint8Array[] = [];
664
+ for (let i = 0; i < proofBuffer.length; i += Fr.SIZE_IN_BYTES) {
665
+ proofFields.push(new Uint8Array(proofBuffer.subarray(i, i + Fr.SIZE_IN_BYTES)));
666
+ }
667
+ const piBuffer = publicInputs.serializeWithMessagePack();
668
+
669
+ await using instance = await this.bbJsFactory.getInstance();
670
+ const { verified, durationMs } = await instance.verifyAvmProof(proofFields, piBuffer);
666
671
 
667
- await this.runInDirectory(operation);
672
+ if (!verified) {
673
+ throw new ProvingError('Failed to verify AVM proof!');
674
+ }
675
+
676
+ logger.info(`Successfully verified AVM proof in ${durationMs} ms`);
668
677
  }
669
678
 
670
679
  /**
@@ -680,29 +689,6 @@ export class BBNativeRollupProver implements ServerCircuitProver {
680
689
  return vk;
681
690
  }
682
691
 
683
- private async readAvmProofAsFields(
684
- proofFilename: string,
685
- ): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
686
- const rawProofBuffer = await fs.readFile(proofFilename);
687
- const reader = BufferReader.asReader(rawProofBuffer);
688
- const proofFields = reader.readArray(rawProofBuffer.length / Fr.SIZE_IN_BYTES, Fr);
689
-
690
- // We extend to a fixed-size padded proof as during development any new AVM circuit column changes the
691
- // proof length and we do not have a mechanism to feedback a cpp constant to noir/TS.
692
- // TODO(#13390): Revive a non-padded AVM proof
693
- if (proofFields.length > AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED) {
694
- throw new Error(
695
- `Proof has ${proofFields.length} fields, expected no more than ${AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED}.`,
696
- );
697
- }
698
- const proofFieldsPadded = proofFields.concat(
699
- Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
700
- );
701
-
702
- const proof = new Proof(rawProofBuffer, /*numPublicInputs=*/ 0);
703
- return new RecursiveProof(proofFieldsPadded, proof, true, AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
704
- }
705
-
706
692
  private runInDirectory<T>(fn: (dir: string) => Promise<T>) {
707
693
  return runInDirectory(
708
694
  this.config.bbWorkingDirectory,
@@ -716,3 +702,12 @@ export class BBNativeRollupProver implements ServerCircuitProver {
716
702
  );
717
703
  }
718
704
  }
705
+
706
+ /** Split a buffer into 32-byte Uint8Array field elements. */
707
+ function splitBufferToFieldArrays(buffer: Buffer): Uint8Array[] {
708
+ const fields: Uint8Array[] = [];
709
+ for (let i = 0; i < buffer.length; i += 32) {
710
+ fields.push(new Uint8Array(buffer.subarray(i, i + 32)));
711
+ }
712
+ return fields;
713
+ }
@@ -5,7 +5,7 @@ import { VerificationKeyAsFields, VerificationKeyData } from '@aztec/stdlib/vks'
5
5
  import { promises as fs } from 'fs';
6
6
  import * as path from 'path';
7
7
 
8
- import { VK_FILENAME } from '../bb/execute.js';
8
+ import { VK_FILENAME } from '../bb/file_names.js';
9
9
 
10
10
  /**
11
11
  * Reads the verification key data stored at the specified location and parses into a VerificationKeyData