@aztec/bb-prover 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a

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 (50) hide show
  1. package/dest/avm_proving_tests/avm_proving_tester.d.ts +9 -5
  2. package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +1 -1
  3. package/dest/avm_proving_tests/avm_proving_tester.js +146 -104
  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 +7 -2
  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/prover/client/bb_private_kernel_prover.d.ts +10 -2
  19. package/dest/prover/client/bb_private_kernel_prover.d.ts.map +1 -1
  20. package/dest/prover/client/bb_private_kernel_prover.js +25 -1
  21. package/dest/prover/proof_utils.d.ts +11 -1
  22. package/dest/prover/proof_utils.d.ts.map +1 -1
  23. package/dest/prover/proof_utils.js +24 -1
  24. package/dest/prover/server/bb_prover.d.ts +4 -5
  25. package/dest/prover/server/bb_prover.d.ts.map +1 -1
  26. package/dest/prover/server/bb_prover.js +207 -78
  27. package/dest/verification_key/verification_key_data.js +1 -1
  28. package/dest/verifier/batch_chonk_verifier.d.ts +13 -2
  29. package/dest/verifier/batch_chonk_verifier.d.ts.map +1 -1
  30. package/dest/verifier/batch_chonk_verifier.js +210 -58
  31. package/dest/verifier/bb_verifier.d.ts +4 -1
  32. package/dest/verifier/bb_verifier.d.ts.map +1 -1
  33. package/dest/verifier/bb_verifier.js +134 -45
  34. package/package.json +18 -17
  35. package/src/avm_proving_tests/avm_proving_tester.ts +45 -126
  36. package/src/bb/bb_js_backend.ts +435 -0
  37. package/src/bb/bb_js_debug.ts +227 -0
  38. package/src/bb/file_names.ts +6 -0
  39. package/src/config.ts +6 -1
  40. package/src/index.ts +2 -1
  41. package/src/prover/client/bb_private_kernel_prover.ts +100 -0
  42. package/src/prover/proof_utils.ts +41 -1
  43. package/src/prover/server/bb_prover.ts +132 -137
  44. package/src/verification_key/verification_key_data.ts +1 -1
  45. package/src/verifier/batch_chonk_verifier.ts +204 -65
  46. package/src/verifier/bb_verifier.ts +66 -76
  47. package/dest/bb/execute.d.ts +0 -108
  48. package/dest/bb/execute.d.ts.map +0 -1
  49. package/dest/bb/execute.js +0 -652
  50. package/src/bb/execute.ts +0 -687
@@ -9,8 +9,8 @@ import type { Tx } from '@aztec/stdlib/tx';
9
9
 
10
10
  import { Unpackr } from 'msgpackr';
11
11
  import { execFile } from 'node:child_process';
12
- import { unlinkSync } from 'node:fs';
13
- import { unlink } from 'node:fs/promises';
12
+ import { rmSync } from 'node:fs';
13
+ import { mkdtemp, rm } from 'node:fs/promises';
14
14
  import * as os from 'node:os';
15
15
  import * as path from 'node:path';
16
16
  import { promisify } from 'node:util';
@@ -18,6 +18,8 @@ import { promisify } from 'node:util';
18
18
  import type { BBConfig } from '../config.js';
19
19
 
20
20
  const execFileAsync = promisify(execFile);
21
+ const RESULT_TIMEOUT_MS = 5 * 60 * 1000;
22
+ const STOP_DRAIN_TIMEOUT_MS = 5_000;
21
23
 
22
24
  /** Result from the FIFO, matching the C++ VerifyResult struct. */
23
25
  interface FifoVerifyResult {
@@ -34,11 +36,12 @@ interface PendingRequest {
34
36
  resolve: (result: IVCProofVerificationResult) => void;
35
37
  reject: (error: Error) => void;
36
38
  totalTimer: Timer;
39
+ timeout: ReturnType<typeof setTimeout>;
37
40
  }
38
41
 
39
42
  /**
40
43
  * Batch verifier for Chonk IVC proofs. Uses the bb batch verifier service
41
- * which batches IPA verification into a single SRS MSM for better throughput.
44
+ * which batches IPA verification into a constant number of SRS MSMs for better throughput.
42
45
  *
43
46
  * Architecture:
44
47
  * - Spawns a persistent `bb msgpack run` process via Barretenberg (native backend)
@@ -48,7 +51,8 @@ interface PendingRequest {
48
51
  */
49
52
  export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
50
53
  private bb!: Barretenberg;
51
- private fifoPath: string;
54
+ private fifoDir: string | undefined;
55
+ private fifoPath = '';
52
56
  private nextRequestId = 0;
53
57
  private pendingRequests = new Map<number, PendingRequest>();
54
58
  private sendQueue: SerialQueue;
@@ -58,6 +62,9 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
58
62
  private vkIndexMap = new Map<string, number>();
59
63
  /** Bound cleanup handler for process exit signals. */
60
64
  private exitCleanup: (() => void) | null = null;
65
+ private stopped = false;
66
+ private fatalError: Error | undefined;
67
+ private pendingDrainedResolvers = new Set<() => void>();
61
68
 
62
69
  private constructor(
63
70
  private config: Pick<BBConfig, 'bbChonkVerifyConcurrency'> & Partial<Pick<BBConfig, 'bbBinaryPath'>>,
@@ -65,7 +72,6 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
65
72
  private batchSize: number,
66
73
  private label: string,
67
74
  ) {
68
- this.fifoPath = path.join(os.tmpdir(), `bb-batch-${label}-${process.pid}-${Date.now()}.fifo`);
69
75
  this.fifoReader = new FifoFrameReader();
70
76
  this.sendQueue = new SerialQueue();
71
77
  this.sendQueue.start(1);
@@ -106,48 +112,87 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
106
112
  private async start(): Promise<void> {
107
113
  this.logger.info('Starting BatchChonkVerifier');
108
114
 
109
- this.bb = await Barretenberg.new({
110
- bbPath: this.config.bbBinaryPath,
111
- backend: BackendType.NativeUnixSocket,
112
- });
113
- await this.bb.initSRSChonk();
114
-
115
- await execFileAsync('mkfifo', [this.fifoPath]);
116
- this.registerExitCleanup();
117
-
118
- await this.bb.chonkBatchVerifierStart({
119
- vks: this.vkBuffers,
120
- numCores: this.config.bbChonkVerifyConcurrency || 0,
121
- batchSize: this.batchSize,
122
- fifoPath: this.fifoPath,
123
- });
124
-
125
- this.startFifoReader();
115
+ try {
116
+ this.bb = await Barretenberg.new({
117
+ bbPath: this.config.bbBinaryPath,
118
+ backend: BackendType.NativeUnixSocket,
119
+ });
120
+ await this.bb.initSRSChonk();
121
+
122
+ // Keep the FIFO in a private directory so cleanup has a single owner.
123
+ this.fifoDir = await mkdtemp(path.join(os.tmpdir(), `bb-batch-${this.label}-${process.pid}-`));
124
+ this.fifoPath = path.join(this.fifoDir, 'results.fifo');
125
+ await execFileAsync('mkfifo', [this.fifoPath]);
126
+ this.registerExitCleanup();
127
+ this.startFifoReader();
128
+
129
+ await this.bb.chonkBatchVerifierStart({
130
+ vks: this.vkBuffers,
131
+ numCores: this.config.bbChonkVerifyConcurrency || 0,
132
+ batchSize: this.batchSize,
133
+ fifoPath: this.fifoPath,
134
+ });
135
+ } catch (err) {
136
+ this.fifoReader.stop();
137
+ this.deregisterExitCleanup();
138
+ await this.cleanupFifo();
139
+ await this.bb?.destroy().catch(() => {});
140
+ throw err;
141
+ }
126
142
  this.logger.info('BatchChonkVerifier started', { fifoPath: this.fifoPath });
127
143
  }
128
144
 
129
145
  public verifyProof(tx: Tx): Promise<IVCProofVerificationResult> {
130
- const circuit = tx.data.forPublic ? 'HidingKernelToPublic' : 'HidingKernelToRollup';
131
- const vkIndex = this.vkIndexMap.get(circuit);
132
- if (vkIndex === undefined) {
133
- throw new Error(`No VK index for circuit ${circuit}`);
134
- }
135
- const proofWithPubInputs = tx.chonkProof.attachPublicInputs(tx.data.publicInputs().toFields());
136
- const proofFields = proofWithPubInputs.fieldsWithPublicInputs.map(f => f.toBuffer());
137
- return this.enqueueProof(vkIndex, proofFields);
146
+ const totalTimer = new Timer();
147
+ return (async () => {
148
+ const circuit = tx.data.forPublic ? 'HidingKernelToPublic' : 'HidingKernelToRollup';
149
+ const vkIndex = this.vkIndexMap.get(circuit);
150
+ if (vkIndex === undefined) {
151
+ throw new Error(`No VK index for circuit ${circuit}`);
152
+ }
153
+ const proofWithPubInputs = tx.chonkProof.attachPublicInputs(tx.data.publicInputs().toFields());
154
+ const proofFields = proofWithPubInputs.fieldsWithPublicInputs.map(f => f.toBuffer());
155
+ return await this.enqueueProof(vkIndex, proofFields);
156
+ })().catch(err => {
157
+ this.logger.warn(`Failed to verify Chonk proof for tx ${tx.getTxHash().toString()}: ${String(err)}`);
158
+ return { valid: false, durationMs: 0, totalDurationMs: totalTimer.ms() };
159
+ });
138
160
  }
139
161
 
140
162
  /** Enqueue raw proof fields for verification. Used directly by tests with custom VKs. */
141
163
  public enqueueProof(vkIndex: number, proofFields: Uint8Array[]): Promise<IVCProofVerificationResult> {
164
+ if (this.stopped) {
165
+ return Promise.reject(new Error('BatchChonkVerifier stopped'));
166
+ }
167
+ if (this.fatalError) {
168
+ return Promise.reject(this.fatalError);
169
+ }
170
+
142
171
  const totalTimer = new Timer();
143
172
  const requestId = this.nextRequestId++;
144
173
 
145
174
  const resultPromise = new Promise<IVCProofVerificationResult>((resolve, reject) => {
146
- this.pendingRequests.set(requestId, { resolve, reject, totalTimer });
175
+ const timeout = setTimeout(() => {
176
+ const pending = this.pendingRequests.get(requestId);
177
+ if (!pending) {
178
+ return;
179
+ }
180
+ this.pendingRequests.delete(requestId);
181
+ pending.reject(new Error(`BatchChonkVerifier result timed out for request_id=${requestId}`));
182
+ this.notifyPendingDrained();
183
+ }, RESULT_TIMEOUT_MS);
184
+ // A pending result timer must never keep the host process alive on its own (e.g. an
185
+ // orphaned request at process exit); the FIFO reader keeps the loop alive while results
186
+ // are genuinely awaited.
187
+ timeout.unref();
188
+ this.pendingRequests.set(requestId, { resolve, reject, totalTimer, timeout });
147
189
  });
148
190
 
149
191
  void this.sendQueue
150
192
  .put(async () => {
193
+ if (this.fatalError) {
194
+ throw this.fatalError;
195
+ }
151
196
  await this.bb.chonkBatchVerifierQueue({
152
197
  requestId,
153
198
  vkIndex,
@@ -158,7 +203,9 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
158
203
  const pending = this.pendingRequests.get(requestId);
159
204
  if (pending) {
160
205
  this.pendingRequests.delete(requestId);
206
+ clearTimeout(pending.timeout);
161
207
  pending.reject(err instanceof Error ? err : new Error(String(err)));
208
+ this.notifyPendingDrained();
162
209
  }
163
210
  });
164
211
 
@@ -167,36 +214,60 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
167
214
 
168
215
  public async stop(): Promise<void> {
169
216
  this.logger.info('Stopping BatchChonkVerifier');
217
+ this.stopped = true;
170
218
 
171
- // Stop accepting new proofs
172
- await this.sendQueue.end();
173
-
174
- // Stop the bb service (flushes remaining proofs)
175
219
  try {
176
- await this.bb.chonkBatchVerifierStop({});
177
- } catch (err) {
178
- this.logger.warn(`Error stopping batch verifier service: ${err}`);
179
- }
220
+ // Stop accepting new proofs and flush the send queue. Bound it so an unresponsive
221
+ // native process can't block teardown of our own event-loop handles indefinitely.
222
+ await this.withTimeout(this.sendQueue.end(), STOP_DRAIN_TIMEOUT_MS, 'send queue flush');
180
223
 
181
- // Stop FIFO reader
182
- this.fifoReader.stop();
224
+ // Stop the bb service (flushes remaining proofs).
225
+ await this.withTimeout(this.bb.chonkBatchVerifierStop({}), STOP_DRAIN_TIMEOUT_MS, 'chonkBatchVerifierStop');
183
226
 
184
- // Clean up FIFO file and deregister exit handler
185
- await unlink(this.fifoPath).catch(() => {});
186
- this.deregisterExitCleanup();
187
-
188
- // Reject any remaining pending requests
189
- for (const [id, pending] of this.pendingRequests) {
190
- pending.reject(new Error('BatchChonkVerifier stopped'));
191
- this.pendingRequests.delete(id);
227
+ // Native stop flushes callbacks; keep the FIFO open until those frames are observed.
228
+ const drained = await this.waitForPendingRequestsToDrain(STOP_DRAIN_TIMEOUT_MS);
229
+ if (!drained) {
230
+ this.rejectPendingRequests(new Error('Timed out waiting for BatchChonkVerifier results during stop'));
231
+ }
232
+ } catch (err) {
233
+ this.logger.warn(`Error during BatchChonkVerifier graceful stop: ${err}`);
234
+ this.rejectPendingRequests(err instanceof Error ? err : new Error(String(err)));
235
+ } finally {
236
+ // Always release our own event-loop handles — the FIFO read stream (a blocking
237
+ // threadpool read that can't be unref'd), the unix socket, the native process, and the
238
+ // exit handler — so the host process exits cleanly even if the native backend is wedged.
239
+ this.fifoReader.stop();
240
+ this.deregisterExitCleanup();
241
+ await this.cleanupFifo();
242
+ await this.bb.destroy().catch(err => this.logger.warn(`Error destroying bb backend during stop: ${err}`));
192
243
  }
193
244
 
194
- // Destroy bb process
195
- await this.bb.destroy();
196
-
197
245
  this.logger.info('BatchChonkVerifier stopped');
198
246
  }
199
247
 
248
+ /**
249
+ * Races a promise against a timeout so a wedged native backend can't block teardown. The
250
+ * timer is unref'd so it never keeps the process alive; the underlying promise stays handled
251
+ * by the race even if the timeout wins, so it cannot surface as an unhandled rejection.
252
+ */
253
+ private async withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T | void> {
254
+ let timer: ReturnType<typeof setTimeout> | undefined;
255
+ const timeout = new Promise<void>((_, reject) => {
256
+ timer = setTimeout(
257
+ () => reject(new Error(`BatchChonkVerifier ${label} timed out after ${timeoutMs}ms`)),
258
+ timeoutMs,
259
+ );
260
+ timer.unref();
261
+ });
262
+ try {
263
+ return await Promise.race([promise, timeout]);
264
+ } finally {
265
+ if (timer) {
266
+ clearTimeout(timer);
267
+ }
268
+ }
269
+ }
270
+
200
271
  private startFifoReader(): void {
201
272
  const unpackr = new Unpackr({ useRecords: false });
202
273
 
@@ -206,18 +277,20 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
206
277
  this.handleResult(result);
207
278
  } catch (err) {
208
279
  this.logger.error(`FIFO: failed to decode msgpack result: ${err}`);
280
+ // A corrupt result stream cannot safely be matched to outstanding requests.
281
+ this.failVerifier(err instanceof Error ? err : new Error(String(err)));
209
282
  }
210
283
  });
211
284
 
212
285
  this.fifoReader.on('error', (err: Error) => {
213
286
  this.logger.error(`FIFO reader error: ${err}`);
287
+ this.failVerifier(err);
214
288
  });
215
289
 
216
290
  this.fifoReader.on('end', () => {
217
291
  this.logger.debug('FIFO reader: stream ended');
218
- for (const [id, pending] of this.pendingRequests) {
219
- pending.reject(new Error('FIFO stream ended unexpectedly'));
220
- this.pendingRequests.delete(id);
292
+ if (!this.stopped) {
293
+ this.failVerifier(new Error('FIFO stream ended unexpectedly'));
221
294
  }
222
295
  });
223
296
 
@@ -231,6 +304,7 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
231
304
  return;
232
305
  }
233
306
  this.pendingRequests.delete(result.request_id);
307
+ clearTimeout(pending.timeout);
234
308
 
235
309
  const valid = result.status === 0; // VerifyStatus::OK
236
310
  const durationMs = result.time_in_verify_ms;
@@ -249,28 +323,93 @@ export class BatchChonkVerifier implements ClientProtocolCircuitVerifier {
249
323
  }
250
324
 
251
325
  pending.resolve(ivcResult);
326
+ this.notifyPendingDrained();
252
327
  }
253
328
 
254
329
  private registerExitCleanup(): void {
255
- // Signal handlers must be synchronous — unlinkSync is intentional here
256
330
  this.exitCleanup = () => {
257
- try {
258
- unlinkSync(this.fifoPath);
259
- } catch {
260
- /* ignore */
261
- }
331
+ this.cleanupFifoSync();
262
332
  };
263
333
  process.on('exit', this.exitCleanup);
264
- process.on('SIGINT', this.exitCleanup);
265
- process.on('SIGTERM', this.exitCleanup);
266
334
  }
267
335
 
268
336
  private deregisterExitCleanup(): void {
269
337
  if (this.exitCleanup) {
270
338
  process.removeListener('exit', this.exitCleanup);
271
- process.removeListener('SIGINT', this.exitCleanup);
272
- process.removeListener('SIGTERM', this.exitCleanup);
273
339
  this.exitCleanup = null;
274
340
  }
275
341
  }
342
+
343
+ private rejectPendingRequests(error: Error): void {
344
+ for (const [id, pending] of Array.from(this.pendingRequests)) {
345
+ pending.reject(error);
346
+ clearTimeout(pending.timeout);
347
+ this.pendingRequests.delete(id);
348
+ }
349
+ this.notifyPendingDrained();
350
+ }
351
+
352
+ private failVerifier(error: Error): void {
353
+ if (!this.fatalError) {
354
+ this.fatalError = error;
355
+ }
356
+ this.rejectPendingRequests(error);
357
+ }
358
+
359
+ private waitForPendingRequestsToDrain(timeoutMs: number): Promise<boolean> {
360
+ if (this.pendingRequests.size === 0) {
361
+ return Promise.resolve(true);
362
+ }
363
+
364
+ let timeout: ReturnType<typeof setTimeout> | undefined;
365
+ let onDrain: (() => void) | undefined;
366
+ const drained = new Promise<boolean>(resolve => {
367
+ onDrain = () => resolve(true);
368
+ this.pendingDrainedResolvers.add(onDrain);
369
+ });
370
+ const timedOut = new Promise<boolean>(resolve => {
371
+ timeout = setTimeout(() => resolve(false), timeoutMs);
372
+ timeout.unref();
373
+ });
374
+
375
+ return Promise.race([drained, timedOut]).finally(() => {
376
+ if (timeout) {
377
+ clearTimeout(timeout);
378
+ }
379
+ if (onDrain) {
380
+ this.pendingDrainedResolvers.delete(onDrain);
381
+ }
382
+ });
383
+ }
384
+
385
+ private notifyPendingDrained(): void {
386
+ if (this.pendingRequests.size > 0) {
387
+ return;
388
+ }
389
+ for (const resolve of Array.from(this.pendingDrainedResolvers)) {
390
+ resolve();
391
+ }
392
+ }
393
+
394
+ private async cleanupFifo(): Promise<void> {
395
+ if (this.fifoDir) {
396
+ await rm(this.fifoDir, { recursive: true, force: true }).catch(() => {});
397
+ } else if (this.fifoPath) {
398
+ await rm(this.fifoPath, { force: true }).catch(() => {});
399
+ }
400
+ this.fifoDir = undefined;
401
+ this.fifoPath = '';
402
+ }
403
+
404
+ private cleanupFifoSync(): void {
405
+ try {
406
+ if (this.fifoDir) {
407
+ rmSync(this.fifoDir, { recursive: true, force: true });
408
+ } else if (this.fifoPath) {
409
+ rmSync(this.fifoPath, { force: true });
410
+ }
411
+ } catch {
412
+ /* ignore */
413
+ }
414
+ }
276
415
  }
@@ -1,4 +1,3 @@
1
- import { runInDirectory } from '@aztec/foundation/fs';
2
1
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
2
  import { Timer } from '@aztec/foundation/timer';
4
3
  import { ProtocolCircuitVks } from '@aztec/noir-protocol-circuits-types/server/vks';
@@ -15,28 +14,29 @@ import { Tx } from '@aztec/stdlib/tx';
15
14
  import type { VerificationKeyData } from '@aztec/stdlib/vks';
16
15
 
17
16
  import { promises as fs } from 'fs';
18
- import * as path from 'path';
19
17
 
20
- import {
21
- BB_RESULT,
22
- PROOF_FILENAME,
23
- PUBLIC_INPUTS_FILENAME,
24
- VK_FILENAME,
25
- verifyChonkProof,
26
- verifyProof,
27
- } from '../bb/execute.js';
18
+ import { BBJsFactory } from '../bb/bb_js_backend.js';
28
19
  import type { BBConfig } from '../config.js';
29
20
  import { getUltraHonkFlavorForCircuit } from '../honk.js';
30
- import { writeChonkProofToPath } from '../prover/proof_utils.js';
31
21
 
32
22
  export class BBCircuitVerifier implements ClientProtocolCircuitVerifier {
23
+ private bbJsFactory: BBJsFactory;
24
+
33
25
  private constructor(
34
26
  private config: BBConfig,
35
27
  private logger: Logger,
36
- ) {}
28
+ ) {
29
+ // BB_NUM_IVC_VERIFIERS bounds the number of long-lived bb processes the pool keeps alive.
30
+ // If 0, fall back to spawning a fresh bb per verification.
31
+ this.bbJsFactory = new BBJsFactory(config.bbBinaryPath, {
32
+ poolSize: config.numConcurrentIVCVerifiers > 0 ? config.numConcurrentIVCVerifiers : undefined,
33
+ logger,
34
+ debugDir: config.bbDebugOutputDir,
35
+ });
36
+ }
37
37
 
38
38
  public stop(): Promise<void> {
39
- return Promise.resolve();
39
+ return this.bbJsFactory.destroy();
40
40
  }
41
41
 
42
42
  public static async new(config: BBConfig, logger = createLogger('bb-prover:verifier')) {
@@ -55,87 +55,77 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier {
55
55
  return vk;
56
56
  }
57
57
 
58
+ /** Verify an UltraHonk proof via bb.js API (no temp files). */
58
59
  public async verifyProofForCircuit(circuit: ServerProtocolArtifact, proof: Proof) {
59
- const operation = async (bbWorkingDirectory: string) => {
60
- const publicInputsFileName = path.join(bbWorkingDirectory, PUBLIC_INPUTS_FILENAME);
61
- const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME);
62
- const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME);
63
- const verificationKey = this.getVerificationKeyData(circuit);
60
+ const verificationKey = this.getVerificationKeyData(circuit);
61
+ const flavor = getUltraHonkFlavorForCircuit(circuit);
64
62
 
65
- this.logger.debug(`${circuit} Verifying with key: ${verificationKey.keyAsFields.hash.toString()}`);
63
+ this.logger.debug(`${circuit} Verifying with key: ${verificationKey.keyAsFields.hash.toString()}`);
66
64
 
67
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/13189): Put this proof parsing logic in the proof class.
68
- await fs.writeFile(publicInputsFileName, proof.buffer.slice(0, proof.numPublicInputs * 32));
69
- await fs.writeFile(proofFileName, proof.buffer.slice(proof.numPublicInputs * 32));
70
- await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
65
+ // Split proof buffer into public input fields and proof fields (32-byte each)
66
+ const publicInputFields = splitBufferToFieldArrays(proof.buffer.subarray(0, proof.numPublicInputs * 32));
67
+ const proofFields = splitBufferToFieldArrays(proof.buffer.subarray(proof.numPublicInputs * 32));
71
68
 
72
- const result = await verifyProof(
73
- this.config.bbBinaryPath,
74
- proofFileName,
75
- verificationKeyPath!,
76
- getUltraHonkFlavorForCircuit(circuit),
77
- this.logger,
78
- );
69
+ await using instance = await this.bbJsFactory.getInstance();
70
+ const { verified, durationMs } = await instance.verifyProof(
71
+ proofFields,
72
+ verificationKey.keyAsBytes,
73
+ publicInputFields,
74
+ flavor,
75
+ );
79
76
 
80
- if (result.status === BB_RESULT.FAILURE) {
81
- const errorMessage = `Failed to verify ${circuit} proof!`;
82
- throw new Error(errorMessage);
83
- }
77
+ if (!verified) {
78
+ throw new Error(`Failed to verify ${circuit} proof!`);
79
+ }
84
80
 
85
- this.logger.debug(`${circuit} verification successful`, {
86
- circuitName: mapProtocolArtifactNameToCircuitName(circuit),
87
- duration: result.durationMs,
88
- eventName: 'circuit-verification',
89
- proofType: 'ultra-honk',
90
- } satisfies CircuitVerificationStats);
91
- };
92
- await runInDirectory(this.config.bbWorkingDirectory, operation, this.config.bbSkipCleanup, this.logger);
81
+ this.logger.debug(`${circuit} verification successful`, {
82
+ circuitName: mapProtocolArtifactNameToCircuitName(circuit),
83
+ duration: durationMs,
84
+ eventName: 'circuit-verification',
85
+ proofType: 'ultra-honk',
86
+ } satisfies CircuitVerificationStats);
93
87
  }
94
88
 
89
+ /** Verify a Chonk (IVC) proof from a transaction via bb.js API. */
95
90
  public async verifyProof(tx: Tx): Promise<IVCProofVerificationResult> {
96
91
  const proofType = 'Chonk';
97
92
  try {
98
93
  const totalTimer = new Timer();
99
- let verificationDuration = 0;
100
94
 
101
95
  const circuit: ClientProtocolArtifact = tx.data.forPublic ? 'HidingKernelToPublic' : 'HidingKernelToRollup';
96
+ const verificationKey = this.getVerificationKeyData(circuit);
97
+
98
+ // Reconstruct the full proof with public inputs prepended, then convert Fr[] to Uint8Array[]
99
+ const proofWithPubInputs = tx.chonkProof.attachPublicInputs(tx.data.publicInputs().toFields());
100
+ const fieldsAsBuffers = proofWithPubInputs.fieldsWithPublicInputs.map(f => new Uint8Array(f.toBuffer()));
102
101
 
103
- // Block below is almost copy-pasted from verifyProofForCircuit
104
- const operation = async (bbWorkingDirectory: string) => {
105
- const proofPath = path.join(bbWorkingDirectory, PROOF_FILENAME);
106
- await writeChonkProofToPath(tx.chonkProof.attachPublicInputs(tx.data.publicInputs().toFields()), proofPath);
107
-
108
- const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME);
109
- const verificationKey = this.getVerificationKeyData(circuit);
110
- await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
111
-
112
- const timer = new Timer();
113
- const result = await verifyChonkProof(
114
- this.config.bbBinaryPath,
115
- proofPath,
116
- verificationKeyPath,
117
- this.logger,
118
- this.config.bbIVCConcurrency,
119
- );
120
- verificationDuration = timer.ms();
121
-
122
- if (result.status === BB_RESULT.FAILURE) {
123
- const errorMessage = `Failed to verify ${proofType} proof for ${circuit}!`;
124
- throw new Error(errorMessage);
125
- }
126
-
127
- this.logger.debug(`${proofType} verification successful`, {
128
- circuitName: mapProtocolArtifactNameToCircuitName(circuit),
129
- duration: result.durationMs,
130
- eventName: 'circuit-verification',
131
- proofType: 'chonk',
132
- } satisfies CircuitVerificationStats);
133
- };
134
- await runInDirectory(this.config.bbWorkingDirectory, operation, this.config.bbSkipCleanup, this.logger);
135
- return { valid: true, durationMs: verificationDuration, totalDurationMs: totalTimer.ms() };
102
+ await using instance = await this.bbJsFactory.getInstance();
103
+ const { verified, durationMs } = await instance.verifyChonkProof(fieldsAsBuffers, verificationKey.keyAsBytes);
104
+
105
+ if (!verified) {
106
+ throw new Error(`Failed to verify ${proofType} proof for ${circuit}!`);
107
+ }
108
+
109
+ this.logger.debug(`${proofType} verification successful`, {
110
+ circuitName: mapProtocolArtifactNameToCircuitName(circuit),
111
+ duration: durationMs,
112
+ eventName: 'circuit-verification',
113
+ proofType: 'chonk',
114
+ } satisfies CircuitVerificationStats);
115
+
116
+ return { valid: true, durationMs, totalDurationMs: totalTimer.ms() };
136
117
  } catch (err) {
137
118
  this.logger.warn(`Failed to verify ${proofType} proof for tx ${tx.getTxHash().toString()}: ${String(err)}`);
138
119
  return { valid: false, durationMs: 0, totalDurationMs: 0 };
139
120
  }
140
121
  }
141
122
  }
123
+
124
+ /** Split a buffer into 32-byte Uint8Array field elements. */
125
+ function splitBufferToFieldArrays(buffer: Buffer): Uint8Array[] {
126
+ const fields: Uint8Array[] = [];
127
+ for (let i = 0; i < buffer.length; i += 32) {
128
+ fields.push(new Uint8Array(buffer.subarray(i, i + 32)));
129
+ }
130
+ return fields;
131
+ }