@aztec/bb-prover 0.40.0 → 0.41.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.
@@ -1,4 +1,5 @@
1
1
  import { type AppCircuitProofOutput, type KernelProofOutput, type ProofCreator } from '@aztec/circuit-types';
2
+ import { type CircuitProvingStats, type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
2
3
  import {
3
4
  Fr,
4
5
  NESTED_RECURSIVE_PROOF_LENGTH,
@@ -6,6 +7,7 @@ import {
6
7
  type PrivateKernelCircuitPublicInputs,
7
8
  type PrivateKernelInitCircuitPrivateInputs,
8
9
  type PrivateKernelInnerCircuitPrivateInputs,
10
+ type PrivateKernelResetCircuitPrivateInputsVariants,
9
11
  type PrivateKernelTailCircuitPrivateInputs,
10
12
  type PrivateKernelTailCircuitPublicInputs,
11
13
  Proof,
@@ -18,13 +20,17 @@ import { siloNoteHash } from '@aztec/circuits.js/hash';
18
20
  import { randomBytes } from '@aztec/foundation/crypto';
19
21
  import { createDebugLogger } from '@aztec/foundation/log';
20
22
  import { type Tuple } from '@aztec/foundation/serialize';
23
+ import { Timer } from '@aztec/foundation/timer';
21
24
  import {
22
25
  ClientCircuitArtifacts,
23
26
  type ClientProtocolArtifact,
27
+ PrivateResetTagToArtifactName,
24
28
  convertPrivateKernelInitInputsToWitnessMap,
25
29
  convertPrivateKernelInitOutputsFromWitnessMap,
26
30
  convertPrivateKernelInnerInputsToWitnessMap,
27
31
  convertPrivateKernelInnerOutputsFromWitnessMap,
32
+ convertPrivateKernelResetInputsToWitnessMap,
33
+ convertPrivateKernelResetOutputsFromWitnessMap,
28
34
  convertPrivateKernelTailForPublicOutputsFromWitnessMap,
29
35
  convertPrivateKernelTailInputsToWitnessMap,
30
36
  convertPrivateKernelTailOutputsFromWitnessMap,
@@ -47,6 +53,7 @@ import {
47
53
  generateProof,
48
54
  verifyProof,
49
55
  } from '../bb/execute.js';
56
+ import { mapProtocolArtifactNameToCircuitName } from '../stats.js';
50
57
  import {
51
58
  AGGREGATION_OBJECT_SIZE,
52
59
  CIRCUIT_PUBLIC_INPUTS_INDEX,
@@ -55,25 +62,6 @@ import {
55
62
  type VerificationKeyData,
56
63
  } from './verification_key_data.js';
57
64
 
58
- type PrivateKernelProvingOps = {
59
- convertOutputs: (outputs: WitnessMap) => PrivateKernelCircuitPublicInputs | PrivateKernelTailCircuitPublicInputs;
60
- };
61
-
62
- const PrivateKernelArtifactMapping: Record<ClientProtocolArtifact, PrivateKernelProvingOps> = {
63
- PrivateKernelInitArtifact: {
64
- convertOutputs: convertPrivateKernelInitOutputsFromWitnessMap,
65
- },
66
- PrivateKernelInnerArtifact: {
67
- convertOutputs: convertPrivateKernelInnerOutputsFromWitnessMap,
68
- },
69
- PrivateKernelTailArtifact: {
70
- convertOutputs: convertPrivateKernelTailOutputsFromWitnessMap,
71
- },
72
- PrivateKernelTailToPublicArtifact: {
73
- convertOutputs: convertPrivateKernelTailForPublicOutputsFromWitnessMap,
74
- },
75
- };
76
-
77
65
  /**
78
66
  * This proof creator implementation uses the native bb binary.
79
67
  * This is a temporary implementation until we make the WASM version work.
@@ -103,38 +91,66 @@ export class BBNativeProofCreator implements ProofCreator {
103
91
  public async createProofInit(
104
92
  inputs: PrivateKernelInitCircuitPrivateInputs,
105
93
  ): Promise<KernelProofOutput<PrivateKernelCircuitPublicInputs>> {
106
- const witnessMap = convertPrivateKernelInitInputsToWitnessMap(inputs);
107
- return await this.createSafeProof(witnessMap, 'PrivateKernelInitArtifact');
94
+ return await this.createSafeProof(
95
+ inputs,
96
+ 'PrivateKernelInitArtifact',
97
+ convertPrivateKernelInitInputsToWitnessMap,
98
+ convertPrivateKernelInitOutputsFromWitnessMap,
99
+ );
108
100
  }
109
101
 
110
102
  public async createProofInner(
111
103
  inputs: PrivateKernelInnerCircuitPrivateInputs,
112
104
  ): Promise<KernelProofOutput<PrivateKernelCircuitPublicInputs>> {
113
- const witnessMap = convertPrivateKernelInnerInputsToWitnessMap(inputs);
114
- return await this.createSafeProof(witnessMap, 'PrivateKernelInnerArtifact');
105
+ return await this.createSafeProof(
106
+ inputs,
107
+ 'PrivateKernelInnerArtifact',
108
+ convertPrivateKernelInnerInputsToWitnessMap,
109
+ convertPrivateKernelInnerOutputsFromWitnessMap,
110
+ );
111
+ }
112
+
113
+ public async createProofReset(
114
+ inputs: PrivateKernelResetCircuitPrivateInputsVariants,
115
+ ): Promise<KernelProofOutput<PrivateKernelCircuitPublicInputs>> {
116
+ return await this.createSafeProof(
117
+ inputs,
118
+ PrivateResetTagToArtifactName[inputs.sizeTag],
119
+ convertPrivateKernelResetInputsToWitnessMap,
120
+ output => convertPrivateKernelResetOutputsFromWitnessMap(output, inputs.sizeTag),
121
+ );
115
122
  }
116
123
 
117
124
  public async createProofTail(
118
125
  inputs: PrivateKernelTailCircuitPrivateInputs,
119
126
  ): Promise<KernelProofOutput<PrivateKernelTailCircuitPublicInputs>> {
120
127
  if (!inputs.isForPublic()) {
121
- const witnessMap = convertPrivateKernelTailInputsToWitnessMap(inputs);
122
- return await this.createSafeProof(witnessMap, 'PrivateKernelTailArtifact');
128
+ return await this.createSafeProof(
129
+ inputs,
130
+ 'PrivateKernelTailArtifact',
131
+ convertPrivateKernelTailInputsToWitnessMap,
132
+ convertPrivateKernelTailOutputsFromWitnessMap,
133
+ );
123
134
  }
124
- const witnessMap = convertPrivateKernelTailToPublicInputsToWitnessMap(inputs);
125
- return await this.createSafeProof(witnessMap, 'PrivateKernelTailToPublicArtifact');
135
+ return await this.createSafeProof(
136
+ inputs,
137
+ 'PrivateKernelTailToPublicArtifact',
138
+ convertPrivateKernelTailToPublicInputsToWitnessMap,
139
+ convertPrivateKernelTailForPublicOutputsFromWitnessMap,
140
+ );
126
141
  }
127
142
 
128
143
  public async createAppCircuitProof(
129
144
  partialWitness: Map<number, ACVMField>,
130
145
  bytecode: Buffer,
146
+ appCircuitName?: string,
131
147
  ): Promise<AppCircuitProofOutput> {
132
148
  const directory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
133
149
  await fs.mkdir(directory, { recursive: true });
134
150
  this.log.debug(`Created directory: ${directory}`);
135
151
  try {
136
152
  this.log.debug(`Proving app circuit`);
137
- const proofOutput = await this.createProof(directory, partialWitness, bytecode, 'App');
153
+ const proofOutput = await this.createProof(directory, partialWitness, bytecode, 'App', 0, 0, appCircuitName);
138
154
  if (proofOutput.proof.proof.length != RECURSIVE_PROOF_LENGTH) {
139
155
  throw new Error(`Incorrect proof length`);
140
156
  }
@@ -263,48 +279,66 @@ export class BBNativeProofCreator implements ProofCreator {
263
279
  return await promise;
264
280
  }
265
281
 
266
- private async createSafeProof<T>(
267
- inputs: WitnessMap,
282
+ private async createSafeProof<I extends { toBuffer: () => Buffer }, O extends { toBuffer: () => Buffer }>(
283
+ inputs: I,
268
284
  circuitType: ClientProtocolArtifact,
269
- ): Promise<KernelProofOutput<T>> {
285
+ convertInputs: (inputs: I) => WitnessMap,
286
+ convertOutputs: (outputs: WitnessMap) => O,
287
+ ): Promise<KernelProofOutput<O>> {
270
288
  const directory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
271
289
  await fs.mkdir(directory, { recursive: true });
272
290
  this.log.debug(`Created directory: ${directory}`);
273
291
  try {
274
- return await this.generateWitnessAndCreateProof(inputs, circuitType, directory);
292
+ return await this.generateWitnessAndCreateProof(inputs, circuitType, directory, convertInputs, convertOutputs);
275
293
  } finally {
276
294
  await fs.rm(directory, { recursive: true, force: true });
277
295
  this.log.debug(`Deleted directory: ${directory}`);
278
296
  }
279
297
  }
280
298
 
281
- private async generateWitnessAndCreateProof<T>(
282
- inputs: WitnessMap,
299
+ private async generateWitnessAndCreateProof<
300
+ I extends { toBuffer: () => Buffer },
301
+ O extends { toBuffer: () => Buffer },
302
+ >(
303
+ inputs: I,
283
304
  circuitType: ClientProtocolArtifact,
284
305
  directory: string,
285
- ): Promise<KernelProofOutput<T>> {
306
+ convertInputs: (inputs: I) => WitnessMap,
307
+ convertOutputs: (outputs: WitnessMap) => O,
308
+ ): Promise<KernelProofOutput<O>> {
286
309
  this.log.debug(`Generating witness for ${circuitType}`);
287
310
  const compiledCircuit: NoirCompiledCircuit = ClientCircuitArtifacts[circuitType];
288
311
 
289
- const outputWitness = await this.simulator.simulateCircuit(inputs, compiledCircuit);
290
-
291
- this.log.debug(`Generated witness for ${circuitType}`);
292
-
293
- const publicInputs = PrivateKernelArtifactMapping[circuitType].convertOutputs(outputWitness) as T;
312
+ const witnessMap = convertInputs(inputs);
313
+ const timer = new Timer();
314
+ const outputWitness = await this.simulator.simulateCircuit(witnessMap, compiledCircuit);
315
+ const output = convertOutputs(outputWitness);
316
+
317
+ const inputSize = inputs.toBuffer().length;
318
+ const outputSize = output.toBuffer().length;
319
+ this.log.debug(`Generated witness for ${circuitType}`, {
320
+ eventName: 'circuit-witness-generation',
321
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
322
+ duration: timer.ms(),
323
+ inputSize,
324
+ outputSize,
325
+ } satisfies CircuitWitnessGenerationStats);
294
326
 
295
327
  const proofOutput = await this.createProof(
296
328
  directory,
297
329
  outputWitness,
298
330
  Buffer.from(compiledCircuit.bytecode, 'base64'),
299
331
  circuitType,
332
+ inputSize,
333
+ outputSize,
300
334
  );
301
335
  if (proofOutput.proof.proof.length != NESTED_RECURSIVE_PROOF_LENGTH) {
302
336
  throw new Error(`Incorrect proof length`);
303
337
  }
304
338
  const nestedProof = proofOutput.proof as RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH>;
305
339
 
306
- const kernelOutput: KernelProofOutput<T> = {
307
- publicInputs,
340
+ const kernelOutput: KernelProofOutput<O> = {
341
+ publicInputs: output,
308
342
  proof: nestedProof,
309
343
  verificationKey: proofOutput.verificationKey,
310
344
  };
@@ -316,6 +350,9 @@ export class BBNativeProofCreator implements ProofCreator {
316
350
  partialWitness: WitnessMap,
317
351
  bytecode: Buffer,
318
352
  circuitType: ClientProtocolArtifact | 'App',
353
+ inputSize: number,
354
+ outputSize: number,
355
+ appCircuitName?: string,
319
356
  ): Promise<{
320
357
  proof: RecursiveProof<typeof RECURSIVE_PROOF_LENGTH> | RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH>;
321
358
  verificationKey: VerificationKeyAsFields;
@@ -345,11 +382,36 @@ export class BBNativeProofCreator implements ProofCreator {
345
382
  if (circuitType === 'App') {
346
383
  const vkData = await this.convertVk(directory);
347
384
  const proof = await this.readProofAsFields<typeof RECURSIVE_PROOF_LENGTH>(directory, circuitType, vkData);
385
+
386
+ this.log.debug(`Generated proof`, {
387
+ eventName: 'circuit-proving',
388
+ circuitName: 'app-circuit',
389
+ duration: provingResult.duration,
390
+ inputSize,
391
+ outputSize,
392
+ proofSize: proof.binaryProof.buffer.length,
393
+ appCircuitName,
394
+ circuitSize: vkData.circuitSize,
395
+ numPublicInputs: vkData.numPublicInputs,
396
+ } as CircuitProvingStats);
397
+
348
398
  return { proof, verificationKey: new VerificationKeyAsFields(vkData.keyAsFields, vkData.hash) };
349
399
  }
350
400
 
351
401
  const vkData = await this.updateVerificationKeyAfterProof(directory, circuitType);
352
402
  const proof = await this.readProofAsFields<typeof NESTED_RECURSIVE_PROOF_LENGTH>(directory, circuitType, vkData);
403
+
404
+ this.log.debug(`Generated proof`, {
405
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
406
+ duration: provingResult.duration,
407
+ eventName: 'circuit-proving',
408
+ inputSize,
409
+ outputSize,
410
+ proofSize: proof.binaryProof.buffer.length,
411
+ circuitSize: vkData.circuitSize,
412
+ numPublicInputs: vkData.numPublicInputs,
413
+ } as CircuitProvingStats);
414
+
353
415
  return { proof, verificationKey: new VerificationKeyAsFields(vkData.keyAsFields, vkData.hash) };
354
416
  }
355
417
 
@@ -7,6 +7,7 @@ import {
7
7
  type ServerCircuitProver,
8
8
  makePublicInputsAndProof,
9
9
  } from '@aztec/circuit-types';
10
+ import { type CircuitProvingStats, type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
10
11
  import {
11
12
  type BaseOrMergeRollupPublicInputs,
12
13
  type BaseParityInputs,
@@ -14,12 +15,11 @@ import {
14
15
  Fr,
15
16
  type KernelCircuitPublicInputs,
16
17
  type MergeRollupInputs,
17
- type NESTED_RECURSIVE_PROOF_LENGTH,
18
- type ParityPublicInputs,
18
+ NESTED_RECURSIVE_PROOF_LENGTH,
19
19
  type PreviousRollupData,
20
20
  Proof,
21
21
  type PublicKernelCircuitPublicInputs,
22
- type RECURSIVE_PROOF_LENGTH,
22
+ RECURSIVE_PROOF_LENGTH,
23
23
  RecursiveProof,
24
24
  RollupTypes,
25
25
  RootParityInput,
@@ -65,7 +65,7 @@ import {
65
65
  verifyProof,
66
66
  } from '../bb/execute.js';
67
67
  import { PublicKernelArtifactMapping } from '../mappings/mappings.js';
68
- import { circuitTypeToCircuitName, emitCircuitProvingStats, emitCircuitWitnessGenerationStats } from '../stats.js';
68
+ import { mapProtocolArtifactNameToCircuitName } from '../stats.js';
69
69
  import {
70
70
  AGGREGATION_OBJECT_SIZE,
71
71
  CIRCUIT_PUBLIC_INPUTS_INDEX,
@@ -114,11 +114,11 @@ export class BBNativeRollupProver implements ServerCircuitProver {
114
114
  * @returns The public inputs of the parity circuit.
115
115
  */
116
116
  public async getBaseParityProof(inputs: BaseParityInputs): Promise<RootParityInput<typeof RECURSIVE_PROOF_LENGTH>> {
117
- const witnessMap = convertBaseParityInputsToWitnessMap(inputs);
118
-
119
- const [circuitOutput, proof] = await this.createRecursiveProof<typeof RECURSIVE_PROOF_LENGTH, ParityPublicInputs>(
120
- witnessMap,
117
+ const [circuitOutput, proof] = await this.createRecursiveProof(
118
+ inputs,
121
119
  'BaseParityArtifact',
120
+ RECURSIVE_PROOF_LENGTH,
121
+ convertBaseParityInputsToWitnessMap,
122
122
  convertBaseParityOutputsFromWitnessMap,
123
123
  );
124
124
 
@@ -137,12 +137,13 @@ export class BBNativeRollupProver implements ServerCircuitProver {
137
137
  public async getRootParityProof(
138
138
  inputs: RootParityInputs,
139
139
  ): Promise<RootParityInput<typeof NESTED_RECURSIVE_PROOF_LENGTH>> {
140
- const witnessMap = convertRootParityInputsToWitnessMap(inputs);
141
-
142
- const [circuitOutput, proof] = await this.createRecursiveProof<
143
- typeof NESTED_RECURSIVE_PROOF_LENGTH,
144
- ParityPublicInputs
145
- >(witnessMap, 'RootParityArtifact', convertRootParityOutputsFromWitnessMap);
140
+ const [circuitOutput, proof] = await this.createRecursiveProof(
141
+ inputs,
142
+ 'RootParityArtifact',
143
+ NESTED_RECURSIVE_PROOF_LENGTH,
144
+ convertRootParityInputsToWitnessMap,
145
+ convertRootParityOutputsFromWitnessMap,
146
+ );
146
147
 
147
148
  const verificationKey = await this.getVerificationKeyDataForCircuit('RootParityArtifact');
148
149
 
@@ -163,11 +164,13 @@ export class BBNativeRollupProver implements ServerCircuitProver {
163
164
  if (kernelOps === undefined) {
164
165
  throw new Error(`Unable to prove kernel type ${PublicKernelType[kernelRequest.type]}`);
165
166
  }
166
- const witnessMap = kernelOps.convertInputs(kernelRequest.inputs);
167
-
168
- const [outputWitness, proof] = await this.createProof(witnessMap, kernelOps.artifact);
167
+ const [result, proof] = await this.createProof(
168
+ kernelRequest.inputs,
169
+ kernelOps.artifact,
170
+ kernelOps.convertInputs,
171
+ kernelOps.convertOutputs,
172
+ );
169
173
 
170
- const result = kernelOps.convertOutputs(outputWitness);
171
174
  return makePublicInputsAndProof(result, proof);
172
175
  }
173
176
 
@@ -179,11 +182,13 @@ export class BBNativeRollupProver implements ServerCircuitProver {
179
182
  public async getPublicTailProof(
180
183
  kernelRequest: PublicKernelTailRequest,
181
184
  ): Promise<PublicInputsAndProof<KernelCircuitPublicInputs>> {
182
- const witnessMap = convertPublicTailInputsToWitnessMap(kernelRequest.inputs);
183
-
184
- const [outputWitness, proof] = await this.createProof(witnessMap, 'PublicKernelTailArtifact');
185
+ const [result, proof] = await this.createProof(
186
+ kernelRequest.inputs,
187
+ 'PublicKernelTailArtifact',
188
+ convertPublicTailInputsToWitnessMap,
189
+ convertPublicTailOutputFromWitnessMap,
190
+ );
185
191
 
186
- const result = convertPublicTailOutputFromWitnessMap(outputWitness);
187
192
  return makePublicInputsAndProof(result, proof);
188
193
  }
189
194
 
@@ -195,11 +200,12 @@ export class BBNativeRollupProver implements ServerCircuitProver {
195
200
  public async getBaseRollupProof(
196
201
  input: BaseRollupInputs,
197
202
  ): Promise<PublicInputsAndProof<BaseOrMergeRollupPublicInputs>> {
198
- const witnessMap = convertBaseRollupInputsToWitnessMap(input);
199
-
200
- const [outputWitness, proof] = await this.createProof(witnessMap, 'BaseRollupArtifact');
201
-
202
- const result = convertBaseRollupOutputsFromWitnessMap(outputWitness);
203
+ const [result, proof] = await this.createProof(
204
+ input,
205
+ 'BaseRollupArtifact',
206
+ convertBaseRollupInputsToWitnessMap,
207
+ convertBaseRollupOutputsFromWitnessMap,
208
+ );
203
209
 
204
210
  return makePublicInputsAndProof(result, proof);
205
211
  }
@@ -214,11 +220,12 @@ export class BBNativeRollupProver implements ServerCircuitProver {
214
220
  // verify both inputs
215
221
  await Promise.all(input.previousRollupData.map(prev => this.verifyPreviousRollupProof(prev)));
216
222
 
217
- const witnessMap = convertMergeRollupInputsToWitnessMap(input);
218
-
219
- const [outputWitness, proof] = await this.createProof(witnessMap, 'MergeRollupArtifact');
220
-
221
- const result = convertMergeRollupOutputsFromWitnessMap(outputWitness);
223
+ const [result, proof] = await this.createProof(
224
+ input,
225
+ 'MergeRollupArtifact',
226
+ convertMergeRollupInputsToWitnessMap,
227
+ convertMergeRollupOutputsFromWitnessMap,
228
+ );
222
229
 
223
230
  return makePublicInputsAndProof(result, proof);
224
231
  }
@@ -232,18 +239,25 @@ export class BBNativeRollupProver implements ServerCircuitProver {
232
239
  // verify the inputs
233
240
  await Promise.all(input.previousRollupData.map(prev => this.verifyPreviousRollupProof(prev)));
234
241
 
235
- const witnessMap = convertRootRollupInputsToWitnessMap(input);
236
-
237
- const [outputWitness, proof] = await this.createProof(witnessMap, 'RootRollupArtifact');
242
+ const [result, proof] = await this.createProof(
243
+ input,
244
+ 'RootRollupArtifact',
245
+ convertRootRollupInputsToWitnessMap,
246
+ convertRootRollupOutputsFromWitnessMap,
247
+ );
238
248
 
239
249
  await this.verifyProof('RootRollupArtifact', proof);
240
250
 
241
- const result = convertRootRollupOutputsFromWitnessMap(outputWitness);
242
251
  return makePublicInputsAndProof(result, proof);
243
252
  }
244
253
 
245
254
  // TODO(@PhilWindle): Delete when no longer required
246
- public async createProof(witnessMap: WitnessMap, circuitType: ServerProtocolArtifact): Promise<[WitnessMap, Proof]> {
255
+ public async createProof<Input extends { toBuffer: () => Buffer }, Output extends { toBuffer: () => Buffer }>(
256
+ input: Input,
257
+ circuitType: ServerProtocolArtifact,
258
+ convertInput: (input: Input) => WitnessMap,
259
+ convertOutput: (outputWitness: WitnessMap) => Output,
260
+ ): Promise<[Output, Proof]> {
247
261
  // Create random directory to be used for temp files
248
262
  const bbWorkingDirectory = `${this.config.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
249
263
  await fs.mkdir(bbWorkingDirectory, { recursive: true });
@@ -265,15 +279,16 @@ export class BBNativeRollupProver implements ServerCircuitProver {
265
279
 
266
280
  logger.debug(`Generating witness data for ${circuitType}`);
267
281
 
282
+ const witnessMap = convertInput(input);
268
283
  const timer = new Timer();
269
284
  const outputWitness = await simulator.simulateCircuit(witnessMap, artifact);
270
- emitCircuitWitnessGenerationStats(
271
- circuitTypeToCircuitName(circuitType),
272
- timer.ms(),
273
- witnessMap.size * Fr.SIZE_IN_BYTES,
274
- outputWitness.size * Fr.SIZE_IN_BYTES,
275
- logger,
276
- );
285
+ logger.debug(`Generated witness`, {
286
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
287
+ duration: timer.ms(),
288
+ inputSize: witnessMap.size * Fr.SIZE_IN_BYTES,
289
+ outputSize: outputWitness.size * Fr.SIZE_IN_BYTES,
290
+ eventName: 'circuit-witness-generation',
291
+ } satisfies CircuitWitnessGenerationStats);
277
292
 
278
293
  // Now prove the circuit from the generated witness
279
294
  logger.debug(`Proving ${circuitType}...`);
@@ -293,38 +308,51 @@ export class BBNativeRollupProver implements ServerCircuitProver {
293
308
  }
294
309
 
295
310
  // Ensure our vk cache is up to date
296
- await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType);
311
+ const vkData = await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType);
297
312
 
298
313
  // Read the proof and then cleanup up our temporary directory
299
- const proof = await fs.readFile(`${provingResult.proofPath!}/${PROOF_FILENAME}`);
300
-
301
- // does not include reading the proof from disk above because duration comes from the bb wrapper
302
- emitCircuitProvingStats(
303
- circuitTypeToCircuitName(circuitType),
304
- provingResult.duration,
305
- witnessMap.size * Fr.SIZE_IN_BYTES,
306
- outputWitness.size * Fr.SIZE_IN_BYTES,
307
- proof.length,
308
- logger,
309
- );
314
+ const rawProof = await fs.readFile(`${provingResult.proofPath!}/${PROOF_FILENAME}`);
310
315
 
311
316
  await fs.rm(bbWorkingDirectory, { recursive: true, force: true });
312
317
 
313
- logger.info(`Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.length} fields`);
318
+ const output = convertOutput(outputWitness);
319
+ const proof = new Proof(rawProof);
320
+ logger.info(
321
+ `Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.buffer.length} fields`,
322
+ {
323
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
324
+ // does not include reading the proof from disk
325
+ duration: provingResult.duration,
326
+ proofSize: proof.buffer.length,
327
+ eventName: 'circuit-proving',
328
+ inputSize: input.toBuffer().length,
329
+ outputSize: output.toBuffer().length,
330
+ circuitSize: vkData.circuitSize,
331
+ numPublicInputs: vkData.numPublicInputs,
332
+ } satisfies CircuitProvingStats,
333
+ );
314
334
 
315
- return [outputWitness, new Proof(proof)];
335
+ return [output, proof];
316
336
  }
317
337
 
318
338
  /**
319
339
  * Executes a circuit and returns it's outputs and corresponding proof with embedded aggregation object
320
340
  * @param witnessMap - The input witness
321
341
  * @param circuitType - The type of circuit to be executed
342
+ * @param proofLength - The length of the proof to be generated. This is a dummy parameter to aid in type checking
343
+ * @param convertInput - Function for mapping the input object to a witness map.
322
344
  * @param convertOutput - Function for parsing the output witness to it's corresponding object
323
345
  * @returns The circuits output object and it's proof
324
346
  */
325
- public async createRecursiveProof<PROOF_LENGTH extends number, CircuitOutputType>(
326
- witnessMap: WitnessMap,
347
+ public async createRecursiveProof<
348
+ PROOF_LENGTH extends number,
349
+ CircuitInputType extends { toBuffer: () => Buffer },
350
+ CircuitOutputType extends { toBuffer: () => Buffer },
351
+ >(
352
+ input: CircuitInputType,
327
353
  circuitType: ServerProtocolArtifact,
354
+ proofLength: PROOF_LENGTH,
355
+ convertInput: (input: CircuitInputType) => WitnessMap,
328
356
  convertOutput: (outputWitness: WitnessMap) => CircuitOutputType,
329
357
  ): Promise<[CircuitOutputType, RecursiveProof<PROOF_LENGTH>]> {
330
358
  // Create random directory to be used for temp files
@@ -350,17 +378,20 @@ export class BBNativeRollupProver implements ServerCircuitProver {
350
378
  logger.debug(`Generating witness data for ${circuitType}`);
351
379
 
352
380
  const timer = new Timer();
381
+ const witnessMap = convertInput(input);
353
382
  const outputWitness = await simulator.simulateCircuit(witnessMap, artifact);
354
383
 
355
- emitCircuitWitnessGenerationStats(
356
- circuitTypeToCircuitName(circuitType),
357
- timer.ms(),
358
- witnessMap.size * Fr.SIZE_IN_BYTES,
359
- outputWitness.size * Fr.SIZE_IN_BYTES,
360
- logger,
361
- );
384
+ const output = convertOutput(outputWitness);
362
385
 
363
- const outputType = convertOutput(outputWitness);
386
+ const inputSize = input.toBuffer().length;
387
+ const outputSize = output.toBuffer().length;
388
+ logger.debug(`Generated witness`, {
389
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
390
+ duration: timer.ms(),
391
+ inputSize,
392
+ outputSize,
393
+ eventName: 'circuit-witness-generation',
394
+ } satisfies CircuitWitnessGenerationStats);
364
395
 
365
396
  // Now prove the circuit from the generated witness
366
397
  logger.debug(`Proving ${circuitType}...`);
@@ -380,25 +411,26 @@ export class BBNativeRollupProver implements ServerCircuitProver {
380
411
  }
381
412
 
382
413
  // Ensure our vk cache is up to date
383
- await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType);
414
+ const vkData = await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType);
384
415
 
385
416
  // Read the proof and then cleanup up our temporary directory
386
- const proof = await this.readProofAsFields<PROOF_LENGTH>(provingResult.proofPath!, circuitType);
417
+ const proof = await this.readProofAsFields(provingResult.proofPath!, circuitType, proofLength);
387
418
 
388
419
  logger.info(
389
420
  `Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.proof.length} fields`,
421
+ {
422
+ circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
423
+ circuitSize: vkData.circuitSize,
424
+ duration: provingResult.duration,
425
+ inputSize,
426
+ outputSize,
427
+ proofSize: proof.binaryProof.buffer.length,
428
+ eventName: 'circuit-proving',
429
+ numPublicInputs: vkData.numPublicInputs,
430
+ } satisfies CircuitProvingStats,
390
431
  );
391
432
 
392
- emitCircuitProvingStats(
393
- circuitTypeToCircuitName(circuitType),
394
- provingResult.duration,
395
- witnessMap.size * Fr.SIZE_IN_BYTES,
396
- outputWitness.size * Fr.SIZE_IN_BYTES,
397
- proof.binaryProof.buffer.length,
398
- logger,
399
- );
400
-
401
- return [outputType, proof];
433
+ return [output, proof];
402
434
  } finally {
403
435
  await fs.rm(bbWorkingDirectory, { recursive: true, force: true });
404
436
  }
@@ -515,13 +547,16 @@ export class BBNativeRollupProver implements ServerCircuitProver {
515
547
  * @param filePath - The directory containing the verification key data files
516
548
  * @param circuitType - The type of circuit to which the verification key corresponds
517
549
  */
518
- private async updateVerificationKeyAfterProof(filePath: string, circuitType: ServerProtocolArtifact) {
550
+ private async updateVerificationKeyAfterProof(
551
+ filePath: string,
552
+ circuitType: ServerProtocolArtifact,
553
+ ): Promise<VerificationKeyData> {
519
554
  let promise = this.verificationKeys.get(circuitType);
520
555
  if (!promise) {
521
556
  promise = this.convertVk(filePath);
522
557
  this.verificationKeys.set(circuitType, promise);
523
558
  }
524
- await promise;
559
+ return promise;
525
560
  }
526
561
 
527
562
  /**
@@ -533,6 +568,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
533
568
  private async readProofAsFields<PROOF_LENGTH extends number>(
534
569
  filePath: string,
535
570
  circuitType: ServerProtocolArtifact,
571
+ proofLength: PROOF_LENGTH,
536
572
  ): Promise<RecursiveProof<PROOF_LENGTH>> {
537
573
  const [binaryProof, proofString] = await Promise.all([
538
574
  fs.readFile(`${filePath}/${PROOF_FILENAME}`),
@@ -552,6 +588,10 @@ export class BBNativeRollupProver implements ServerCircuitProver {
552
588
  `Circuit type: ${circuitType}, complete proof length: ${fields.length}, without public inputs: ${fieldsWithoutPublicInputs.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`,
553
589
  );
554
590
  const proof = new RecursiveProof<PROOF_LENGTH>(fieldsWithoutPublicInputs, new Proof(binaryProof));
591
+ if (proof.proof.length !== proofLength) {
592
+ throw new Error("Proof length doesn't match expected length");
593
+ }
594
+
555
595
  return proof;
556
596
  }
557
597
  }