@aztec/prover-client 0.55.1 → 0.57.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 (51) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/mocks/fixtures.d.ts.map +1 -1
  4. package/dest/mocks/fixtures.js +6 -27
  5. package/dest/mocks/test_context.d.ts +9 -10
  6. package/dest/mocks/test_context.d.ts.map +1 -1
  7. package/dest/mocks/test_context.js +13 -15
  8. package/dest/orchestrator/block-building-helpers.d.ts +12 -3
  9. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  10. package/dest/orchestrator/block-building-helpers.js +34 -32
  11. package/dest/orchestrator/{proving-state.d.ts → block-proving-state.d.ts} +19 -16
  12. package/dest/orchestrator/block-proving-state.d.ts.map +1 -0
  13. package/dest/orchestrator/block-proving-state.js +147 -0
  14. package/dest/orchestrator/epoch-proving-state.d.ts +63 -0
  15. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -0
  16. package/dest/orchestrator/epoch-proving-state.js +143 -0
  17. package/dest/orchestrator/orchestrator.d.ts +33 -15
  18. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  19. package/dest/orchestrator/orchestrator.js +315 -172
  20. package/dest/orchestrator/tx-proving-state.d.ts +3 -2
  21. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  22. package/dest/orchestrator/tx-proving-state.js +54 -26
  23. package/dest/prover-agent/memory-proving-queue.d.ts +11 -5
  24. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  25. package/dest/prover-agent/memory-proving-queue.js +16 -6
  26. package/dest/prover-agent/prover-agent.d.ts.map +1 -1
  27. package/dest/prover-agent/prover-agent.js +10 -10
  28. package/dest/prover-agent/rpc.d.ts.map +1 -1
  29. package/dest/prover-agent/rpc.js +8 -2
  30. package/dest/test/mock_prover.d.ts +6 -4
  31. package/dest/test/mock_prover.d.ts.map +1 -1
  32. package/dest/test/mock_prover.js +9 -3
  33. package/dest/tx-prover/tx-prover.d.ts +3 -3
  34. package/dest/tx-prover/tx-prover.d.ts.map +1 -1
  35. package/dest/tx-prover/tx-prover.js +1 -1
  36. package/package.json +15 -11
  37. package/src/index.ts +1 -1
  38. package/src/mocks/fixtures.ts +5 -49
  39. package/src/mocks/test_context.ts +15 -20
  40. package/src/orchestrator/block-building-helpers.ts +90 -57
  41. package/src/orchestrator/{proving-state.ts → block-proving-state.ts} +29 -53
  42. package/src/orchestrator/epoch-proving-state.ts +221 -0
  43. package/src/orchestrator/orchestrator.ts +494 -292
  44. package/src/orchestrator/tx-proving-state.ts +63 -27
  45. package/src/prover-agent/memory-proving-queue.ts +31 -16
  46. package/src/prover-agent/prover-agent.ts +11 -9
  47. package/src/prover-agent/rpc.ts +9 -0
  48. package/src/test/mock_prover.ts +23 -1
  49. package/src/tx-prover/tx-prover.ts +4 -4
  50. package/dest/orchestrator/proving-state.d.ts.map +0 -1
  51. package/dest/orchestrator/proving-state.js +0 -170
@@ -6,35 +6,28 @@ import {
6
6
  MerkleTreeId,
7
7
  type PaddingProcessedTx,
8
8
  type ProcessedTx,
9
- PublicKernelType,
10
- Tx,
9
+ ProvingRequestType,
10
+ type PublicInputsAndRecursiveProof,
11
+ type ServerCircuitProver,
11
12
  type TxEffect,
12
13
  UnencryptedTxL2Logs,
13
14
  makeEmptyProcessedTx,
14
15
  makePaddingProcessedTx,
15
- mapPublicKernelToCircuitName,
16
+ mapProvingRequestTypeToCircuitName,
16
17
  toTxEffect,
17
18
  } 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';
19
+ import { type EpochProver } from '@aztec/circuit-types/interfaces';
28
20
  import { type CircuitName } from '@aztec/circuit-types/stats';
29
21
  import {
30
22
  AvmCircuitInputs,
31
23
  type BaseOrMergeRollupPublicInputs,
32
24
  BaseParityInputs,
33
25
  type BaseRollupInputs,
34
- ContentCommitment,
26
+ type BlockRootOrBlockMergePublicInputs,
27
+ BlockRootRollupInputs,
28
+ EmptyBlockRootRollupInputs,
35
29
  Fr,
36
30
  type GlobalVariables,
37
- Header,
38
31
  type KernelCircuitPublicInputs,
39
32
  L1_TO_L2_MSG_SUBTREE_HEIGHT,
40
33
  L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
@@ -48,9 +41,9 @@ import {
48
41
  type RecursiveProof,
49
42
  type RootParityInput,
50
43
  RootParityInputs,
51
- StateReference,
52
44
  type TUBE_PROOF_LENGTH,
53
45
  TubeInputs,
46
+ type VMCircuitPublicInputs,
54
47
  type VerificationKeyAsFields,
55
48
  VerificationKeyData,
56
49
  makeEmptyProof,
@@ -58,7 +51,6 @@ import {
58
51
  } from '@aztec/circuits.js';
59
52
  import { makeTuple } from '@aztec/foundation/array';
60
53
  import { padArrayEnd } from '@aztec/foundation/collection';
61
- import { sha256Trunc } from '@aztec/foundation/crypto';
62
54
  import { AbortError } from '@aztec/foundation/error';
63
55
  import { createDebugLogger } from '@aztec/foundation/log';
64
56
  import { promiseWithResolvers } from '@aztec/foundation/promise';
@@ -73,16 +65,26 @@ import { inspect } from 'util';
73
65
 
74
66
  import {
75
67
  buildBaseRollupInput,
68
+ buildHeaderFromCircuitOutputs,
69
+ buildHeaderFromTxEffects,
70
+ createBlockMergeRollupInputs,
76
71
  createMergeRollupInputs,
77
- getBlockRootRollupInput,
72
+ getPreviousRollupDataFromPublicInputs,
73
+ getRootRollupInput,
74
+ getRootTreeSiblingPath,
78
75
  getSubtreeSiblingPath,
79
76
  getTreeSnapshot,
80
- validateBlockRootOutput,
81
77
  validatePartialState,
82
78
  validateTx,
83
79
  } from './block-building-helpers.js';
80
+ import { type BlockProvingState, type MergeRollupInputData } from './block-proving-state.js';
81
+ import {
82
+ type BlockMergeRollupInputData,
83
+ EpochProvingState,
84
+ type ProvingResult,
85
+ type TreeSnapshots,
86
+ } 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,11 +103,12 @@ 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
 
111
+ private provingPromise: Promise<ProvingResult> | undefined = undefined;
109
112
  private metrics: ProvingOrchestratorMetrics;
110
113
 
111
114
  constructor(
@@ -132,9 +135,20 @@ export class ProvingOrchestrator implements BlockProver {
132
135
  this.paddingTx = undefined;
133
136
  }
134
137
 
138
+ public startNewEpoch(epochNumber: number, totalNumBlocks: number) {
139
+ const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
140
+ const promise = _promise.catch((reason): ProvingResult => ({ status: 'failure', reason }));
141
+ if (totalNumBlocks <= 0 || !Number.isInteger(totalNumBlocks)) {
142
+ throw new Error(`Invalid number of blocks for epoch (got ${totalNumBlocks})`);
143
+ }
144
+ logger.info(`Starting epoch ${epochNumber} with ${totalNumBlocks} blocks`);
145
+ this.provingState = new EpochProvingState(epochNumber, totalNumBlocks, resolve, reject);
146
+ this.provingPromise = promise;
147
+ }
148
+
135
149
  /**
136
150
  * Starts off a new block
137
- * @param numTxs - The total number of transactions in the block. Must be a power of 2
151
+ * @param numTxs - The total number of transactions in the block.
138
152
  * @param globalVariables - The global variables for the block
139
153
  * @param l1ToL2Messages - The l1 to l2 messages for the block
140
154
  * @param verificationKeys - The private kernel verification keys
@@ -144,16 +158,24 @@ export class ProvingOrchestrator implements BlockProver {
144
158
  [Attributes.BLOCK_SIZE]: numTxs,
145
159
  [Attributes.BLOCK_NUMBER]: globalVariables.blockNumber.toNumber(),
146
160
  }))
147
- public async startNewBlock(
148
- numTxs: number,
149
- globalVariables: GlobalVariables,
150
- l1ToL2Messages: Fr[],
151
- ): Promise<ProvingTicket> {
161
+ public async startNewBlock(numTxs: number, globalVariables: GlobalVariables, l1ToL2Messages: Fr[]) {
162
+ if (!this.provingState) {
163
+ throw new Error(`Invalid proving state, call startNewEpoch before starting a block`);
164
+ }
165
+
166
+ if (!this.provingState?.isAcceptingBlocks()) {
167
+ throw new Error(`Epoch not accepting further blocks`);
168
+ }
169
+
152
170
  if (!Number.isInteger(numTxs) || numTxs < 2) {
153
- throw new Error(`Length of txs for the block should be at least two (got ${numTxs})`);
171
+ throw new Error(`Invalid number of txs for block (got ${numTxs})`);
154
172
  }
155
173
 
156
- // TODO(palla/prover-node): Store block number in the db itself to make this check more reliable,
174
+ if (this.provingState.currentBlock && !this.provingState.currentBlock.block) {
175
+ throw new Error(`Must end previous block before starting a new one`);
176
+ }
177
+
178
+ // TODO(palla/prover): Store block number in the db itself to make this check more reliable,
157
179
  // and turn this warning into an exception that we throw.
158
180
  const { blockNumber } = globalVariables;
159
181
  const dbBlockNumber = (await this.db.getTreeInfo(MerkleTreeId.ARCHIVE)).size - 1n;
@@ -163,11 +185,10 @@ export class ProvingOrchestrator implements BlockProver {
163
185
  );
164
186
  }
165
187
 
166
- // Cancel any currently proving block before starting a new one
167
- this.cancelBlock();
168
188
  logger.info(
169
189
  `Starting block ${globalVariables.blockNumber} for slot ${globalVariables.slotNumber} with ${numTxs} transactions`,
170
190
  );
191
+
171
192
  // we start the block by enqueueing all of the base parity circuits
172
193
  let baseParityInputs: BaseParityInputs[] = [];
173
194
  let l1ToL2MessagesPadded: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>;
@@ -197,36 +218,32 @@ export class ProvingOrchestrator implements BlockProver {
197
218
 
198
219
  // Update the local trees to include the new l1 to l2 messages
199
220
  await this.db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
200
-
201
- const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
202
- const promise = _promise.catch(
203
- (reason): ProvingResult => ({
204
- status: PROVING_STATUS.FAILURE,
205
- reason,
206
- }),
221
+ const messageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, this.db);
222
+
223
+ // Get archive snapshot before this block lands
224
+ const startArchiveSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.db);
225
+ const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, this.db);
226
+ const previousBlockHash = await this.db.getLeafValue(
227
+ MerkleTreeId.ARCHIVE,
228
+ BigInt(startArchiveSnapshot.nextAvailableLeafIndex - 1),
207
229
  );
208
230
 
209
- const provingState = new ProvingState(
231
+ this.provingState!.startNewBlock(
210
232
  numTxs,
211
- resolve,
212
- reject,
213
233
  globalVariables,
214
234
  l1ToL2MessagesPadded,
215
- baseParityInputs.length,
216
235
  messageTreeSnapshot,
217
236
  newL1ToL2MessageTreeRootSiblingPath,
237
+ messageTreeSnapshotAfterInsertion,
238
+ startArchiveSnapshot,
239
+ newArchiveSiblingPath,
240
+ previousBlockHash!,
218
241
  );
219
242
 
243
+ // Enqueue base parity circuits for the block
220
244
  for (let i = 0; i < baseParityInputs.length; i++) {
221
- this.enqueueBaseParityCircuit(provingState, baseParityInputs[i], i);
245
+ this.enqueueBaseParityCircuit(this.provingState!.currentBlock!, baseParityInputs[i], i);
222
246
  }
223
-
224
- this.provingState = provingState;
225
-
226
- const ticket: ProvingTicket = {
227
- provingPromise: promise,
228
- };
229
- return ticket;
230
247
  }
231
248
 
232
249
  /**
@@ -237,14 +254,19 @@ export class ProvingOrchestrator implements BlockProver {
237
254
  [Attributes.TX_HASH]: tx.hash.toString(),
238
255
  }))
239
256
  public async addNewTx(tx: ProcessedTx): Promise<void> {
240
- if (!this.provingState) {
257
+ const provingState = this?.provingState?.currentBlock;
258
+ if (!provingState) {
241
259
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions`);
242
260
  }
243
261
 
244
- if (!this.provingState.isAcceptingTransactions()) {
262
+ if (!provingState.isAcceptingTransactions()) {
245
263
  throw new Error(`Rollup not accepting further transactions`);
246
264
  }
247
265
 
266
+ if (!provingState.verifyState()) {
267
+ throw new Error(`Invalid proving state when adding a tx`);
268
+ }
269
+
248
270
  validateTx(tx);
249
271
 
250
272
  logger.info(`Received transaction: ${tx.hash}`);
@@ -254,69 +276,187 @@ export class ProvingOrchestrator implements BlockProver {
254
276
  return;
255
277
  }
256
278
 
257
- const [inputs, treeSnapshots] = await this.prepareTransaction(tx, this.provingState);
258
- this.enqueueFirstProofs(inputs, treeSnapshots, tx, this.provingState);
279
+ const [inputs, treeSnapshots] = await this.prepareTransaction(tx, provingState);
280
+ this.enqueueFirstProofs(inputs, treeSnapshots, tx, provingState);
281
+
282
+ if (provingState.transactionsReceived === provingState.totalNumTxs) {
283
+ logger.verbose(`All transactions received for block ${provingState.globalVariables.blockNumber}.`);
284
+ }
259
285
  }
260
286
 
261
287
  /**
262
288
  * Marks the block as full and pads it if required, no more transactions will be accepted.
289
+ * Computes the block header and updates the archive tree.
263
290
  */
264
291
  @trackSpan('ProvingOrchestrator.setBlockCompleted', function () {
265
- if (!this.provingState) {
292
+ const block = this.provingState?.currentBlock;
293
+ if (!block) {
266
294
  return {};
267
295
  }
268
-
269
296
  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,
297
+ [Attributes.BLOCK_NUMBER]: block.globalVariables.blockNumber.toNumber(),
298
+ [Attributes.BLOCK_SIZE]: block.totalNumTxs,
299
+ [Attributes.BLOCK_TXS_COUNT]: block.transactionsReceived,
273
300
  };
274
301
  })
275
- public async setBlockCompleted() {
276
- if (!this.provingState) {
302
+ public async setBlockCompleted(): Promise<L2Block> {
303
+ const provingState = this.provingState?.currentBlock;
304
+ if (!provingState) {
277
305
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions or completing the block`);
278
306
  }
279
307
 
280
- // we may need to pad the rollup with empty transactions
281
- const paddingTxCount = this.provingState.totalNumTxs - this.provingState.transactionsReceived;
282
- if (paddingTxCount === 0) {
283
- return;
284
- } else if (this.provingState.totalNumTxs > 2) {
308
+ // We may need to pad the rollup with empty transactions
309
+ const paddingTxCount = provingState.totalNumTxs - provingState.transactionsReceived;
310
+ if (paddingTxCount > 0 && provingState.totalNumTxs > 2) {
285
311
  throw new Error(`Block not ready for completion: expecting ${paddingTxCount} more transactions.`);
286
312
  }
287
313
 
288
- logger.debug(`Padding rollup with ${paddingTxCount} empty transactions`);
289
- // Make an empty padding transaction
290
- // Required for:
291
- // 0 (when we want an empty block, largely for testing), or
292
- // 1 (we need to pad with one tx as all rollup circuits require a pair of inputs) txs
293
- // Insert it into the tree the required number of times to get all of the
294
- // base rollup inputs
295
- // Then enqueue the proving of all the transactions
296
- const unprovenPaddingTx = makeEmptyProcessedTx(
297
- this.db.getInitialHeader(),
298
- this.provingState.globalVariables.chainId,
299
- this.provingState.globalVariables.version,
300
- getVKTreeRoot(),
314
+ if (paddingTxCount > 0) {
315
+ logger.debug(`Padding rollup with ${paddingTxCount} empty transactions`);
316
+ // Make an empty padding transaction
317
+ // Required for:
318
+ // 0 (when we want an empty block, largely for testing), or
319
+ // 1 (we need to pad with one tx as all rollup circuits require a pair of inputs) txs
320
+ // Insert it into the tree the required number of times to get all of the
321
+ // base rollup inputs
322
+ // Then enqueue the proving of all the transactions
323
+ const unprovenPaddingTx = makeEmptyProcessedTx(
324
+ this.db.getInitialHeader(),
325
+ provingState.globalVariables.chainId,
326
+ provingState.globalVariables.version,
327
+ getVKTreeRoot(),
328
+ );
329
+ const txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }> = [];
330
+ for (let i = 0; i < paddingTxCount; i++) {
331
+ const [inputs, snapshot] = await this.prepareTransaction(unprovenPaddingTx, provingState);
332
+ const txInput = {
333
+ inputs,
334
+ snapshot,
335
+ };
336
+ txInputs.push(txInput);
337
+ }
338
+
339
+ // Now enqueue the proving
340
+ this.enqueuePaddingTxs(provingState, txInputs, unprovenPaddingTx);
341
+ }
342
+
343
+ // And build the block header
344
+ logger.verbose(`Block ${provingState.globalVariables.blockNumber} completed. Assembling header.`);
345
+ await this.buildBlock(provingState);
346
+
347
+ // If the proofs were faster than the block building, then we need to try the block root rollup again here
348
+ this.checkAndEnqueueBlockRootRollup(provingState);
349
+ return provingState.block!;
350
+ }
351
+
352
+ /** Returns the block as built for a given index. */
353
+ public getBlock(index: number): L2Block {
354
+ const block = this.provingState?.blocks[index].block;
355
+ if (!block) {
356
+ throw new Error(`Block at index ${index} not available`);
357
+ }
358
+ return block;
359
+ }
360
+
361
+ @trackSpan('ProvingOrchestrator.padEpoch', function () {
362
+ if (!this.provingState) {
363
+ return {};
364
+ }
365
+ return {
366
+ [Attributes.EPOCH_NUMBER]: this.provingState.epochNumber,
367
+ [Attributes.EPOCH_SIZE]: this.provingState.totalNumBlocks,
368
+ };
369
+ })
370
+ private padEpoch() {
371
+ const provingState = this.provingState!;
372
+ const lastBlock = provingState.currentBlock?.block;
373
+ if (!lastBlock) {
374
+ throw new Error(`Epoch needs at least one completed block in order to be padded`);
375
+ }
376
+
377
+ const paddingBlockCount = Math.max(2, provingState.totalNumBlocks) - provingState.blocks.length;
378
+ if (paddingBlockCount === 0) {
379
+ return;
380
+ }
381
+
382
+ logger.debug(`Padding epoch proof with ${paddingBlockCount} empty block proofs`);
383
+
384
+ const inputs = EmptyBlockRootRollupInputs.from({
385
+ archive: lastBlock.archive,
386
+ blockHash: lastBlock.header.hash(),
387
+ globalVariables: lastBlock.header.globalVariables,
388
+ vkTreeRoot: getVKTreeRoot(),
389
+ proverId: this.proverId,
390
+ });
391
+
392
+ logger.debug(`Enqueuing deferred proving for padding block to enqueue ${paddingBlockCount} paddings`);
393
+ this.deferredProving(
394
+ provingState,
395
+ wrapCallbackInSpan(
396
+ this.tracer,
397
+ 'ProvingOrchestrator.prover.getEmptyBlockRootRollupProof',
398
+ {
399
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
400
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'empty-block-root-rollup' satisfies CircuitName,
401
+ },
402
+ signal => this.prover.getEmptyBlockRootRollupProof(inputs, signal, provingState.epochNumber),
403
+ ),
404
+ result => {
405
+ logger.debug(`Completed proof for padding block`);
406
+ const currentLevel = provingState.numMergeLevels + 1n;
407
+ for (let i = 0; i < paddingBlockCount; i++) {
408
+ logger.debug(`Enqueuing padding block with index ${provingState.blocks.length + i}`);
409
+ const index = BigInt(provingState.blocks.length + i);
410
+ this.storeAndExecuteNextBlockMergeLevel(provingState, currentLevel, index, [
411
+ result.inputs,
412
+ result.proof,
413
+ result.verificationKey.keyAsFields,
414
+ ]);
415
+ }
416
+ },
417
+ );
418
+ }
419
+
420
+ private async buildBlock(provingState: BlockProvingState) {
421
+ // Collect all new nullifiers, commitments, and contracts from all txs in this block to build body
422
+ const gasFees = provingState.globalVariables.gasFees;
423
+ const nonEmptyTxEffects: TxEffect[] = provingState!.allTxs
424
+ .map(txProvingState => toTxEffect(txProvingState.processedTx, gasFees))
425
+ .filter(txEffect => !txEffect.isEmpty());
426
+ const body = new Body(nonEmptyTxEffects);
427
+
428
+ // Given we've applied every change from this block, now assemble the block header
429
+ // and update the archive tree, so we're ready to start processing the next block
430
+ const header = await buildHeaderFromTxEffects(
431
+ body,
432
+ provingState.globalVariables,
433
+ provingState.newL1ToL2Messages,
434
+ this.db,
301
435
  );
302
- const txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }> = [];
303
- for (let i = 0; i < paddingTxCount; i++) {
304
- const [inputs, snapshot] = await this.prepareTransaction(unprovenPaddingTx, this.provingState);
305
- const txInput = {
306
- inputs,
307
- snapshot,
308
- };
309
- txInputs.push(txInput);
436
+
437
+ logger.verbose(`Updating archive tree with block ${provingState.blockNumber} header ${header.hash().toString()}`);
438
+ await this.db.updateArchive(header);
439
+
440
+ // Assemble the L2 block
441
+ const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.db);
442
+ const l2Block = new L2Block(newArchive, header, body);
443
+
444
+ if (!l2Block.body.getTxsEffectsHash().equals(header.contentCommitment.txsEffectsHash)) {
445
+ throw new Error(
446
+ `Txs effects hash mismatch, ${l2Block.body
447
+ .getTxsEffectsHash()
448
+ .toString('hex')} == ${header.contentCommitment.txsEffectsHash.toString('hex')} `,
449
+ );
310
450
  }
311
451
 
312
- // Now enqueue the proving
313
- this.enqueuePaddingTxs(this.provingState, txInputs, unprovenPaddingTx);
452
+ logger.verbose(`Orchestrator finalised block ${l2Block.number}`);
453
+ provingState.block = l2Block;
314
454
  }
315
455
 
316
456
  // Enqueues the proving of the required padding transactions
317
457
  // If the fully proven padding transaction is not available, this will first be proven
318
458
  private enqueuePaddingTxs(
319
- provingState: ProvingState,
459
+ provingState: BlockProvingState,
320
460
  txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }>,
321
461
  unprovenPaddingTx: ProcessedTx,
322
462
  ) {
@@ -334,7 +474,7 @@ export class ProvingOrchestrator implements BlockProver {
334
474
  'ProvingOrchestrator.prover.getEmptyPrivateKernelProof',
335
475
  {
336
476
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
337
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' as CircuitName,
477
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' satisfies CircuitName,
338
478
  },
339
479
  signal =>
340
480
  this.prover.getEmptyPrivateKernelProof(
@@ -368,7 +508,7 @@ export class ProvingOrchestrator implements BlockProver {
368
508
  private provePaddingTransactions(
369
509
  txInputs: Array<{ inputs: BaseRollupInputs; snapshot: TreeSnapshots }>,
370
510
  paddingTx: PaddingProcessedTx,
371
- provingState: ProvingState,
511
+ provingState: BlockProvingState,
372
512
  ) {
373
513
  // The padding tx contains the proof and vk, generated separately from the base inputs
374
514
  // Copy these into the base rollup inputs and enqueue the base rollup proof
@@ -385,9 +525,9 @@ export class ProvingOrchestrator implements BlockProver {
385
525
  }
386
526
 
387
527
  /**
388
- * Cancel any further proving of the block
528
+ * Cancel any further proving
389
529
  */
390
- public cancelBlock() {
530
+ public cancel() {
391
531
  for (const controller of this.pendingProvingJobs) {
392
532
  controller.abort();
393
533
  }
@@ -397,125 +537,52 @@ export class ProvingOrchestrator implements BlockProver {
397
537
 
398
538
  /**
399
539
  * Extract the block header from public inputs.
400
- * TODO(#7346): Refactor this once new batch rollup circuits are integrated
401
540
  * @returns The header of this proving state's block.
402
541
  */
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;
542
+ private extractBlockHeaderFromPublicInputs(
543
+ provingState: BlockProvingState,
544
+ rootRollupOutputs: BlockRootOrBlockMergePublicInputs,
545
+ ) {
546
+ const previousMergeData = provingState.getMergeInputs(0).inputs;
414
547
 
415
548
  if (!previousMergeData[0] || !previousMergeData[1]) {
416
549
  throw new Error(`Invalid proving state, final merge inputs before block root circuit missing.`);
417
550
  }
418
551
 
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()])),
426
- );
427
- const state = new StateReference(
428
- await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, this.db),
429
- previousMergeData[1].end,
552
+ return buildHeaderFromCircuitOutputs(
553
+ [previousMergeData[0], previousMergeData[1]],
554
+ provingState.finalRootParityInput!.publicInputs,
555
+ rootRollupOutputs,
556
+ provingState.messageTreeSnapshotAfterInsertion,
557
+ logger,
430
558
  );
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
559
  }
443
560
 
444
561
  /**
445
- * Performs the final tree update for the block and returns the fully proven block.
446
- * @returns The fully proven block and proof.
562
+ * Returns the proof for the current epoch.
447
563
  */
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() {
456
- 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
- });
484
-
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
- );
492
- }
564
+ public async finaliseEpoch() {
565
+ if (!this.provingState || !this.provingPromise) {
566
+ throw new Error(`Invalid proving state, an epoch must be proven before it can be finalised`);
567
+ }
493
568
 
494
- logger.info(`Orchestrator finalised block ${l2Block.number}`);
569
+ this.padEpoch();
495
570
 
496
- this.provingState.block = l2Block;
571
+ const result = await this.provingPromise!;
572
+ if (result.status === 'failure') {
573
+ throw new Error(`Epoch proving failed: ${result.reason}`);
574
+ }
497
575
 
498
- const blockResult: ProvingBlockResult = {
499
- proof: this.provingState.finalProof,
500
- aggregationObject: this.provingState.finalProof.extractAggregationObject(),
501
- block: l2Block,
502
- };
576
+ if (!this.provingState.rootRollupPublicInputs || !this.provingState.finalProof) {
577
+ throw new Error(`Invalid proving state, missing root rollup public inputs or final proof`);
578
+ }
503
579
 
504
- pushTestData('blockResults', {
505
- proverId: this.proverId.toString(),
506
- vkTreeRoot: getVKTreeRoot().toString(),
507
- block: l2Block.toString(),
508
- proof: this.provingState.finalProof.toString(),
509
- aggregationObject: blockResult.aggregationObject.map(x => x.toString()),
510
- });
580
+ pushTestData('epochProofResult', {
581
+ proof: this.provingState.finalProof.toString(),
582
+ publicInputs: this.provingState.rootRollupPublicInputs.toString(),
583
+ });
511
584
 
512
- return blockResult;
513
- } catch (err) {
514
- throw new BlockProofError(
515
- err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err),
516
- this.provingState?.allTxs.map(x => Tx.getHash(x.processedTx)) ?? [],
517
- );
518
- }
585
+ return { proof: this.provingState.finalProof, publicInputs: this.provingState.rootRollupPublicInputs };
519
586
  }
520
587
 
521
588
  /**
@@ -523,7 +590,7 @@ export class ProvingOrchestrator implements BlockProver {
523
590
  * @param tx - The transaction whose proving we wish to commence
524
591
  * @param provingState - The proving state being worked on
525
592
  */
526
- private async prepareTransaction(tx: ProcessedTx, provingState: ProvingState) {
593
+ private async prepareTransaction(tx: ProcessedTx, provingState: BlockProvingState) {
527
594
  const txInputs = await this.prepareBaseRollupInputs(provingState, tx);
528
595
  if (!txInputs) {
529
596
  // This should not be possible
@@ -536,7 +603,7 @@ export class ProvingOrchestrator implements BlockProver {
536
603
  inputs: BaseRollupInputs,
537
604
  treeSnapshots: TreeSnapshots,
538
605
  tx: ProcessedTx,
539
- provingState: ProvingState,
606
+ provingState: BlockProvingState,
540
607
  ) {
541
608
  const txProvingState = new TxProvingState(tx, inputs, treeSnapshots);
542
609
  const txIndex = provingState.addNewTx(txProvingState);
@@ -557,7 +624,7 @@ export class ProvingOrchestrator implements BlockProver {
557
624
  * @param job - The actual job, returns a promise notifying of the job's completion
558
625
  */
559
626
  private deferredProving<T>(
560
- provingState: ProvingState | undefined,
627
+ provingState: EpochProvingState | BlockProvingState | undefined,
561
628
  request: (signal: AbortSignal) => Promise<T>,
562
629
  callback: (result: T) => void | Promise<void>,
563
630
  ) {
@@ -617,7 +684,7 @@ export class ProvingOrchestrator implements BlockProver {
617
684
  [Attributes.TX_HASH]: tx.hash.toString(),
618
685
  }))
619
686
  private async prepareBaseRollupInputs(
620
- provingState: ProvingState | undefined,
687
+ provingState: BlockProvingState | undefined,
621
688
  tx: ProcessedTx,
622
689
  ): Promise<[BaseRollupInputs, TreeSnapshots] | undefined> {
623
690
  if (!provingState?.verifyState()) {
@@ -655,34 +722,9 @@ export class ProvingOrchestrator implements BlockProver {
655
722
  return [inputs, treeSnapshots];
656
723
  }
657
724
 
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
725
  // Executes the base rollup circuit and stored the output as intermediate state for the parent merge/root circuit
684
726
  // Executes the next level of merge if all inputs are available
685
- private enqueueBaseRollup(provingState: ProvingState | undefined, index: bigint, tx: TxProvingState) {
727
+ private enqueueBaseRollup(provingState: BlockProvingState | undefined, index: bigint, tx: TxProvingState) {
686
728
  if (!provingState?.verifyState()) {
687
729
  logger.debug('Not running base rollup, state invalid');
688
730
  return;
@@ -743,7 +785,7 @@ export class ProvingOrchestrator implements BlockProver {
743
785
  {
744
786
  [Attributes.TX_HASH]: tx.processedTx.hash.toString(),
745
787
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
746
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' as CircuitName,
788
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' satisfies CircuitName,
747
789
  },
748
790
  signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal, provingState.epochNumber),
749
791
  ),
@@ -762,7 +804,7 @@ export class ProvingOrchestrator implements BlockProver {
762
804
 
763
805
  // Enqueues the tub circuit for a given transaction index
764
806
  // Once completed, will enqueue the next circuit, either a public kernel or the base rollup
765
- private enqueueTube(provingState: ProvingState, txIndex: number) {
807
+ private enqueueTube(provingState: BlockProvingState, txIndex: number) {
766
808
  if (!provingState?.verifyState()) {
767
809
  logger.debug('Not running tube circuit, state invalid');
768
810
  return;
@@ -779,7 +821,7 @@ export class ProvingOrchestrator implements BlockProver {
779
821
  {
780
822
  [Attributes.TX_HASH]: txProvingState.processedTx.hash.toString(),
781
823
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
782
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-circuit' as CircuitName,
824
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-circuit' satisfies CircuitName,
783
825
  },
784
826
  signal =>
785
827
  this.prover.getTubeProof(
@@ -791,14 +833,7 @@ export class ProvingOrchestrator implements BlockProver {
791
833
  result => {
792
834
  logger.debug(`Completed tube proof for tx index: ${txIndex}`);
793
835
  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
- );
836
+ this.checkAndEnqueueNextTxCircuit(provingState, txIndex, result.tubeProof, result.tubeVK, nextKernelRequest);
802
837
  },
803
838
  );
804
839
  }
@@ -806,7 +841,7 @@ export class ProvingOrchestrator implements BlockProver {
806
841
  // Executes the merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
807
842
  // Enqueues the next level of merge if all inputs are available
808
843
  private enqueueMergeRollup(
809
- provingState: ProvingState,
844
+ provingState: BlockProvingState,
810
845
  level: bigint,
811
846
  index: bigint,
812
847
  mergeInputData: MergeRollupInputData,
@@ -823,7 +858,7 @@ export class ProvingOrchestrator implements BlockProver {
823
858
  'ProvingOrchestrator.prover.getMergeRollupProof',
824
859
  {
825
860
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
826
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' as CircuitName,
861
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' satisfies CircuitName,
827
862
  },
828
863
  signal => this.prover.getMergeRollupProof(inputs, signal, provingState.epochNumber),
829
864
  ),
@@ -838,29 +873,44 @@ export class ProvingOrchestrator implements BlockProver {
838
873
  }
839
874
 
840
875
  // Executes the block root rollup circuit
841
- private async enqueueBlockRootRollup(provingState: ProvingState | undefined) {
842
- if (!provingState?.verifyState()) {
843
- logger.debug('Not running root rollup, state no longer valid');
876
+ private enqueueBlockRootRollup(provingState: BlockProvingState) {
877
+ if (!provingState.block) {
878
+ throw new Error(`Invalid proving state for block root rollup, block not available`);
879
+ }
880
+
881
+ if (!provingState.verifyState()) {
882
+ logger.debug('Not running block root rollup, state no longer valid');
844
883
  return;
845
884
  }
885
+
886
+ provingState.blockRootRollupStarted = true;
846
887
  const mergeInputData = provingState.getMergeInputs(0);
847
888
  const rootParityInput = provingState.finalRootParityInput!;
848
889
 
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,
890
+ logger.debug(
891
+ `Enqueuing block root rollup for block ${provingState.blockNumber} with ${provingState.newL1ToL2Messages.length} l1 to l2 msgs`,
862
892
  );
863
893
 
894
+ const previousRollupData: BlockRootRollupInputs['previousRollupData'] = makeTuple(2, i =>
895
+ getPreviousRollupDataFromPublicInputs(
896
+ mergeInputData.inputs[i]!,
897
+ mergeInputData.proofs[i]!,
898
+ mergeInputData.verificationKeys[i]!,
899
+ ),
900
+ );
901
+
902
+ const inputs = BlockRootRollupInputs.from({
903
+ previousRollupData,
904
+ l1ToL2Roots: rootParityInput,
905
+ newL1ToL2Messages: provingState.newL1ToL2Messages,
906
+ newL1ToL2MessageTreeRootSiblingPath: provingState.messageTreeRootSiblingPath,
907
+ startL1ToL2MessageTreeSnapshot: provingState.messageTreeSnapshot,
908
+ startArchiveSnapshot: provingState.archiveTreeSnapshot,
909
+ newArchiveSiblingPath: provingState.archiveTreeRootSiblingPath,
910
+ previousBlockHash: provingState.previousBlockHash,
911
+ proverId: this.proverId,
912
+ });
913
+
864
914
  this.deferredProving(
865
915
  provingState,
866
916
  wrapCallbackInSpan(
@@ -868,25 +918,38 @@ export class ProvingOrchestrator implements BlockProver {
868
918
  'ProvingOrchestrator.prover.getBlockRootRollupProof',
869
919
  {
870
920
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
871
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-root-rollup' as CircuitName,
921
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-root-rollup' satisfies CircuitName,
872
922
  },
873
923
  signal => this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber),
874
924
  ),
875
925
  result => {
926
+ const header = this.extractBlockHeaderFromPublicInputs(provingState, result.inputs);
927
+ if (!header.hash().equals(provingState.block!.header.hash())) {
928
+ logger.error(
929
+ `Block header mismatch\nCircuit:${inspect(header)}\nComputed:${inspect(provingState.block!.header)}`,
930
+ );
931
+ provingState.reject(`Block header hash mismatch`);
932
+ }
933
+
876
934
  provingState.blockRootRollupPublicInputs = result.inputs;
877
935
  provingState.finalProof = result.proof.binaryProof;
878
936
 
879
- const provingResult: ProvingResult = {
880
- status: PROVING_STATUS.SUCCESS,
881
- };
882
- provingState.resolve(provingResult);
937
+ logger.debug(`Completed proof for block root rollup for ${provingState.block?.number}`);
938
+ // validatePartialState(result.inputs.end, tx.treeSnapshots); // TODO(palla/prover)
939
+
940
+ const currentLevel = this.provingState!.numMergeLevels + 1n;
941
+ this.storeAndExecuteNextBlockMergeLevel(this.provingState!, currentLevel, BigInt(provingState.index), [
942
+ result.inputs,
943
+ result.proof,
944
+ result.verificationKey.keyAsFields,
945
+ ]);
883
946
  },
884
947
  );
885
948
  }
886
949
 
887
950
  // Executes the base parity circuit and stores the intermediate state for the root parity circuit
888
951
  // Enqueues the root parity circuit if all inputs are available
889
- private enqueueBaseParityCircuit(provingState: ProvingState, inputs: BaseParityInputs, index: number) {
952
+ private enqueueBaseParityCircuit(provingState: BlockProvingState, inputs: BaseParityInputs, index: number) {
890
953
  this.deferredProving(
891
954
  provingState,
892
955
  wrapCallbackInSpan(
@@ -894,7 +957,7 @@ export class ProvingOrchestrator implements BlockProver {
894
957
  'ProvingOrchestrator.prover.getBaseParityProof',
895
958
  {
896
959
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
897
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' as CircuitName,
960
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' satisfies CircuitName,
898
961
  },
899
962
  signal => this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber),
900
963
  ),
@@ -915,7 +978,7 @@ export class ProvingOrchestrator implements BlockProver {
915
978
 
916
979
  // Runs the root parity circuit ans stored the outputs
917
980
  // Enqueues the root rollup proof if all inputs are available
918
- private enqueueRootParityCircuit(provingState: ProvingState, inputs: RootParityInputs) {
981
+ private enqueueRootParityCircuit(provingState: BlockProvingState, inputs: RootParityInputs) {
919
982
  this.deferredProving(
920
983
  provingState,
921
984
  wrapCallbackInSpan(
@@ -923,23 +986,109 @@ export class ProvingOrchestrator implements BlockProver {
923
986
  'ProvingOrchestrator.prover.getRootParityProof',
924
987
  {
925
988
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
926
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' as CircuitName,
989
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' satisfies CircuitName,
927
990
  },
928
991
  signal => this.prover.getRootParityProof(inputs, signal, provingState.epochNumber),
929
992
  ),
930
- async rootInput => {
993
+ rootInput => {
931
994
  provingState!.finalRootParityInput = rootInput;
932
- await this.checkAndEnqueueBlockRootRollup(provingState);
995
+ this.checkAndEnqueueBlockRootRollup(provingState);
933
996
  },
934
997
  );
935
998
  }
936
999
 
937
- private async checkAndEnqueueBlockRootRollup(provingState: ProvingState | undefined) {
1000
+ // Executes the block merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
1001
+ // Enqueues the next level of merge if all inputs are available
1002
+ private enqueueBlockMergeRollup(
1003
+ provingState: EpochProvingState,
1004
+ level: bigint,
1005
+ index: bigint,
1006
+ mergeInputData: BlockMergeRollupInputData,
1007
+ ) {
1008
+ const inputs = createBlockMergeRollupInputs(
1009
+ [mergeInputData.inputs[0]!, mergeInputData.proofs[0]!, mergeInputData.verificationKeys[0]!],
1010
+ [mergeInputData.inputs[1]!, mergeInputData.proofs[1]!, mergeInputData.verificationKeys[1]!],
1011
+ );
1012
+
1013
+ this.deferredProving(
1014
+ provingState,
1015
+ wrapCallbackInSpan(
1016
+ this.tracer,
1017
+ 'ProvingOrchestrator.prover.getBlockMergeRollupProof',
1018
+ {
1019
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
1020
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-merge-rollup' satisfies CircuitName,
1021
+ },
1022
+ signal => this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber),
1023
+ ),
1024
+ result => {
1025
+ this.storeAndExecuteNextBlockMergeLevel(provingState, level, index, [
1026
+ result.inputs,
1027
+ result.proof,
1028
+ result.verificationKey.keyAsFields,
1029
+ ]);
1030
+ },
1031
+ );
1032
+ }
1033
+
1034
+ // Executes the root rollup circuit
1035
+ private enqueueRootRollup(provingState: EpochProvingState | undefined) {
1036
+ if (!provingState?.verifyState()) {
1037
+ logger.debug('Not running root rollup, state no longer valid');
1038
+ return;
1039
+ }
1040
+
1041
+ logger.debug(`Preparing root rollup`);
1042
+ const mergeInputData = provingState.getMergeInputs(0);
1043
+
1044
+ const inputs = getRootRollupInput(
1045
+ mergeInputData.inputs[0]!,
1046
+ mergeInputData.proofs[0]!,
1047
+ mergeInputData.verificationKeys[0]!,
1048
+ mergeInputData.inputs[1]!,
1049
+ mergeInputData.proofs[1]!,
1050
+ mergeInputData.verificationKeys[1]!,
1051
+ this.proverId,
1052
+ );
1053
+
1054
+ this.deferredProving(
1055
+ provingState,
1056
+ wrapCallbackInSpan(
1057
+ this.tracer,
1058
+ 'ProvingOrchestrator.prover.getRootRollupProof',
1059
+ {
1060
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
1061
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup' satisfies CircuitName,
1062
+ },
1063
+ signal => this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber),
1064
+ ),
1065
+ result => {
1066
+ logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
1067
+ provingState.rootRollupPublicInputs = result.inputs;
1068
+ provingState.finalProof = result.proof.binaryProof;
1069
+ provingState.resolve({ status: 'success' });
1070
+ },
1071
+ );
1072
+ }
1073
+
1074
+ private checkAndEnqueueBlockRootRollup(provingState: BlockProvingState) {
938
1075
  if (!provingState?.isReadyForBlockRootRollup()) {
939
1076
  logger.debug('Not ready for root rollup');
940
1077
  return;
941
1078
  }
942
- await this.enqueueBlockRootRollup(provingState);
1079
+ if (provingState.blockRootRollupStarted) {
1080
+ logger.debug('Block root rollup already started');
1081
+ return;
1082
+ }
1083
+ this.enqueueBlockRootRollup(provingState);
1084
+ }
1085
+
1086
+ private checkAndEnqueueRootRollup(provingState: EpochProvingState | undefined) {
1087
+ if (!provingState?.isReadyForRootRollup()) {
1088
+ logger.debug('Not ready for root rollup');
1089
+ return;
1090
+ }
1091
+ this.enqueueRootRollup(provingState);
943
1092
  }
944
1093
 
945
1094
  /**
@@ -950,7 +1099,7 @@ export class ProvingOrchestrator implements BlockProver {
950
1099
  * @param mergeInputData - The inputs to be stored
951
1100
  */
952
1101
  private storeAndExecuteNextMergeLevel(
953
- provingState: ProvingState,
1102
+ provingState: BlockProvingState,
954
1103
  currentLevel: bigint,
955
1104
  currentIndex: bigint,
956
1105
  mergeInputData: [
@@ -959,19 +1108,68 @@ export class ProvingOrchestrator implements BlockProver {
959
1108
  VerificationKeyAsFields,
960
1109
  ],
961
1110
  ) {
962
- const result = this.storeMergeInputs(provingState, currentLevel, currentIndex, mergeInputData);
1111
+ const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
1112
+ currentLevel,
1113
+ currentIndex,
1114
+ );
1115
+ const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
1116
+ const ready = provingState.storeMergeInputs(mergeInputData, Number(indexWithinMerge), Number(mergeIndex));
1117
+ const nextMergeInputData = provingState.getMergeInputs(Number(mergeIndex));
1118
+
1119
+ // Are we ready to execute the next circuit?
1120
+ if (!ready) {
1121
+ return;
1122
+ }
1123
+
1124
+ if (mergeLevel === 0n) {
1125
+ this.checkAndEnqueueBlockRootRollup(provingState);
1126
+ } else {
1127
+ // onto the next merge level
1128
+ this.enqueueMergeRollup(provingState, mergeLevel, indexWithinMergeLevel, nextMergeInputData);
1129
+ }
1130
+ }
1131
+
1132
+ /**
1133
+ * Stores the inputs to a block merge/root circuit and enqueues the circuit if ready
1134
+ * @param provingState - The proving state being operated on
1135
+ * @param currentLevel - The level of the merge/root circuit
1136
+ * @param currentIndex - The index of the merge/root circuit
1137
+ * @param mergeInputData - The inputs to be stored
1138
+ */
1139
+ private storeAndExecuteNextBlockMergeLevel(
1140
+ provingState: EpochProvingState,
1141
+ currentLevel: bigint,
1142
+ currentIndex: bigint,
1143
+ mergeInputData: [
1144
+ BlockRootOrBlockMergePublicInputs,
1145
+ RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH>,
1146
+ VerificationKeyAsFields,
1147
+ ],
1148
+ ) {
1149
+ const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
1150
+ currentLevel,
1151
+ currentIndex,
1152
+ );
1153
+ logger.debug(`Computed merge for ${currentLevel}.${currentIndex} as ${mergeLevel}.${indexWithinMergeLevel}`);
1154
+ if (mergeLevel < 0n) {
1155
+ throw new Error(`Invalid merge level ${mergeLevel}`);
1156
+ }
1157
+
1158
+ const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
1159
+ const ready = provingState.storeMergeInputs(mergeInputData, Number(indexWithinMerge), Number(mergeIndex));
1160
+ const nextMergeInputData = provingState.getMergeInputs(Number(mergeIndex));
963
1161
 
964
1162
  // Are we ready to execute the next circuit?
965
- if (!result.ready) {
1163
+ if (!ready) {
1164
+ logger.debug(`Not ready to execute next block merge for level ${mergeLevel} index ${indexWithinMergeLevel}`);
966
1165
  return;
967
1166
  }
968
1167
 
969
- if (result.mergeLevel === 0n) {
970
- // TODO (alexg) remove this `void`
971
- void this.checkAndEnqueueBlockRootRollup(provingState);
1168
+ if (mergeLevel === 0n) {
1169
+ this.checkAndEnqueueRootRollup(provingState);
972
1170
  } else {
973
1171
  // onto the next merge level
974
- this.enqueueMergeRollup(provingState, result.mergeLevel, result.indexWithinMergeLevel, result.mergeInputData);
1172
+ this.enqueueBlockMergeRollup(provingState, mergeLevel, indexWithinMergeLevel, nextMergeInputData);
975
1173
  }
976
1174
  }
977
1175
 
@@ -982,7 +1180,7 @@ export class ProvingOrchestrator implements BlockProver {
982
1180
  * @param txIndex - The index of the transaction being proven
983
1181
  * @param functionIndex - The index of the function/kernel being proven
984
1182
  */
985
- private enqueueVM(provingState: ProvingState | undefined, txIndex: number, functionIndex: number) {
1183
+ private enqueueVM(provingState: BlockProvingState | undefined, txIndex: number, functionIndex: number) {
986
1184
  if (!provingState?.verifyState()) {
987
1185
  logger.debug(`Not running VM circuit as state is no longer valid`);
988
1186
  return;
@@ -1028,13 +1226,11 @@ export class ProvingOrchestrator implements BlockProver {
1028
1226
  logger.debug(`Proven VM for function index ${functionIndex} of tx index ${txIndex}`);
1029
1227
  this.checkAndEnqueuePublicKernelFromVMProof(provingState, txIndex, functionIndex, proofAndVk.proof);
1030
1228
  });
1031
- } else {
1032
- this.checkAndEnqueuePublicKernelFromVMProof(provingState, txIndex, functionIndex, /*vmProof=*/ makeEmptyProof());
1033
1229
  }
1034
1230
  }
1035
1231
 
1036
1232
  private checkAndEnqueuePublicKernelFromVMProof(
1037
- provingState: ProvingState,
1233
+ provingState: BlockProvingState,
1038
1234
  txIndex: number,
1039
1235
  functionIndex: number,
1040
1236
  vmProof: Proof,
@@ -1055,9 +1251,8 @@ export class ProvingOrchestrator implements BlockProver {
1055
1251
  // This could be either a public kernel or the base rollup
1056
1252
  // Alternatively, if we are still waiting on a public VM prof then it will continue waiting
1057
1253
  private checkAndEnqueueNextTxCircuit(
1058
- provingState: ProvingState,
1254
+ provingState: BlockProvingState,
1059
1255
  txIndex: number,
1060
- completedFunctionIndex: number,
1061
1256
  proof: RecursiveProof<typeof NESTED_RECURSIVE_PROOF_LENGTH> | RecursiveProof<typeof TUBE_PROOF_LENGTH>,
1062
1257
  verificationKey: VerificationKeyData,
1063
1258
  nextKernelRequest: TxProvingInstruction,
@@ -1089,7 +1284,7 @@ export class ProvingOrchestrator implements BlockProver {
1089
1284
  throw new Error(`Error occurred, public function request undefined after kernel proof completed`);
1090
1285
  }
1091
1286
 
1092
- this.enqueuePublicKernel(provingState, txIndex, completedFunctionIndex + 1);
1287
+ this.enqueuePublicKernel(provingState, txIndex, nextKernelRequest.functionIndex!);
1093
1288
  }
1094
1289
 
1095
1290
  /**
@@ -1099,7 +1294,7 @@ export class ProvingOrchestrator implements BlockProver {
1099
1294
  * @param txIndex - The index of the transaction being proven
1100
1295
  * @param functionIndex - The index of the function/kernel being proven
1101
1296
  */
1102
- private enqueuePublicKernel(provingState: ProvingState | undefined, txIndex: number, functionIndex: number) {
1297
+ private enqueuePublicKernel(provingState: BlockProvingState | undefined, txIndex: number, functionIndex: number) {
1103
1298
  if (!provingState?.verifyState()) {
1104
1299
  logger.debug(`Not running public kernel circuit as state is no longer valid`);
1105
1300
  return;
@@ -1112,20 +1307,28 @@ export class ProvingOrchestrator implements BlockProver {
1112
1307
  provingState,
1113
1308
  wrapCallbackInSpan(
1114
1309
  this.tracer,
1115
- request.type === PublicKernelType.TAIL
1310
+ request.type === ProvingRequestType.PUBLIC_KERNEL_TAIL
1116
1311
  ? 'ProvingOrchestrator.prover.getPublicTailProof'
1117
- : 'ProvingOrchestrator.prover.getPublicKernelProof',
1312
+ : request.type === ProvingRequestType.PUBLIC_KERNEL_MERGE
1313
+ ? 'ProvingOrchestrator.prover.getPublicKernelMergeProof'
1314
+ : 'ProvingOrchestrator.prover.getPublicKernelInnerProof',
1118
1315
  {
1119
1316
  [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
1120
- [Attributes.PROTOCOL_CIRCUIT_NAME]: mapPublicKernelToCircuitName(request.type),
1317
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: mapProvingRequestTypeToCircuitName(request.type),
1121
1318
  },
1122
1319
  (
1123
1320
  signal,
1124
- ): Promise<PublicInputsAndRecursiveProof<KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs>> => {
1125
- if (request.type === PublicKernelType.TAIL) {
1126
- return this.prover.getPublicTailProof(request, signal, provingState.epochNumber);
1321
+ ): Promise<
1322
+ PublicInputsAndRecursiveProof<
1323
+ KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs | VMCircuitPublicInputs
1324
+ >
1325
+ > => {
1326
+ if (request.type === ProvingRequestType.PUBLIC_KERNEL_TAIL) {
1327
+ return this.prover.getPublicTailProof(request.inputs, signal, provingState.epochNumber);
1328
+ } else if (request.type === ProvingRequestType.PUBLIC_KERNEL_MERGE) {
1329
+ return this.prover.getPublicKernelMergeProof(request.inputs, signal, provingState.epochNumber);
1127
1330
  } else {
1128
- return this.prover.getPublicKernelProof(request, signal, provingState.epochNumber);
1331
+ return this.prover.getPublicKernelInnerProof(request.inputs, signal, provingState.epochNumber);
1129
1332
  }
1130
1333
  },
1131
1334
  ),
@@ -1138,7 +1341,6 @@ export class ProvingOrchestrator implements BlockProver {
1138
1341
  this.checkAndEnqueueNextTxCircuit(
1139
1342
  provingState,
1140
1343
  txIndex,
1141
- functionIndex,
1142
1344
  result.proof,
1143
1345
  result.verificationKey,
1144
1346
  nextKernelRequest,