@aztec/prover-client 0.55.0 → 0.56.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 (44) hide show
  1. package/dest/mocks/fixtures.d.ts.map +1 -1
  2. package/dest/mocks/fixtures.js +6 -27
  3. package/dest/mocks/test_context.d.ts +4 -5
  4. package/dest/mocks/test_context.d.ts.map +1 -1
  5. package/dest/mocks/test_context.js +7 -9
  6. package/dest/orchestrator/block-building-helpers.d.ts +12 -3
  7. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  8. package/dest/orchestrator/block-building-helpers.js +34 -32
  9. package/dest/orchestrator/{proving-state.d.ts → block-proving-state.d.ts} +17 -13
  10. package/dest/orchestrator/block-proving-state.d.ts.map +1 -0
  11. package/dest/orchestrator/block-proving-state.js +170 -0
  12. package/dest/orchestrator/epoch-proving-state.d.ts +57 -0
  13. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -0
  14. package/dest/orchestrator/epoch-proving-state.js +151 -0
  15. package/dest/orchestrator/orchestrator.d.ts +32 -11
  16. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  17. package/dest/orchestrator/orchestrator.js +246 -139
  18. package/dest/orchestrator/tx-proving-state.d.ts +3 -2
  19. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  20. package/dest/orchestrator/tx-proving-state.js +54 -26
  21. package/dest/prover-agent/memory-proving-queue.d.ts +11 -5
  22. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  23. package/dest/prover-agent/memory-proving-queue.js +16 -6
  24. package/dest/prover-agent/prover-agent.d.ts.map +1 -1
  25. package/dest/prover-agent/prover-agent.js +10 -10
  26. package/dest/prover-agent/rpc.d.ts.map +1 -1
  27. package/dest/prover-agent/rpc.js +8 -2
  28. package/dest/test/mock_prover.d.ts +27 -0
  29. package/dest/test/mock_prover.d.ts.map +1 -0
  30. package/dest/test/mock_prover.js +58 -0
  31. package/package.json +13 -11
  32. package/src/mocks/fixtures.ts +5 -49
  33. package/src/mocks/test_context.ts +7 -12
  34. package/src/orchestrator/block-building-helpers.ts +90 -57
  35. package/src/orchestrator/{proving-state.ts → block-proving-state.ts} +42 -40
  36. package/src/orchestrator/epoch-proving-state.ts +232 -0
  37. package/src/orchestrator/orchestrator.ts +410 -244
  38. package/src/orchestrator/tx-proving-state.ts +63 -27
  39. package/src/prover-agent/memory-proving-queue.ts +30 -16
  40. package/src/prover-agent/prover-agent.ts +11 -9
  41. package/src/prover-agent/rpc.ts +9 -0
  42. package/src/test/mock_prover.ts +170 -0
  43. package/dest/orchestrator/proving-state.d.ts.map +0 -1
  44. package/dest/orchestrator/proving-state.js +0 -170
@@ -1,40 +1,38 @@
1
1
  import {
2
+ BlockProofError,
2
3
  Body,
3
4
  EncryptedNoteTxL2Logs,
4
5
  EncryptedTxL2Logs,
5
6
  L2Block,
6
7
  MerkleTreeId,
8
+ PROVING_STATUS,
7
9
  type PaddingProcessedTx,
8
10
  type ProcessedTx,
9
- PublicKernelType,
11
+ type ProvingBlockResult,
12
+ ProvingRequestType,
13
+ type ProvingResult,
14
+ type ProvingTicket,
15
+ type PublicInputsAndRecursiveProof,
16
+ type ServerCircuitProver,
10
17
  Tx,
11
18
  type TxEffect,
12
19
  UnencryptedTxL2Logs,
13
20
  makeEmptyProcessedTx,
14
21
  makePaddingProcessedTx,
15
- mapPublicKernelToCircuitName,
22
+ mapProvingRequestTypeToCircuitName,
16
23
  toTxEffect,
17
24
  } from '@aztec/circuit-types';
18
- import {
19
- BlockProofError,
20
- type BlockProver,
21
- PROVING_STATUS,
22
- type ProvingBlockResult,
23
- type ProvingResult,
24
- type ProvingTicket,
25
- type PublicInputsAndRecursiveProof,
26
- type ServerCircuitProver,
27
- } from '@aztec/circuit-types/interfaces';
25
+ import { type EpochProver } from '@aztec/circuit-types/interfaces';
28
26
  import { type CircuitName } from '@aztec/circuit-types/stats';
29
27
  import {
30
28
  AvmCircuitInputs,
31
29
  type BaseOrMergeRollupPublicInputs,
32
30
  BaseParityInputs,
33
31
  type BaseRollupInputs,
34
- ContentCommitment,
32
+ type BlockRootOrBlockMergePublicInputs,
33
+ BlockRootRollupInputs,
35
34
  Fr,
36
35
  type GlobalVariables,
37
- Header,
38
36
  type KernelCircuitPublicInputs,
39
37
  L1_TO_L2_MSG_SUBTREE_HEIGHT,
40
38
  L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
@@ -48,9 +46,9 @@ import {
48
46
  type RecursiveProof,
49
47
  type RootParityInput,
50
48
  RootParityInputs,
51
- StateReference,
52
49
  type TUBE_PROOF_LENGTH,
53
50
  TubeInputs,
51
+ type VMCircuitPublicInputs,
54
52
  type VerificationKeyAsFields,
55
53
  VerificationKeyData,
56
54
  makeEmptyProof,
@@ -58,7 +56,6 @@ import {
58
56
  } from '@aztec/circuits.js';
59
57
  import { makeTuple } from '@aztec/foundation/array';
60
58
  import { padArrayEnd } from '@aztec/foundation/collection';
61
- import { sha256Trunc } from '@aztec/foundation/crypto';
62
59
  import { AbortError } from '@aztec/foundation/error';
63
60
  import { createDebugLogger } from '@aztec/foundation/log';
64
61
  import { promiseWithResolvers } from '@aztec/foundation/promise';
@@ -73,16 +70,21 @@ import { inspect } from 'util';
73
70
 
74
71
  import {
75
72
  buildBaseRollupInput,
73
+ buildHeaderFromCircuitOutputs,
74
+ buildHeaderFromTxEffects,
75
+ createBlockMergeRollupInputs,
76
76
  createMergeRollupInputs,
77
- getBlockRootRollupInput,
77
+ getPreviousRollupDataFromPublicInputs,
78
+ getRootRollupInput,
79
+ getRootTreeSiblingPath,
78
80
  getSubtreeSiblingPath,
79
81
  getTreeSnapshot,
80
- validateBlockRootOutput,
81
82
  validatePartialState,
82
83
  validateTx,
83
84
  } from './block-building-helpers.js';
85
+ import { type BlockProvingState, type MergeRollupInputData } from './block-proving-state.js';
86
+ import { type BlockMergeRollupInputData, EpochProvingState, type TreeSnapshots } from './epoch-proving-state.js';
84
87
  import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js';
85
- import { type MergeRollupInputData, ProvingState, type TreeSnapshots } from './proving-state.js';
86
88
  import { TX_PROVING_CODE, type TxProvingInstruction, TxProvingState } from './tx-proving-state.js';
87
89
 
88
90
  const logger = createDebugLogger('aztec:prover:proving-orchestrator');
@@ -101,8 +103,8 @@ const logger = createDebugLogger('aztec:prover:proving-orchestrator');
101
103
  /**
102
104
  * The orchestrator, managing the flow of recursive proving operations required to build the rollup proof tree.
103
105
  */
104
- export class ProvingOrchestrator implements BlockProver {
105
- private provingState: ProvingState | undefined = undefined;
106
+ export class ProvingOrchestrator implements EpochProver {
107
+ private provingState: EpochProvingState | undefined = undefined;
106
108
  private pendingProvingJobs: AbortController[] = [];
107
109
  private paddingTx: PaddingProcessedTx | undefined = undefined;
108
110
 
@@ -132,6 +134,23 @@ export class ProvingOrchestrator implements BlockProver {
132
134
  this.paddingTx = undefined;
133
135
  }
134
136
 
137
+ @trackSpan('ProvingOrchestrator.startNewEpoch', (epochNumber, totalNumBlocks) => ({
138
+ [Attributes.EPOCH_SIZE]: totalNumBlocks,
139
+ [Attributes.EPOCH_NUMBER]: epochNumber,
140
+ }))
141
+ public startNewEpoch(epochNumber: number, totalNumBlocks: number): ProvingTicket {
142
+ const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
143
+ const promise = _promise.catch(
144
+ (reason): ProvingResult => ({
145
+ status: PROVING_STATUS.FAILURE,
146
+ reason,
147
+ }),
148
+ );
149
+
150
+ this.provingState = new EpochProvingState(epochNumber, totalNumBlocks, resolve, reject);
151
+ return { provingPromise: promise };
152
+ }
153
+
135
154
  /**
136
155
  * Starts off a new block
137
156
  * @param numTxs - The total number of transactions in the block. Must be a power of 2
@@ -149,6 +168,15 @@ export class ProvingOrchestrator implements BlockProver {
149
168
  globalVariables: GlobalVariables,
150
169
  l1ToL2Messages: Fr[],
151
170
  ): Promise<ProvingTicket> {
171
+ // If no proving state, assume we only care about proving this block and initialize a 1-block epoch
172
+ if (!this.provingState) {
173
+ this.startNewEpoch(globalVariables.blockNumber.toNumber(), 1);
174
+ }
175
+
176
+ if (!this.provingState?.isAcceptingBlocks()) {
177
+ throw new Error(`Epoch not accepting further blocks`);
178
+ }
179
+
152
180
  if (!Number.isInteger(numTxs) || numTxs < 2) {
153
181
  throw new Error(`Length of txs for the block should be at least two (got ${numTxs})`);
154
182
  }
@@ -163,11 +191,10 @@ export class ProvingOrchestrator implements BlockProver {
163
191
  );
164
192
  }
165
193
 
166
- // Cancel any currently proving block before starting a new one
167
- this.cancelBlock();
168
194
  logger.info(
169
195
  `Starting block ${globalVariables.blockNumber} for slot ${globalVariables.slotNumber} with ${numTxs} transactions`,
170
196
  );
197
+
171
198
  // we start the block by enqueueing all of the base parity circuits
172
199
  let baseParityInputs: BaseParityInputs[] = [];
173
200
  let l1ToL2MessagesPadded: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>;
@@ -197,36 +224,39 @@ export class ProvingOrchestrator implements BlockProver {
197
224
 
198
225
  // Update the local trees to include the new l1 to l2 messages
199
226
  await this.db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
227
+ const messageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, this.db);
228
+
229
+ // Get archive snapshot before this block lands
230
+ const startArchiveSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.db);
231
+ const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, this.db);
232
+ const previousBlockHash = await this.db.getLeafValue(
233
+ MerkleTreeId.ARCHIVE,
234
+ BigInt(startArchiveSnapshot.nextAvailableLeafIndex - 1),
235
+ );
200
236
 
201
237
  const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
202
- const promise = _promise.catch(
203
- (reason): ProvingResult => ({
204
- status: PROVING_STATUS.FAILURE,
205
- reason,
206
- }),
207
- );
238
+ const promise = _promise.catch((reason): ProvingResult => ({ status: PROVING_STATUS.FAILURE, reason }));
208
239
 
209
- const provingState = new ProvingState(
240
+ this.provingState!.startNewBlock(
210
241
  numTxs,
211
- resolve,
212
- reject,
213
242
  globalVariables,
214
243
  l1ToL2MessagesPadded,
215
- baseParityInputs.length,
216
244
  messageTreeSnapshot,
217
245
  newL1ToL2MessageTreeRootSiblingPath,
246
+ messageTreeSnapshotAfterInsertion,
247
+ startArchiveSnapshot,
248
+ newArchiveSiblingPath,
249
+ previousBlockHash!,
250
+ resolve,
251
+ reject,
218
252
  );
219
253
 
254
+ // Enqueue base parity circuits for the block
220
255
  for (let i = 0; i < baseParityInputs.length; i++) {
221
- this.enqueueBaseParityCircuit(provingState, baseParityInputs[i], i);
256
+ this.enqueueBaseParityCircuit(this.provingState!.currentBlock!, baseParityInputs[i], i);
222
257
  }
223
258
 
224
- this.provingState = provingState;
225
-
226
- const ticket: ProvingTicket = {
227
- provingPromise: promise,
228
- };
229
- return ticket;
259
+ return { provingPromise: promise };
230
260
  }
231
261
 
232
262
  /**
@@ -237,11 +267,12 @@ export class ProvingOrchestrator implements BlockProver {
237
267
  [Attributes.TX_HASH]: tx.hash.toString(),
238
268
  }))
239
269
  public async addNewTx(tx: ProcessedTx): Promise<void> {
240
- if (!this.provingState) {
270
+ const provingState = this?.provingState?.currentBlock;
271
+ if (!provingState) {
241
272
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions`);
242
273
  }
243
274
 
244
- if (!this.provingState.isAcceptingTransactions()) {
275
+ if (!provingState.isAcceptingTransactions()) {
245
276
  throw new Error(`Rollup not accepting further transactions`);
246
277
  }
247
278
 
@@ -254,34 +285,43 @@ export class ProvingOrchestrator implements BlockProver {
254
285
  return;
255
286
  }
256
287
 
257
- const [inputs, treeSnapshots] = await this.prepareTransaction(tx, this.provingState);
258
- this.enqueueFirstProofs(inputs, treeSnapshots, tx, this.provingState);
288
+ const [inputs, treeSnapshots] = await this.prepareTransaction(tx, provingState);
289
+ this.enqueueFirstProofs(inputs, treeSnapshots, tx, provingState);
290
+
291
+ if (provingState.transactionsReceived === provingState.totalNumTxs) {
292
+ logger.verbose(
293
+ `All transactions received for block ${provingState.globalVariables.blockNumber}. Assembling header.`,
294
+ );
295
+ await this.buildBlockHeader(provingState);
296
+ }
259
297
  }
260
298
 
261
299
  /**
262
300
  * Marks the block as full and pads it if required, no more transactions will be accepted.
301
+ * Computes the block header and updates the archive tree.
263
302
  */
264
303
  @trackSpan('ProvingOrchestrator.setBlockCompleted', function () {
265
- if (!this.provingState) {
304
+ const block = this.provingState?.currentBlock;
305
+ if (!block) {
266
306
  return {};
267
307
  }
268
-
269
308
  return {
270
- [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(),
271
- [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs,
272
- [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived,
309
+ [Attributes.BLOCK_NUMBER]: block.globalVariables.blockNumber.toNumber(),
310
+ [Attributes.BLOCK_SIZE]: block.totalNumTxs,
311
+ [Attributes.BLOCK_TXS_COUNT]: block.transactionsReceived,
273
312
  };
274
313
  })
275
314
  public async setBlockCompleted() {
276
- if (!this.provingState) {
315
+ const provingState = this.provingState?.currentBlock;
316
+ if (!provingState) {
277
317
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions or completing the block`);
278
318
  }
279
319
 
280
320
  // we may need to pad the rollup with empty transactions
281
- const paddingTxCount = this.provingState.totalNumTxs - this.provingState.transactionsReceived;
321
+ const paddingTxCount = provingState.totalNumTxs - provingState.transactionsReceived;
282
322
  if (paddingTxCount === 0) {
283
323
  return;
284
- } else if (this.provingState.totalNumTxs > 2) {
324
+ } else if (provingState.totalNumTxs > 2) {
285
325
  throw new Error(`Block not ready for completion: expecting ${paddingTxCount} more transactions.`);
286
326
  }
287
327
 
@@ -295,13 +335,13 @@ export class ProvingOrchestrator implements BlockProver {
295
335
  // Then enqueue the proving of all the transactions
296
336
  const unprovenPaddingTx = makeEmptyProcessedTx(
297
337
  this.db.getInitialHeader(),
298
- this.provingState.globalVariables.chainId,
299
- this.provingState.globalVariables.version,
338
+ provingState.globalVariables.chainId,
339
+ provingState.globalVariables.version,
300
340
  getVKTreeRoot(),
301
341
  );
302
342
  const txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }> = [];
303
343
  for (let i = 0; i < paddingTxCount; i++) {
304
- const [inputs, snapshot] = await this.prepareTransaction(unprovenPaddingTx, this.provingState);
344
+ const [inputs, snapshot] = await this.prepareTransaction(unprovenPaddingTx, provingState);
305
345
  const txInput = {
306
346
  inputs,
307
347
  snapshot,
@@ -310,13 +350,53 @@ export class ProvingOrchestrator implements BlockProver {
310
350
  }
311
351
 
312
352
  // Now enqueue the proving
313
- this.enqueuePaddingTxs(this.provingState, txInputs, unprovenPaddingTx);
353
+ this.enqueuePaddingTxs(provingState, txInputs, unprovenPaddingTx);
354
+
355
+ // And build the block header
356
+ logger.verbose(`Block ${provingState.globalVariables.blockNumber} padded with empty tx(s). Assembling header.`);
357
+ await this.buildBlockHeader(provingState);
358
+ }
359
+
360
+ private async buildBlockHeader(provingState: BlockProvingState) {
361
+ // Collect all new nullifiers, commitments, and contracts from all txs in this block to build body
362
+ const gasFees = provingState.globalVariables.gasFees;
363
+ const nonEmptyTxEffects: TxEffect[] = provingState!.allTxs
364
+ .map(txProvingState => toTxEffect(txProvingState.processedTx, gasFees))
365
+ .filter(txEffect => !txEffect.isEmpty());
366
+ const body = new Body(nonEmptyTxEffects);
367
+
368
+ // Given we've applied every change from this block, now assemble the block header
369
+ // and update the archive tree, so we're ready to start processing the next block
370
+ const header = await buildHeaderFromTxEffects(
371
+ body,
372
+ provingState.globalVariables,
373
+ provingState.newL1ToL2Messages,
374
+ this.db,
375
+ );
376
+
377
+ logger.verbose(`Updating archive tree with block ${provingState.blockNumber} header ${header.hash().toString()}`);
378
+ await this.db.updateArchive(header);
379
+
380
+ // Assemble the L2 block
381
+ const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.db);
382
+ const l2Block = L2Block.fromFields({ archive: newArchive, header, body });
383
+
384
+ if (!l2Block.body.getTxsEffectsHash().equals(header.contentCommitment.txsEffectsHash)) {
385
+ throw new Error(
386
+ `Txs effects hash mismatch, ${l2Block.body
387
+ .getTxsEffectsHash()
388
+ .toString('hex')} == ${header.contentCommitment.txsEffectsHash.toString('hex')} `,
389
+ );
390
+ }
391
+
392
+ logger.verbose(`Orchestrator finalised block ${l2Block.number}`);
393
+ provingState.block = l2Block;
314
394
  }
315
395
 
316
396
  // Enqueues the proving of the required padding transactions
317
397
  // If the fully proven padding transaction is not available, this will first be proven
318
398
  private enqueuePaddingTxs(
319
- provingState: ProvingState,
399
+ provingState: BlockProvingState,
320
400
  txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }>,
321
401
  unprovenPaddingTx: ProcessedTx,
322
402
  ) {
@@ -334,7 +414,7 @@ export class ProvingOrchestrator implements BlockProver {
334
414
  'ProvingOrchestrator.prover.getEmptyPrivateKernelProof',
335
415
  {
336
416
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
337
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' as CircuitName,
417
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' satisfies CircuitName,
338
418
  },
339
419
  signal =>
340
420
  this.prover.getEmptyPrivateKernelProof(
@@ -368,7 +448,7 @@ export class ProvingOrchestrator implements BlockProver {
368
448
  private provePaddingTransactions(
369
449
  txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }>,
370
450
  paddingTx: PaddingProcessedTx,
371
- provingState: ProvingState,
451
+ provingState: BlockProvingState,
372
452
  ) {
373
453
  // The padding tx contains the proof and vk, generated separately from the base inputs
374
454
  // Copy these into the base rollup inputs and enqueue the base rollup proof
@@ -385,9 +465,9 @@ export class ProvingOrchestrator implements BlockProver {
385
465
  }
386
466
 
387
467
  /**
388
- * Cancel any further proving of the block
468
+ * Cancel any further proving
389
469
  */
390
- public cancelBlock() {
470
+ public cancel() {
391
471
  for (const controller of this.pendingProvingJobs) {
392
472
  controller.abort();
393
473
  }
@@ -397,133 +477,83 @@ export class ProvingOrchestrator implements BlockProver {
397
477
 
398
478
  /**
399
479
  * Extract the block header from public inputs.
400
- * TODO(#7346): Refactor this once new batch rollup circuits are integrated
401
480
  * @returns The header of this proving state's block.
402
481
  */
403
- private async extractBlockHeader() {
404
- if (
405
- !this.provingState ||
406
- !this.provingState.blockRootRollupPublicInputs ||
407
- !this.provingState.finalRootParityInput?.publicInputs.shaRoot
408
- ) {
409
- throw new Error(`Invalid proving state, a block must be proven before its header can be extracted.`);
410
- }
411
-
412
- const rootRollupOutputs = this.provingState.blockRootRollupPublicInputs;
413
- const previousMergeData = this.provingState.getMergeInputs(0).inputs;
482
+ private extractBlockHeaderFromPublicInputs(
483
+ provingState: BlockProvingState,
484
+ rootRollupOutputs: BlockRootOrBlockMergePublicInputs,
485
+ ) {
486
+ const previousMergeData = provingState.getMergeInputs(0).inputs;
414
487
 
415
488
  if (!previousMergeData[0] || !previousMergeData[1]) {
416
489
  throw new Error(`Invalid proving state, final merge inputs before block root circuit missing.`);
417
490
  }
418
491
 
419
- const contentCommitment = new ContentCommitment(
420
- new Fr(previousMergeData[0].numTxs + previousMergeData[1].numTxs),
421
- sha256Trunc(
422
- Buffer.concat([previousMergeData[0].txsEffectsHash.toBuffer(), previousMergeData[1].txsEffectsHash.toBuffer()]),
423
- ),
424
- this.provingState.finalRootParityInput.publicInputs.shaRoot.toBuffer(),
425
- sha256Trunc(Buffer.concat([previousMergeData[0].outHash.toBuffer(), previousMergeData[1].outHash.toBuffer()])),
492
+ return buildHeaderFromCircuitOutputs(
493
+ [previousMergeData[0], previousMergeData[1]],
494
+ provingState.finalRootParityInput!.publicInputs,
495
+ rootRollupOutputs,
496
+ provingState.messageTreeSnapshotAfterInsertion,
497
+ logger,
426
498
  );
427
- const state = new StateReference(
428
- await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, this.db),
429
- previousMergeData[1].end,
430
- );
431
- const header = new Header(
432
- rootRollupOutputs.previousArchive,
433
- contentCommitment,
434
- state,
435
- previousMergeData[0].constants.globalVariables,
436
- previousMergeData[0].accumulatedFees.add(previousMergeData[1].accumulatedFees),
437
- );
438
- if (!header.hash().equals(rootRollupOutputs.endBlockHash)) {
439
- throw new Error(`Block header mismatch in finalise.`);
440
- }
441
- return header;
442
499
  }
443
500
 
444
501
  /**
445
- * Performs the final tree update for the block and returns the fully proven block.
502
+ * Returns the fully proven block. Requires proving to have been completed.
503
+ * @param index - The index of the block to finalise. Defaults to the last block.
446
504
  * @returns The fully proven block and proof.
447
505
  */
448
- @trackSpan('ProvingOrchestrator.finaliseBlock', function () {
449
- return {
450
- [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(),
451
- [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived,
452
- [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs,
453
- };
454
- })
455
- public async finaliseBlock() {
506
+ public finaliseBlock(index?: number) {
456
507
  try {
457
- if (!this.provingState || !this.provingState.blockRootRollupPublicInputs || !this.provingState.finalProof) {
458
- throw new Error(`Invalid proving state, a block must be proven before it can be finalised`);
459
- }
460
- if (this.provingState.block) {
461
- throw new Error('Block already finalised');
462
- }
463
-
464
- const rootRollupOutputs = this.provingState.blockRootRollupPublicInputs;
465
- const header = await this.extractBlockHeader();
466
-
467
- logger?.debug(`Updating and validating root trees`);
468
- await this.db.updateArchive(header);
469
-
470
- await validateBlockRootOutput(rootRollupOutputs, header, this.db);
471
-
472
- // Collect all new nullifiers, commitments, and contracts from all txs in this block
473
- const gasFees = this.provingState.globalVariables.gasFees;
474
- const nonEmptyTxEffects: TxEffect[] = this.provingState!.allTxs.map(txProvingState =>
475
- toTxEffect(txProvingState.processedTx, gasFees),
476
- ).filter(txEffect => !txEffect.isEmpty());
477
- const blockBody = new Body(nonEmptyTxEffects);
478
-
479
- const l2Block = L2Block.fromFields({
480
- archive: rootRollupOutputs.newArchive,
481
- header: header,
482
- body: blockBody,
483
- });
508
+ const block = this.provingState?.blocks[index ?? this.provingState?.blocks.length - 1];
484
509
 
485
- if (!l2Block.body.getTxsEffectsHash().equals(header.contentCommitment.txsEffectsHash)) {
486
- logger.debug(inspect(blockBody));
487
- throw new Error(
488
- `Txs effects hash mismatch, ${l2Block.body
489
- .getTxsEffectsHash()
490
- .toString('hex')} == ${header.contentCommitment.txsEffectsHash.toString('hex')} `,
491
- );
510
+ if (!block || !block.blockRootRollupPublicInputs || !block.finalProof || !block.block) {
511
+ throw new Error(`Invalid proving state, a block must be proven before it can be finalised`);
492
512
  }
493
513
 
494
- logger.info(`Orchestrator finalised block ${l2Block.number}`);
495
-
496
- this.provingState.block = l2Block;
497
-
498
514
  const blockResult: ProvingBlockResult = {
499
- proof: this.provingState.finalProof,
500
- aggregationObject: this.provingState.finalProof.extractAggregationObject(),
501
- block: l2Block,
515
+ proof: block.finalProof,
516
+ aggregationObject: block.finalProof.extractAggregationObject(),
517
+ block: block.block!,
502
518
  };
503
519
 
504
520
  pushTestData('blockResults', {
505
521
  proverId: this.proverId.toString(),
506
522
  vkTreeRoot: getVKTreeRoot().toString(),
507
- block: l2Block.toString(),
508
- proof: this.provingState.finalProof.toString(),
523
+ block: blockResult.block.toString(),
524
+ proof: blockResult.proof.toString(),
509
525
  aggregationObject: blockResult.aggregationObject.map(x => x.toString()),
510
526
  });
511
527
 
512
- return blockResult;
528
+ return Promise.resolve(blockResult);
513
529
  } catch (err) {
514
530
  throw new BlockProofError(
515
531
  err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err),
516
- this.provingState?.allTxs.map(x => Tx.getHash(x.processedTx)) ?? [],
532
+ this.provingState?.blocks[index ?? this.provingState?.blocks.length - 1]?.allTxs.map(x =>
533
+ Tx.getHash(x.processedTx),
534
+ ) ?? [],
517
535
  );
518
536
  }
519
537
  }
520
538
 
539
+ /**
540
+ * Returns the proof for the current epoch.
541
+ * Requires proving to have been completed.
542
+ */
543
+ public finaliseEpoch() {
544
+ if (!this.provingState || !this.provingState.rootRollupPublicInputs || !this.provingState.finalProof) {
545
+ throw new Error(`Invalid proving state, an epoch must be proven before it can be finalised`);
546
+ }
547
+
548
+ return { proof: this.provingState.finalProof, publicInputs: this.provingState.rootRollupPublicInputs };
549
+ }
550
+
521
551
  /**
522
552
  * Starts the proving process for the given transaction and adds it to our state
523
553
  * @param tx - The transaction whose proving we wish to commence
524
554
  * @param provingState - The proving state being worked on
525
555
  */
526
- private async prepareTransaction(tx: ProcessedTx, provingState: ProvingState) {
556
+ private async prepareTransaction(tx: ProcessedTx, provingState: BlockProvingState) {
527
557
  const txInputs = await this.prepareBaseRollupInputs(provingState, tx);
528
558
  if (!txInputs) {
529
559
  // This should not be possible
@@ -536,7 +566,7 @@ export class ProvingOrchestrator implements BlockProver {
536
566
  inputs: BaseRollupInputs,
537
567
  treeSnapshots: TreeSnapshots,
538
568
  tx: ProcessedTx,
539
- provingState: ProvingState,
569
+ provingState: BlockProvingState,
540
570
  ) {
541
571
  const txProvingState = new TxProvingState(tx, inputs, treeSnapshots);
542
572
  const txIndex = provingState.addNewTx(txProvingState);
@@ -557,7 +587,7 @@ export class ProvingOrchestrator implements BlockProver {
557
587
  * @param job - The actual job, returns a promise notifying of the job's completion
558
588
  */
559
589
  private deferredProving<T>(
560
- provingState: ProvingState | undefined,
590
+ provingState: EpochProvingState | BlockProvingState | undefined,
561
591
  request: (signal: AbortSignal) => Promise<T>,
562
592
  callback: (result: T) => void | Promise<void>,
563
593
  ) {
@@ -617,7 +647,7 @@ export class ProvingOrchestrator implements BlockProver {
617
647
  [Attributes.TX_HASH]: tx.hash.toString(),
618
648
  }))
619
649
  private async prepareBaseRollupInputs(
620
- provingState: ProvingState | undefined,
650
+ provingState: BlockProvingState | undefined,
621
651
  tx: ProcessedTx,
622
652
  ): Promise<[BaseRollupInputs, TreeSnapshots] | undefined> {
623
653
  if (!provingState?.verifyState()) {
@@ -655,34 +685,9 @@ export class ProvingOrchestrator implements BlockProver {
655
685
  return [inputs, treeSnapshots];
656
686
  }
657
687
 
658
- // Stores the intermediate inputs prepared for a merge proof
659
- private storeMergeInputs(
660
- provingState: ProvingState,
661
- currentLevel: bigint,
662
- currentIndex: bigint,
663
- mergeInputs: [
664
- BaseOrMergeRollupPublicInputs,
665
- RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH>,
666
- VerificationKeyAsFields,
667
- ],
668
- ) {
669
- const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
670
- currentLevel,
671
- currentIndex,
672
- );
673
- const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
674
- const ready = provingState.storeMergeInputs(mergeInputs, Number(indexWithinMerge), Number(mergeIndex));
675
- return {
676
- ready,
677
- indexWithinMergeLevel,
678
- mergeLevel,
679
- mergeInputData: provingState.getMergeInputs(Number(mergeIndex)),
680
- };
681
- }
682
-
683
688
  // Executes the base rollup circuit and stored the output as intermediate state for the parent merge/root circuit
684
689
  // Executes the next level of merge if all inputs are available
685
- private enqueueBaseRollup(provingState: ProvingState | undefined, index: bigint, tx: TxProvingState) {
690
+ private enqueueBaseRollup(provingState: BlockProvingState | undefined, index: bigint, tx: TxProvingState) {
686
691
  if (!provingState?.verifyState()) {
687
692
  logger.debug('Not running base rollup, state invalid');
688
693
  return;
@@ -743,7 +748,7 @@ export class ProvingOrchestrator implements BlockProver {
743
748
  {
744
749
  [Attributes.TX_HASH]: tx.processedTx.hash.toString(),
745
750
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
746
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' as CircuitName,
751
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' satisfies CircuitName,
747
752
  },
748
753
  signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal, provingState.epochNumber),
749
754
  ),
@@ -762,7 +767,7 @@ export class ProvingOrchestrator implements BlockProver {
762
767
 
763
768
  // Enqueues the tub circuit for a given transaction index
764
769
  // Once completed, will enqueue the next circuit, either a public kernel or the base rollup
765
- private enqueueTube(provingState: ProvingState, txIndex: number) {
770
+ private enqueueTube(provingState: BlockProvingState, txIndex: number) {
766
771
  if (!provingState?.verifyState()) {
767
772
  logger.debug('Not running tube circuit, state invalid');
768
773
  return;
@@ -779,7 +784,7 @@ export class ProvingOrchestrator implements BlockProver {
779
784
  {
780
785
  [Attributes.TX_HASH]: txProvingState.processedTx.hash.toString(),
781
786
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
782
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-circuit' as CircuitName,
787
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-circuit' satisfies CircuitName,
783
788
  },
784
789
  signal =>
785
790
  this.prover.getTubeProof(
@@ -791,14 +796,7 @@ export class ProvingOrchestrator implements BlockProver {
791
796
  result => {
792
797
  logger.debug(`Completed tube proof for tx index: ${txIndex}`);
793
798
  const nextKernelRequest = txProvingState.getNextPublicKernelFromTubeProof(result.tubeProof, result.tubeVK);
794
- this.checkAndEnqueueNextTxCircuit(
795
- provingState,
796
- txIndex,
797
- -1,
798
- result.tubeProof,
799
- result.tubeVK,
800
- nextKernelRequest,
801
- );
799
+ this.checkAndEnqueueNextTxCircuit(provingState, txIndex, result.tubeProof, result.tubeVK, nextKernelRequest);
802
800
  },
803
801
  );
804
802
  }
@@ -806,7 +804,7 @@ export class ProvingOrchestrator implements BlockProver {
806
804
  // Executes the merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
807
805
  // Enqueues the next level of merge if all inputs are available
808
806
  private enqueueMergeRollup(
809
- provingState: ProvingState,
807
+ provingState: BlockProvingState,
810
808
  level: bigint,
811
809
  index: bigint,
812
810
  mergeInputData: MergeRollupInputData,
@@ -823,7 +821,7 @@ export class ProvingOrchestrator implements BlockProver {
823
821
  'ProvingOrchestrator.prover.getMergeRollupProof',
824
822
  {
825
823
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
826
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' as CircuitName,
824
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' satisfies CircuitName,
827
825
  },
828
826
  signal => this.prover.getMergeRollupProof(inputs, signal, provingState.epochNumber),
829
827
  ),
@@ -838,29 +836,40 @@ export class ProvingOrchestrator implements BlockProver {
838
836
  }
839
837
 
840
838
  // Executes the block root rollup circuit
841
- private async enqueueBlockRootRollup(provingState: ProvingState | undefined) {
839
+ private enqueueBlockRootRollup(provingState: BlockProvingState | undefined) {
842
840
  if (!provingState?.verifyState()) {
843
- logger.debug('Not running root rollup, state no longer valid');
841
+ logger.debug('Not running block root rollup, state no longer valid');
844
842
  return;
845
843
  }
846
844
  const mergeInputData = provingState.getMergeInputs(0);
847
845
  const rootParityInput = provingState.finalRootParityInput!;
848
846
 
849
- const inputs = await getBlockRootRollupInput(
850
- mergeInputData.inputs[0]!,
851
- mergeInputData.proofs[0]!,
852
- mergeInputData.verificationKeys[0]!,
853
- mergeInputData.inputs[1]!,
854
- mergeInputData.proofs[1]!,
855
- mergeInputData.verificationKeys[1]!,
856
- rootParityInput,
857
- provingState.newL1ToL2Messages,
858
- provingState.messageTreeSnapshot,
859
- provingState.messageTreeRootSiblingPath,
860
- this.db,
861
- this.proverId,
847
+ logger.debug(
848
+ `Enqueuing block root rollup for block ${provingState.blockNumber} with ${provingState.newL1ToL2Messages.length} l1 to l2 msgs`,
849
+ );
850
+
851
+ const previousRollupData: BlockRootRollupInputs['previousRollupData'] = makeTuple(2, i =>
852
+ getPreviousRollupDataFromPublicInputs(
853
+ mergeInputData.inputs[i]!,
854
+ mergeInputData.proofs[i]!,
855
+ mergeInputData.verificationKeys[i]!,
856
+ ),
862
857
  );
863
858
 
859
+ const inputs = BlockRootRollupInputs.from({
860
+ previousRollupData,
861
+ l1ToL2Roots: rootParityInput,
862
+ newL1ToL2Messages: provingState.newL1ToL2Messages,
863
+ newL1ToL2MessageTreeRootSiblingPath: provingState.messageTreeRootSiblingPath,
864
+ startL1ToL2MessageTreeSnapshot: provingState.messageTreeSnapshot,
865
+ startArchiveSnapshot: provingState.archiveTreeSnapshot,
866
+ newArchiveSiblingPath: provingState.archiveTreeRootSiblingPath,
867
+ previousBlockHash: provingState.previousBlockHash,
868
+ proverId: this.proverId,
869
+ });
870
+
871
+ const shouldProveEpoch = this.provingState!.totalNumBlocks > 1;
872
+
864
873
  this.deferredProving(
865
874
  provingState,
866
875
  wrapCallbackInSpan(
@@ -868,25 +877,48 @@ export class ProvingOrchestrator implements BlockProver {
868
877
  'ProvingOrchestrator.prover.getBlockRootRollupProof',
869
878
  {
870
879
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
871
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-root-rollup' as CircuitName,
880
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-root-rollup' satisfies CircuitName,
872
881
  },
873
- signal => this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber),
882
+ signal =>
883
+ shouldProveEpoch
884
+ ? this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber)
885
+ : this.prover.getBlockRootRollupFinalProof(inputs, signal, provingState.epochNumber),
874
886
  ),
875
887
  result => {
888
+ const header = this.extractBlockHeaderFromPublicInputs(provingState, result.inputs);
889
+ if (!header.hash().equals(provingState.block!.header.hash())) {
890
+ logger.error(
891
+ `Block header mismatch\nCircuit:${inspect(header)}\nComputed:${inspect(provingState.block!.header)}`,
892
+ );
893
+ provingState.reject(`Block header hash mismatch`);
894
+ }
895
+
876
896
  provingState.blockRootRollupPublicInputs = result.inputs;
877
897
  provingState.finalProof = result.proof.binaryProof;
898
+ provingState.resolve({ status: PROVING_STATUS.SUCCESS });
899
+
900
+ logger.debug(`Completed proof for block root rollup for ${provingState.block?.number}`);
901
+ // validatePartialState(result.inputs.end, tx.treeSnapshots); // TODO(palla/prover)
878
902
 
879
- const provingResult: ProvingResult = {
880
- status: PROVING_STATUS.SUCCESS,
881
- };
882
- provingState.resolve(provingResult);
903
+ // TODO(palla/prover): Remove this once we've dropped the flow for proving single blocks
904
+ if (!shouldProveEpoch) {
905
+ logger.verbose(`Skipping epoch rollup, only one block in epoch`);
906
+ return;
907
+ }
908
+
909
+ const currentLevel = this.provingState!.numMergeLevels + 1n;
910
+ this.storeAndExecuteNextBlockMergeLevel(this.provingState!, currentLevel, BigInt(provingState.index), [
911
+ result.inputs,
912
+ result.proof,
913
+ result.verificationKey.keyAsFields,
914
+ ]);
883
915
  },
884
916
  );
885
917
  }
886
918
 
887
919
  // Executes the base parity circuit and stores the intermediate state for the root parity circuit
888
920
  // Enqueues the root parity circuit if all inputs are available
889
- private enqueueBaseParityCircuit(provingState: ProvingState, inputs: BaseParityInputs, index: number) {
921
+ private enqueueBaseParityCircuit(provingState: BlockProvingState, inputs: BaseParityInputs, index: number) {
890
922
  this.deferredProving(
891
923
  provingState,
892
924
  wrapCallbackInSpan(
@@ -894,7 +926,7 @@ export class ProvingOrchestrator implements BlockProver {
894
926
  'ProvingOrchestrator.prover.getBaseParityProof',
895
927
  {
896
928
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
897
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' as CircuitName,
929
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' satisfies CircuitName,
898
930
  },
899
931
  signal => this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber),
900
932
  ),
@@ -915,7 +947,7 @@ export class ProvingOrchestrator implements BlockProver {
915
947
 
916
948
  // Runs the root parity circuit ans stored the outputs
917
949
  // Enqueues the root rollup proof if all inputs are available
918
- private enqueueRootParityCircuit(provingState: ProvingState, inputs: RootParityInputs) {
950
+ private enqueueRootParityCircuit(provingState: BlockProvingState, inputs: RootParityInputs) {
919
951
  this.deferredProving(
920
952
  provingState,
921
953
  wrapCallbackInSpan(
@@ -923,23 +955,104 @@ export class ProvingOrchestrator implements BlockProver {
923
955
  'ProvingOrchestrator.prover.getRootParityProof',
924
956
  {
925
957
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
926
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' as CircuitName,
958
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' satisfies CircuitName,
927
959
  },
928
960
  signal => this.prover.getRootParityProof(inputs, signal, provingState.epochNumber),
929
961
  ),
930
- async rootInput => {
962
+ rootInput => {
931
963
  provingState!.finalRootParityInput = rootInput;
932
- await this.checkAndEnqueueBlockRootRollup(provingState);
964
+ this.checkAndEnqueueBlockRootRollup(provingState);
965
+ },
966
+ );
967
+ }
968
+
969
+ // Executes the block merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
970
+ // Enqueues the next level of merge if all inputs are available
971
+ private enqueueBlockMergeRollup(
972
+ provingState: EpochProvingState,
973
+ level: bigint,
974
+ index: bigint,
975
+ mergeInputData: BlockMergeRollupInputData,
976
+ ) {
977
+ const inputs = createBlockMergeRollupInputs(
978
+ [mergeInputData.inputs[0]!, mergeInputData.proofs[0]!, mergeInputData.verificationKeys[0]!],
979
+ [mergeInputData.inputs[1]!, mergeInputData.proofs[1]!, mergeInputData.verificationKeys[1]!],
980
+ );
981
+
982
+ this.deferredProving(
983
+ provingState,
984
+ wrapCallbackInSpan(
985
+ this.tracer,
986
+ 'ProvingOrchestrator.prover.getBlockMergeRollupProof',
987
+ {
988
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
989
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-merge-rollup' satisfies CircuitName,
990
+ },
991
+ signal => this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber),
992
+ ),
993
+ result => {
994
+ this.storeAndExecuteNextBlockMergeLevel(provingState, level, index, [
995
+ result.inputs,
996
+ result.proof,
997
+ result.verificationKey.keyAsFields,
998
+ ]);
999
+ },
1000
+ );
1001
+ }
1002
+
1003
+ // Executes the root rollup circuit
1004
+ private enqueueRootRollup(provingState: EpochProvingState | undefined) {
1005
+ if (!provingState?.verifyState()) {
1006
+ logger.debug('Not running root rollup, state no longer valid');
1007
+ return;
1008
+ }
1009
+
1010
+ logger.debug(`Preparing root rollup`);
1011
+ const mergeInputData = provingState.getMergeInputs(0);
1012
+
1013
+ const inputs = getRootRollupInput(
1014
+ mergeInputData.inputs[0]!,
1015
+ mergeInputData.proofs[0]!,
1016
+ mergeInputData.verificationKeys[0]!,
1017
+ mergeInputData.inputs[1]!,
1018
+ mergeInputData.proofs[1]!,
1019
+ mergeInputData.verificationKeys[1]!,
1020
+ this.proverId,
1021
+ );
1022
+
1023
+ this.deferredProving(
1024
+ provingState,
1025
+ wrapCallbackInSpan(
1026
+ this.tracer,
1027
+ 'ProvingOrchestrator.prover.getRootRollupProof',
1028
+ {
1029
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
1030
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup' satisfies CircuitName,
1031
+ },
1032
+ signal => this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber),
1033
+ ),
1034
+ result => {
1035
+ provingState.rootRollupPublicInputs = result.inputs;
1036
+ provingState.finalProof = result.proof.binaryProof;
1037
+ provingState.resolve({ status: PROVING_STATUS.SUCCESS });
933
1038
  },
934
1039
  );
935
1040
  }
936
1041
 
937
- private async checkAndEnqueueBlockRootRollup(provingState: ProvingState | undefined) {
1042
+ private checkAndEnqueueBlockRootRollup(provingState: BlockProvingState | undefined) {
938
1043
  if (!provingState?.isReadyForBlockRootRollup()) {
939
1044
  logger.debug('Not ready for root rollup');
940
1045
  return;
941
1046
  }
942
- await this.enqueueBlockRootRollup(provingState);
1047
+ this.enqueueBlockRootRollup(provingState);
1048
+ }
1049
+
1050
+ private checkAndEnqueueRootRollup(provingState: EpochProvingState | undefined) {
1051
+ if (!provingState?.isReadyForRootRollup()) {
1052
+ logger.debug('Not ready for root rollup');
1053
+ return;
1054
+ }
1055
+ this.enqueueRootRollup(provingState);
943
1056
  }
944
1057
 
945
1058
  /**
@@ -950,7 +1063,7 @@ export class ProvingOrchestrator implements BlockProver {
950
1063
  * @param mergeInputData - The inputs to be stored
951
1064
  */
952
1065
  private storeAndExecuteNextMergeLevel(
953
- provingState: ProvingState,
1066
+ provingState: BlockProvingState,
954
1067
  currentLevel: bigint,
955
1068
  currentIndex: bigint,
956
1069
  mergeInputData: [
@@ -959,19 +1072,68 @@ export class ProvingOrchestrator implements BlockProver {
959
1072
  VerificationKeyAsFields,
960
1073
  ],
961
1074
  ) {
962
- const result = this.storeMergeInputs(provingState, currentLevel, currentIndex, mergeInputData);
1075
+ const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
1076
+ currentLevel,
1077
+ currentIndex,
1078
+ );
1079
+ const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
1080
+ const ready = provingState.storeMergeInputs(mergeInputData, Number(indexWithinMerge), Number(mergeIndex));
1081
+ const nextMergeInputData = provingState.getMergeInputs(Number(mergeIndex));
1082
+
1083
+ // Are we ready to execute the next circuit?
1084
+ if (!ready) {
1085
+ return;
1086
+ }
1087
+
1088
+ if (mergeLevel === 0n) {
1089
+ this.checkAndEnqueueBlockRootRollup(provingState);
1090
+ } else {
1091
+ // onto the next merge level
1092
+ this.enqueueMergeRollup(provingState, mergeLevel, indexWithinMergeLevel, nextMergeInputData);
1093
+ }
1094
+ }
1095
+
1096
+ /**
1097
+ * Stores the inputs to a block merge/root circuit and enqueues the circuit if ready
1098
+ * @param provingState - The proving state being operated on
1099
+ * @param currentLevel - The level of the merge/root circuit
1100
+ * @param currentIndex - The index of the merge/root circuit
1101
+ * @param mergeInputData - The inputs to be stored
1102
+ */
1103
+ private storeAndExecuteNextBlockMergeLevel(
1104
+ provingState: EpochProvingState,
1105
+ currentLevel: bigint,
1106
+ currentIndex: bigint,
1107
+ mergeInputData: [
1108
+ BlockRootOrBlockMergePublicInputs,
1109
+ RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH>,
1110
+ VerificationKeyAsFields,
1111
+ ],
1112
+ ) {
1113
+ const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
1114
+ currentLevel,
1115
+ currentIndex,
1116
+ );
1117
+ logger.debug(`Computed merge for ${currentLevel}.${currentIndex} as ${mergeLevel}.${indexWithinMergeLevel}`);
1118
+ if (mergeLevel < 0n) {
1119
+ throw new Error(`Invalid merge level ${mergeLevel}`);
1120
+ }
1121
+
1122
+ const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
1123
+ const ready = provingState.storeMergeInputs(mergeInputData, Number(indexWithinMerge), Number(mergeIndex));
1124
+ const nextMergeInputData = provingState.getMergeInputs(Number(mergeIndex));
963
1125
 
964
1126
  // Are we ready to execute the next circuit?
965
- if (!result.ready) {
1127
+ if (!ready) {
1128
+ logger.debug(`Not ready to execute next block merge for level ${mergeLevel} index ${indexWithinMergeLevel}`);
966
1129
  return;
967
1130
  }
968
1131
 
969
- if (result.mergeLevel === 0n) {
970
- // TODO (alexg) remove this `void`
971
- void this.checkAndEnqueueBlockRootRollup(provingState);
1132
+ if (mergeLevel === 0n) {
1133
+ this.checkAndEnqueueRootRollup(provingState);
972
1134
  } else {
973
1135
  // onto the next merge level
974
- this.enqueueMergeRollup(provingState, result.mergeLevel, result.indexWithinMergeLevel, result.mergeInputData);
1136
+ this.enqueueBlockMergeRollup(provingState, mergeLevel, indexWithinMergeLevel, nextMergeInputData);
975
1137
  }
976
1138
  }
977
1139
 
@@ -982,7 +1144,7 @@ export class ProvingOrchestrator implements BlockProver {
982
1144
  * @param txIndex - The index of the transaction being proven
983
1145
  * @param functionIndex - The index of the function/kernel being proven
984
1146
  */
985
- private enqueueVM(provingState: ProvingState | undefined, txIndex: number, functionIndex: number) {
1147
+ private enqueueVM(provingState: BlockProvingState | undefined, txIndex: number, functionIndex: number) {
986
1148
  if (!provingState?.verifyState()) {
987
1149
  logger.debug(`Not running VM circuit as state is no longer valid`);
988
1150
  return;
@@ -1028,13 +1190,11 @@ export class ProvingOrchestrator implements BlockProver {
1028
1190
  logger.debug(`Proven VM for function index ${functionIndex} of tx index ${txIndex}`);
1029
1191
  this.checkAndEnqueuePublicKernelFromVMProof(provingState, txIndex, functionIndex, proofAndVk.proof);
1030
1192
  });
1031
- } else {
1032
- this.checkAndEnqueuePublicKernelFromVMProof(provingState, txIndex, functionIndex, /*vmProof=*/ makeEmptyProof());
1033
1193
  }
1034
1194
  }
1035
1195
 
1036
1196
  private checkAndEnqueuePublicKernelFromVMProof(
1037
- provingState: ProvingState,
1197
+ provingState: BlockProvingState,
1038
1198
  txIndex: number,
1039
1199
  functionIndex: number,
1040
1200
  vmProof: Proof,
@@ -1055,9 +1215,8 @@ export class ProvingOrchestrator implements BlockProver {
1055
1215
  // This could be either a public kernel or the base rollup
1056
1216
  // Alternatively, if we are still waiting on a public VM prof then it will continue waiting
1057
1217
  private checkAndEnqueueNextTxCircuit(
1058
- provingState: ProvingState,
1218
+ provingState: BlockProvingState,
1059
1219
  txIndex: number,
1060
- completedFunctionIndex: number,
1061
1220
  proof: RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH> | RecursiveProof<typeof TUBE_PROOF_LENGTH>,
1062
1221
  verificationKey: VerificationKeyData,
1063
1222
  nextKernelRequest: TxProvingInstruction,
@@ -1089,7 +1248,7 @@ export class ProvingOrchestrator implements BlockProver {
1089
1248
  throw new Error(`Error occurred, public function request undefined after kernel proof completed`);
1090
1249
  }
1091
1250
 
1092
- this.enqueuePublicKernel(provingState, txIndex, completedFunctionIndex + 1);
1251
+ this.enqueuePublicKernel(provingState, txIndex, nextKernelRequest.functionIndex!);
1093
1252
  }
1094
1253
 
1095
1254
  /**
@@ -1099,7 +1258,7 @@ export class ProvingOrchestrator implements BlockProver {
1099
1258
  * @param txIndex - The index of the transaction being proven
1100
1259
  * @param functionIndex - The index of the function/kernel being proven
1101
1260
  */
1102
- private enqueuePublicKernel(provingState: ProvingState | undefined, txIndex: number, functionIndex: number) {
1261
+ private enqueuePublicKernel(provingState: BlockProvingState | undefined, txIndex: number, functionIndex: number) {
1103
1262
  if (!provingState?.verifyState()) {
1104
1263
  logger.debug(`Not running public kernel circuit as state is no longer valid`);
1105
1264
  return;
@@ -1112,20 +1271,28 @@ export class ProvingOrchestrator implements BlockProver {
1112
1271
  provingState,
1113
1272
  wrapCallbackInSpan(
1114
1273
  this.tracer,
1115
- request.type === PublicKernelType.TAIL
1274
+ request.type === ProvingRequestType.PUBLIC_KERNEL_TAIL
1116
1275
  ? 'ProvingOrchestrator.prover.getPublicTailProof'
1117
- : 'ProvingOrchestrator.prover.getPublicKernelProof',
1276
+ : request.type === ProvingRequestType.PUBLIC_KERNEL_MERGE
1277
+ ? 'ProvingOrchestrator.prover.getPublicKernelMergeProof'
1278
+ : 'ProvingOrchestrator.prover.getPublicKernelInnerProof',
1118
1279
  {
1119
1280
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
1120
- [Attributes.PROTOCOL_CIRCUIT_NAME]: mapPublicKernelToCircuitName(request.type),
1281
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: mapProvingRequestTypeToCircuitName(request.type),
1121
1282
  },
1122
1283
  (
1123
1284
  signal,
1124
- ): Promise<PublicInputsAndRecursiveProof<KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs>> => {
1125
- if (request.type === PublicKernelType.TAIL) {
1126
- return this.prover.getPublicTailProof(request, signal, provingState.epochNumber);
1285
+ ): Promise<
1286
+ PublicInputsAndRecursiveProof<
1287
+ KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs | VMCircuitPublicInputs
1288
+ >
1289
+ > => {
1290
+ if (request.type === ProvingRequestType.PUBLIC_KERNEL_TAIL) {
1291
+ return this.prover.getPublicTailProof(request.inputs, signal, provingState.epochNumber);
1292
+ } else if (request.type === ProvingRequestType.PUBLIC_KERNEL_MERGE) {
1293
+ return this.prover.getPublicKernelMergeProof(request.inputs, signal, provingState.epochNumber);
1127
1294
  } else {
1128
- return this.prover.getPublicKernelProof(request, signal, provingState.epochNumber);
1295
+ return this.prover.getPublicKernelInnerProof(request.inputs, signal, provingState.epochNumber);
1129
1296
  }
1130
1297
  },
1131
1298
  ),
@@ -1138,7 +1305,6 @@ export class ProvingOrchestrator implements BlockProver {
1138
1305
  this.checkAndEnqueueNextTxCircuit(
1139
1306
  provingState,
1140
1307
  txIndex,
1141
- functionIndex,
1142
1308
  result.proof,
1143
1309
  result.verificationKey,
1144
1310
  nextKernelRequest,