@aztec/prover-client 0.0.1-fake-ceab37513c → 0.0.6-commit.a2d1860fe9

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 (148) hide show
  1. package/dest/config.d.ts +2 -2
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +2 -2
  4. package/dest/index.d.ts +1 -1
  5. package/dest/light/index.d.ts +2 -0
  6. package/dest/light/index.d.ts.map +1 -0
  7. package/dest/light/index.js +1 -0
  8. package/dest/light/lightweight_checkpoint_builder.d.ts +48 -0
  9. package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -0
  10. package/dest/light/lightweight_checkpoint_builder.js +202 -0
  11. package/dest/mocks/fixtures.d.ts +5 -5
  12. package/dest/mocks/fixtures.d.ts.map +1 -1
  13. package/dest/mocks/fixtures.js +35 -16
  14. package/dest/mocks/test_context.d.ts +39 -33
  15. package/dest/mocks/test_context.d.ts.map +1 -1
  16. package/dest/mocks/test_context.js +141 -82
  17. package/dest/orchestrator/block-building-helpers.d.ts +34 -34
  18. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  19. package/dest/orchestrator/block-building-helpers.js +151 -187
  20. package/dest/orchestrator/block-proving-state.d.ts +71 -55
  21. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  22. package/dest/orchestrator/block-proving-state.js +280 -185
  23. package/dest/orchestrator/checkpoint-proving-state.d.ts +76 -0
  24. package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -0
  25. package/dest/orchestrator/checkpoint-proving-state.js +243 -0
  26. package/dest/orchestrator/epoch-proving-state.d.ts +40 -32
  27. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  28. package/dest/orchestrator/epoch-proving-state.js +165 -85
  29. package/dest/orchestrator/index.d.ts +1 -1
  30. package/dest/orchestrator/orchestrator.d.ts +51 -35
  31. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  32. package/dest/orchestrator/orchestrator.js +847 -329
  33. package/dest/orchestrator/orchestrator_metrics.d.ts +1 -3
  34. package/dest/orchestrator/orchestrator_metrics.d.ts.map +1 -1
  35. package/dest/orchestrator/orchestrator_metrics.js +2 -15
  36. package/dest/orchestrator/tx-proving-state.d.ts +15 -12
  37. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  38. package/dest/orchestrator/tx-proving-state.js +27 -33
  39. package/dest/prover-client/factory.d.ts +3 -3
  40. package/dest/prover-client/factory.d.ts.map +1 -1
  41. package/dest/prover-client/index.d.ts +1 -1
  42. package/dest/prover-client/prover-client.d.ts +5 -5
  43. package/dest/prover-client/prover-client.d.ts.map +1 -1
  44. package/dest/prover-client/prover-client.js +15 -10
  45. package/dest/prover-client/server-epoch-prover.d.ts +13 -11
  46. package/dest/prover-client/server-epoch-prover.d.ts.map +1 -1
  47. package/dest/prover-client/server-epoch-prover.js +9 -9
  48. package/dest/proving_broker/broker_prover_facade.d.ts +28 -21
  49. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
  50. package/dest/proving_broker/broker_prover_facade.js +45 -36
  51. package/dest/proving_broker/config.d.ts +24 -8
  52. package/dest/proving_broker/config.d.ts.map +1 -1
  53. package/dest/proving_broker/config.js +26 -3
  54. package/dest/proving_broker/factory.d.ts +1 -1
  55. package/dest/proving_broker/fixtures.d.ts +3 -2
  56. package/dest/proving_broker/fixtures.d.ts.map +1 -1
  57. package/dest/proving_broker/fixtures.js +3 -2
  58. package/dest/proving_broker/index.d.ts +1 -1
  59. package/dest/proving_broker/proof_store/factory.d.ts +2 -5
  60. package/dest/proving_broker/proof_store/factory.d.ts.map +1 -1
  61. package/dest/proving_broker/proof_store/factory.js +7 -30
  62. package/dest/proving_broker/proof_store/file_store_proof_store.d.ts +18 -0
  63. package/dest/proving_broker/proof_store/file_store_proof_store.d.ts.map +1 -0
  64. package/dest/proving_broker/proof_store/file_store_proof_store.js +60 -0
  65. package/dest/proving_broker/proof_store/index.d.ts +2 -1
  66. package/dest/proving_broker/proof_store/index.d.ts.map +1 -1
  67. package/dest/proving_broker/proof_store/index.js +1 -0
  68. package/dest/proving_broker/proof_store/inline_proof_store.d.ts +1 -1
  69. package/dest/proving_broker/proof_store/inline_proof_store.d.ts.map +1 -1
  70. package/dest/proving_broker/proof_store/proof_store.d.ts +1 -1
  71. package/dest/proving_broker/proving_agent.d.ts +5 -9
  72. package/dest/proving_broker/proving_agent.d.ts.map +1 -1
  73. package/dest/proving_broker/proving_agent.js +4 -19
  74. package/dest/proving_broker/proving_broker.d.ts +7 -4
  75. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  76. package/dest/proving_broker/proving_broker.js +64 -30
  77. package/dest/proving_broker/proving_broker_database/memory.d.ts +3 -2
  78. package/dest/proving_broker/proving_broker_database/memory.d.ts.map +1 -1
  79. package/dest/proving_broker/proving_broker_database/persisted.d.ts +5 -3
  80. package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
  81. package/dest/proving_broker/proving_broker_database/persisted.js +392 -3
  82. package/dest/proving_broker/proving_broker_database.d.ts +3 -2
  83. package/dest/proving_broker/proving_broker_database.d.ts.map +1 -1
  84. package/dest/proving_broker/proving_broker_instrumentation.d.ts +1 -1
  85. package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -1
  86. package/dest/proving_broker/proving_broker_instrumentation.js +15 -35
  87. package/dest/proving_broker/proving_job_controller.d.ts +5 -3
  88. package/dest/proving_broker/proving_job_controller.d.ts.map +1 -1
  89. package/dest/proving_broker/proving_job_controller.js +46 -24
  90. package/dest/proving_broker/rpc.d.ts +7 -5
  91. package/dest/proving_broker/rpc.d.ts.map +1 -1
  92. package/dest/proving_broker/rpc.js +8 -0
  93. package/dest/test/mock_proof_store.d.ts +1 -1
  94. package/dest/test/mock_proof_store.d.ts.map +1 -1
  95. package/dest/test/mock_prover.d.ts +23 -19
  96. package/dest/test/mock_prover.d.ts.map +1 -1
  97. package/dest/test/mock_prover.js +38 -23
  98. package/package.json +21 -20
  99. package/src/config.ts +2 -2
  100. package/src/light/index.ts +1 -0
  101. package/src/light/lightweight_checkpoint_builder.ts +294 -0
  102. package/src/mocks/fixtures.ts +43 -37
  103. package/src/mocks/test_context.ts +201 -114
  104. package/src/orchestrator/block-building-helpers.ts +233 -313
  105. package/src/orchestrator/block-proving-state.ts +324 -247
  106. package/src/orchestrator/checkpoint-proving-state.ts +349 -0
  107. package/src/orchestrator/epoch-proving-state.ts +229 -132
  108. package/src/orchestrator/orchestrator.ts +630 -385
  109. package/src/orchestrator/orchestrator_metrics.ts +2 -25
  110. package/src/orchestrator/tx-proving-state.ts +49 -60
  111. package/src/prover-client/factory.ts +6 -2
  112. package/src/prover-client/prover-client.ts +31 -23
  113. package/src/prover-client/server-epoch-prover.ts +30 -21
  114. package/src/proving_broker/broker_prover_facade.ts +183 -118
  115. package/src/proving_broker/config.ts +30 -1
  116. package/src/proving_broker/fixtures.ts +8 -3
  117. package/src/proving_broker/proof_store/factory.ts +10 -32
  118. package/src/proving_broker/proof_store/file_store_proof_store.ts +78 -0
  119. package/src/proving_broker/proof_store/index.ts +1 -0
  120. package/src/proving_broker/proving_agent.ts +6 -19
  121. package/src/proving_broker/proving_broker.ts +72 -28
  122. package/src/proving_broker/proving_broker_database/memory.ts +2 -1
  123. package/src/proving_broker/proving_broker_database/persisted.ts +20 -5
  124. package/src/proving_broker/proving_broker_database.ts +2 -1
  125. package/src/proving_broker/proving_broker_instrumentation.ts +14 -35
  126. package/src/proving_broker/proving_job_controller.ts +51 -25
  127. package/src/proving_broker/rpc.ts +14 -0
  128. package/src/test/mock_prover.ts +144 -74
  129. package/dest/bin/get-proof-inputs.d.ts +0 -2
  130. package/dest/bin/get-proof-inputs.d.ts.map +0 -1
  131. package/dest/bin/get-proof-inputs.js +0 -51
  132. package/dest/block-factory/index.d.ts +0 -2
  133. package/dest/block-factory/index.d.ts.map +0 -1
  134. package/dest/block-factory/index.js +0 -1
  135. package/dest/block-factory/light.d.ts +0 -36
  136. package/dest/block-factory/light.d.ts.map +0 -1
  137. package/dest/block-factory/light.js +0 -87
  138. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts +0 -14
  139. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts.map +0 -1
  140. package/dest/proving_broker/proof_store/gcs_proof_store.js +0 -52
  141. package/dest/proving_broker/proving_agent_instrumentation.d.ts +0 -8
  142. package/dest/proving_broker/proving_agent_instrumentation.d.ts.map +0 -1
  143. package/dest/proving_broker/proving_agent_instrumentation.js +0 -16
  144. package/src/bin/get-proof-inputs.ts +0 -59
  145. package/src/block-factory/index.ts +0 -1
  146. package/src/block-factory/light.ts +0 -114
  147. package/src/proving_broker/proof_store/gcs_proof_store.ts +0 -76
  148. package/src/proving_broker/proving_agent_instrumentation.ts +0 -21
@@ -1,41 +1,48 @@
1
- import { BlobAccumulatorPublicInputs, FinalBlobBatchingChallenges } from '@aztec/blob-lib';
1
+ import { BatchedBlob, FinalBlobBatchingChallenges, SpongeBlob } from '@aztec/blob-lib/types';
2
2
  import {
3
3
  L1_TO_L2_MSG_SUBTREE_HEIGHT,
4
- L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
4
+ L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
5
+ NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
5
6
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
6
7
  NUM_BASE_PARITY_PER_ROOT_PARITY,
7
- type TUBE_PROOF_LENGTH,
8
8
  } from '@aztec/constants';
9
- import { padArrayEnd, times } from '@aztec/foundation/collection';
9
+ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
10
+ import { padArrayEnd } from '@aztec/foundation/collection';
11
+ import { Fr } from '@aztec/foundation/curves/bn254';
10
12
  import { AbortError } from '@aztec/foundation/error';
11
- import { Fr } from '@aztec/foundation/fields';
12
- import { createLogger } from '@aztec/foundation/log';
13
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
13
14
  import { promiseWithResolvers } from '@aztec/foundation/promise';
14
15
  import { assertLength } from '@aztec/foundation/serialize';
15
16
  import { pushTestData } from '@aztec/foundation/testing';
16
17
  import { elapsed } from '@aztec/foundation/timer';
17
18
  import type { TreeNodeLocation } from '@aztec/foundation/trees';
18
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
19
- import { readAvmMinimalPublicTxInputsFromFile } from '@aztec/simulator/public/fixtures';
20
- import { EthAddress, L2Block } from '@aztec/stdlib/block';
19
+ import { EthAddress } from '@aztec/stdlib/block';
21
20
  import type {
22
21
  EpochProver,
23
22
  ForkMerkleTreeOperations,
24
23
  MerkleTreeWriteOperations,
25
- ProofAndVerificationKey,
24
+ PublicInputsAndRecursiveProof,
25
+ ReadonlyWorldStateAccess,
26
26
  ServerCircuitProver,
27
27
  } from '@aztec/stdlib/interfaces/server';
28
- import { BaseParityInputs } from '@aztec/stdlib/parity';
28
+ import type { Proof } from '@aztec/stdlib/proofs';
29
29
  import {
30
30
  type BaseRollupHints,
31
- EmptyBlockRootRollupInputs,
32
- PrivateBaseRollupInputs,
33
- SingleTxBlockRootRollupInputs,
34
- TubeInputs,
31
+ BlockRootEmptyTxFirstRollupPrivateInputs,
32
+ BlockRootFirstRollupPrivateInputs,
33
+ BlockRootSingleTxFirstRollupPrivateInputs,
34
+ BlockRootSingleTxRollupPrivateInputs,
35
+ CheckpointConstantData,
36
+ CheckpointRootSingleBlockRollupPrivateInputs,
37
+ PrivateTxBaseRollupPrivateInputs,
38
+ PublicChonkVerifierPrivateInputs,
39
+ PublicChonkVerifierPublicInputs,
40
+ RootRollupPublicInputs,
35
41
  } from '@aztec/stdlib/rollup';
36
42
  import type { CircuitName } from '@aztec/stdlib/stats';
37
43
  import { type AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
38
- import { type BlockHeader, type GlobalVariables, type ProcessedTx, type Tx, toNumBlobFields } from '@aztec/stdlib/tx';
44
+ import type { BlockHeader, ProcessedTx, Tx } from '@aztec/stdlib/tx';
45
+ import type { UInt64 } from '@aztec/stdlib/types';
39
46
  import {
40
47
  Attributes,
41
48
  type TelemetryClient,
@@ -48,8 +55,9 @@ import {
48
55
  import { inspect } from 'util';
49
56
 
50
57
  import {
51
- buildHeaderAndBodyFromTxs,
58
+ buildHeaderFromCircuitOutputs,
52
59
  getLastSiblingPath,
60
+ getPublicChonkVerifierPrivateInputsFromTx,
53
61
  getRootTreeSiblingPath,
54
62
  getSubtreeSiblingPath,
55
63
  getTreeSnapshot,
@@ -58,12 +66,11 @@ import {
58
66
  validateTx,
59
67
  } from './block-building-helpers.js';
60
68
  import type { BlockProvingState } from './block-proving-state.js';
69
+ import type { CheckpointProvingState } from './checkpoint-proving-state.js';
61
70
  import { EpochProvingState, type ProvingResult, type TreeSnapshots } from './epoch-proving-state.js';
62
71
  import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js';
63
72
  import { TxProvingState } from './tx-proving-state.js';
64
73
 
65
- const logger = createLogger('prover-client:orchestrator');
66
-
67
74
  /**
68
75
  * Implements an event driven proving scheduler to build the recursive proof tree. The idea being:
69
76
  * 1. Transactions are provided to the scheduler post simulation.
@@ -84,14 +91,19 @@ export class ProvingOrchestrator implements EpochProver {
84
91
 
85
92
  private provingPromise: Promise<ProvingResult> | undefined = undefined;
86
93
  private metrics: ProvingOrchestratorMetrics;
87
- private dbs: Map<number, MerkleTreeWriteOperations> = new Map();
94
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
95
+ private dbs: Map<BlockNumber, MerkleTreeWriteOperations> = new Map();
96
+ private logger: Logger;
88
97
 
89
98
  constructor(
90
- private dbProvider: ForkMerkleTreeOperations,
99
+ private dbProvider: ReadonlyWorldStateAccess & ForkMerkleTreeOperations,
91
100
  private prover: ServerCircuitProver,
92
101
  private readonly proverId: EthAddress,
102
+ private readonly cancelJobsOnStop: boolean = false,
93
103
  telemetryClient: TelemetryClient = getTelemetryClient(),
104
+ bindings?: LoggerBindings,
94
105
  ) {
106
+ this.logger = createLogger('prover-client:orchestrator', bindings);
95
107
  this.metrics = new ProvingOrchestratorMetrics(telemetryClient, 'ProvingOrchestrator');
96
108
  }
97
109
 
@@ -103,28 +115,34 @@ export class ProvingOrchestrator implements EpochProver {
103
115
  return this.proverId;
104
116
  }
105
117
 
118
+ public getNumActiveForks() {
119
+ return this.dbs.size;
120
+ }
121
+
106
122
  public stop(): Promise<void> {
107
123
  this.cancel();
108
124
  return Promise.resolve();
109
125
  }
110
126
 
111
127
  public startNewEpoch(
112
- epochNumber: number,
113
- firstBlockNumber: number,
114
- totalNumBlocks: number,
128
+ epochNumber: EpochNumber,
129
+ totalNumCheckpoints: number,
115
130
  finalBlobBatchingChallenges: FinalBlobBatchingChallenges,
116
131
  ) {
132
+ if (this.provingState?.verifyState()) {
133
+ throw new Error(
134
+ `Cannot start epoch ${epochNumber} when epoch ${this.provingState.epochNumber} is still being processed.`,
135
+ );
136
+ }
137
+
117
138
  const { promise: _promise, resolve, reject } = promiseWithResolvers<ProvingResult>();
118
139
  const promise = _promise.catch((reason): ProvingResult => ({ status: 'failure', reason }));
119
- if (totalNumBlocks <= 0 || !Number.isInteger(totalNumBlocks)) {
120
- throw new Error(`Invalid number of blocks for epoch (got ${totalNumBlocks})`);
121
- }
122
- logger.info(`Starting epoch ${epochNumber} with ${totalNumBlocks} blocks`);
140
+ this.logger.info(`Starting epoch ${epochNumber} with ${totalNumCheckpoints} checkpoints.`);
123
141
  this.provingState = new EpochProvingState(
124
142
  epochNumber,
125
- firstBlockNumber,
126
- totalNumBlocks,
143
+ totalNumCheckpoints,
127
144
  finalBlobBatchingChallenges,
145
+ provingState => this.checkAndEnqueueCheckpointRootRollup(provingState),
128
146
  resolve,
129
147
  reject,
130
148
  );
@@ -132,58 +150,128 @@ export class ProvingOrchestrator implements EpochProver {
132
150
  }
133
151
 
134
152
  /**
135
- * Starts off a new block
136
- * @param globalVariables - The global variables for the block
137
- * @param l1ToL2Messages - The l1 to l2 messages for the block
138
- * @returns A proving ticket, containing a promise notifying of proving completion
153
+ * Starts a new checkpoint.
154
+ * @param checkpointIndex - The index of the checkpoint in the epoch.
155
+ * @param constants - The constants for this checkpoint.
156
+ * @param l1ToL2Messages - The set of L1 to L2 messages to be inserted at the beginning of this checkpoint.
157
+ * @param totalNumBlocks - The total number of blocks expected in the checkpoint (must be at least one).
158
+ * @param headerOfLastBlockInPreviousCheckpoint - The header of the last block in the previous checkpoint.
139
159
  */
140
- @trackSpan('ProvingOrchestrator.startNewBlock', globalVariables => ({
141
- [Attributes.BLOCK_NUMBER]: globalVariables.blockNumber,
142
- }))
143
- public async startNewBlock(globalVariables: GlobalVariables, l1ToL2Messages: Fr[], previousBlockHeader: BlockHeader) {
160
+ public async startNewCheckpoint(
161
+ checkpointIndex: number,
162
+ constants: CheckpointConstantData,
163
+ l1ToL2Messages: Fr[],
164
+ totalNumBlocks: number,
165
+ headerOfLastBlockInPreviousCheckpoint: BlockHeader,
166
+ ) {
144
167
  if (!this.provingState) {
145
- throw new Error(`Invalid proving state, call startNewEpoch before starting a block`);
168
+ throw new Error('Empty epoch proving state. Call startNewEpoch before starting a checkpoint.');
146
169
  }
147
170
 
148
- if (!this.provingState?.isAcceptingBlocks()) {
149
- throw new Error(`Epoch not accepting further blocks`);
171
+ if (!this.provingState.isAcceptingCheckpoints()) {
172
+ throw new Error(`Epoch not accepting further checkpoints.`);
150
173
  }
151
174
 
152
- logger.info(`Starting block ${globalVariables.blockNumber} for slot ${globalVariables.slotNumber.toNumber()}`);
175
+ // Fork world state at the end of the immediately previous block.
176
+ const lastBlockNumber = headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber;
177
+ const db = await this.dbProvider.fork(lastBlockNumber);
153
178
 
154
- // Fork world state at the end of the immediately previous block
155
- const db = await this.dbProvider.fork(globalVariables.blockNumber - 1);
156
- this.dbs.set(globalVariables.blockNumber, db);
179
+ const firstBlockNumber = BlockNumber(lastBlockNumber + 1);
180
+ this.dbs.set(firstBlockNumber, db);
157
181
 
158
- // we start the block by enqueueing all of the base parity circuits
159
- const {
160
- l1ToL2MessageTreeSnapshot,
161
- l1ToL2MessageSubtreeSiblingPath,
162
- l1ToL2MessageTreeSnapshotAfterInsertion,
163
- baseParityInputs,
164
- } = await this.prepareBaseParityInputs(l1ToL2Messages, db);
165
-
166
- // Get archive snapshot before this block lands
167
- const lastArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
182
+ // Get archive sibling path before any block in this checkpoint lands.
168
183
  const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db);
169
- const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
170
184
 
171
- const blockProvingState = this.provingState!.startNewBlock(
172
- globalVariables,
185
+ // Insert all the l1 to l2 messages into the db. And get the states before and after the insertion.
186
+ const {
187
+ lastL1ToL2MessageTreeSnapshot,
188
+ lastL1ToL2MessageSubtreeRootSiblingPath,
189
+ newL1ToL2MessageTreeSnapshot,
190
+ newL1ToL2MessageSubtreeRootSiblingPath,
191
+ } = await this.updateL1ToL2MessageTree(l1ToL2Messages, db);
192
+
193
+ this.provingState.startNewCheckpoint(
194
+ checkpointIndex,
195
+ constants,
196
+ totalNumBlocks,
197
+ headerOfLastBlockInPreviousCheckpoint,
198
+ lastArchiveSiblingPath,
173
199
  l1ToL2Messages,
174
- l1ToL2MessageTreeSnapshot,
175
- l1ToL2MessageSubtreeSiblingPath,
176
- l1ToL2MessageTreeSnapshotAfterInsertion,
177
- lastArchive,
200
+ lastL1ToL2MessageTreeSnapshot,
201
+ lastL1ToL2MessageSubtreeRootSiblingPath,
202
+ newL1ToL2MessageTreeSnapshot,
203
+ newL1ToL2MessageSubtreeRootSiblingPath,
204
+ );
205
+ }
206
+
207
+ /**
208
+ * Starts off a new block
209
+ * @param blockNumber - The block number
210
+ * @param timestamp - The timestamp of the block. This is only required for constructing the private inputs for the
211
+ * block that doesn't have any txs.
212
+ * @param totalNumTxs - The total number of txs in the block
213
+ */
214
+ @trackSpan('ProvingOrchestrator.startNewBlock', blockNumber => ({
215
+ [Attributes.BLOCK_NUMBER]: blockNumber,
216
+ }))
217
+ public async startNewBlock(blockNumber: BlockNumber, timestamp: UInt64, totalNumTxs: number) {
218
+ if (!this.provingState) {
219
+ throw new Error('Empty epoch proving state. Call startNewEpoch before starting a block.');
220
+ }
221
+
222
+ const checkpointProvingState = this.provingState.getCheckpointProvingStateByBlockNumber(blockNumber);
223
+ if (!checkpointProvingState) {
224
+ throw new Error(`Checkpoint not started. Call startNewCheckpoint first.`);
225
+ }
226
+
227
+ if (!checkpointProvingState.isAcceptingBlocks()) {
228
+ throw new Error(`Checkpoint not accepting further blocks.`);
229
+ }
230
+
231
+ const constants = checkpointProvingState.constants;
232
+ this.logger.info(`Starting block ${blockNumber} for slot ${constants.slotNumber}.`);
233
+
234
+ // Fork the db only when it's not already set. The db for the first block is set in `startNewCheckpoint`.
235
+ if (!this.dbs.has(blockNumber)) {
236
+ // Fork world state at the end of the immediately previous block
237
+ const db = await this.dbProvider.fork(BlockNumber(blockNumber - 1));
238
+ this.dbs.set(blockNumber, db);
239
+ }
240
+ const db = this.getDbForBlock(blockNumber);
241
+
242
+ // Get archive snapshot and sibling path before any txs in this block lands.
243
+ const lastArchiveTreeSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
244
+ const lastArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
245
+
246
+ const blockProvingState = checkpointProvingState.startNewBlock(
247
+ blockNumber,
248
+ timestamp,
249
+ totalNumTxs,
250
+ lastArchiveTreeSnapshot,
178
251
  lastArchiveSiblingPath,
179
- newArchiveSiblingPath,
180
- previousBlockHeader,
181
- this.proverId,
182
252
  );
183
253
 
184
- // Enqueue base parity circuits for the block
185
- for (let i = 0; i < baseParityInputs.length; i++) {
186
- this.enqueueBaseParityCircuit(blockProvingState, baseParityInputs[i], i);
254
+ // Enqueue base parity circuits for the first block in the checkpoint.
255
+ if (blockProvingState.index === 0) {
256
+ for (let i = 0; i < NUM_BASE_PARITY_PER_ROOT_PARITY; i++) {
257
+ this.enqueueBaseParityCircuit(checkpointProvingState, blockProvingState, i);
258
+ }
259
+ }
260
+
261
+ // Because `addTxs` won't be called for a block without txs, and that's where the sponge blob state is computed.
262
+ // We need to set its end sponge blob here, which will become the start sponge blob for the next block.
263
+ if (totalNumTxs === 0) {
264
+ const endState = await db.getStateReference();
265
+ blockProvingState.setEndState(endState);
266
+
267
+ const endSpongeBlob = blockProvingState.getStartSpongeBlob().clone();
268
+ const blockEndBlobFields = blockProvingState.getBlockEndBlobFields();
269
+ await endSpongeBlob.absorb(blockEndBlobFields);
270
+ blockProvingState.setEndSpongeBlob(endSpongeBlob);
271
+
272
+ // Try to accumulate the out hashes and blobs as far as we can:
273
+ await this.provingState.accumulateCheckpointOutHashes();
274
+ await this.provingState.setBlobAccumulators();
187
275
  }
188
276
  }
189
277
 
@@ -195,28 +283,40 @@ export class ProvingOrchestrator implements EpochProver {
195
283
  [Attributes.BLOCK_TXS_COUNT]: txs.length,
196
284
  }))
197
285
  public async addTxs(txs: ProcessedTx[]): Promise<void> {
286
+ if (!this.provingState) {
287
+ throw new Error(`Empty epoch proving state. Call startNewEpoch before adding txs.`);
288
+ }
289
+
198
290
  if (!txs.length) {
199
291
  // To avoid an ugly throw below. If we require an empty block, we can just call setBlockCompleted
200
292
  // on a block with no txs. We cannot do that here because we cannot find the blockNumber without any txs.
201
- logger.warn(`Provided no txs to orchestrator addTxs.`);
293
+ this.logger.warn(`Provided no txs to orchestrator addTxs.`);
202
294
  return;
203
295
  }
204
- const blockNumber = txs[0].globalVariables.blockNumber;
205
- const provingState = this.provingState?.getBlockProvingStateByBlockNumber(blockNumber!);
296
+
297
+ const blockNumber = BlockNumber(txs[0].globalVariables.blockNumber);
298
+ const provingState = this.provingState.getBlockProvingStateByBlockNumber(blockNumber!);
206
299
  if (!provingState) {
207
- throw new Error(`Block proving state for ${blockNumber} not found`);
300
+ throw new Error(`Proving state for block ${blockNumber} not found. Call startNewBlock first.`);
208
301
  }
209
302
 
210
- if (provingState.totalNumTxs) {
303
+ if (provingState.totalNumTxs !== txs.length) {
304
+ throw new Error(
305
+ `Block ${blockNumber} should be filled with ${provingState.totalNumTxs} txs. Received ${txs.length} txs.`,
306
+ );
307
+ }
308
+
309
+ if (!provingState.isAcceptingTxs()) {
211
310
  throw new Error(`Block ${blockNumber} has been initialized with transactions.`);
212
311
  }
213
312
 
214
- const numBlobFields = toNumBlobFields(txs);
215
- provingState.startNewBlock(txs.length, numBlobFields);
313
+ this.logger.info(`Adding ${txs.length} transactions to block ${blockNumber}`);
314
+
315
+ const db = this.getDbForBlock(blockNumber);
316
+ const lastArchive = provingState.lastArchiveTreeSnapshot;
317
+ const newL1ToL2MessageTreeSnapshot = provingState.newL1ToL2MessageTreeSnapshot;
318
+ const spongeBlobState = provingState.getStartSpongeBlob().clone();
216
319
 
217
- logger.info(
218
- `Adding ${txs.length} transactions with ${numBlobFields} blob fields to block ${provingState.blockNumber}`,
219
- );
220
320
  for (const tx of txs) {
221
321
  try {
222
322
  if (!provingState.verifyState()) {
@@ -225,15 +325,32 @@ export class ProvingOrchestrator implements EpochProver {
225
325
 
226
326
  validateTx(tx);
227
327
 
228
- logger.info(`Received transaction: ${tx.hash}`);
328
+ this.logger.debug(`Received transaction: ${tx.hash}`);
329
+
330
+ const startSpongeBlob = spongeBlobState.clone();
331
+ const [hints, treeSnapshots] = await this.prepareBaseRollupInputs(
332
+ tx,
333
+ lastArchive,
334
+ newL1ToL2MessageTreeSnapshot,
335
+ startSpongeBlob,
336
+ db,
337
+ );
229
338
 
230
- const [hints, treeSnapshots] = await this.prepareTransaction(tx, provingState);
231
- const txProvingState = new TxProvingState(tx, hints, treeSnapshots);
339
+ if (!provingState.verifyState()) {
340
+ throw new Error(`Unable to add transaction, preparing base inputs failed`);
341
+ }
342
+
343
+ await spongeBlobState.absorb(tx.txEffect.toBlobFields());
344
+
345
+ const txProvingState = new TxProvingState(tx, hints, treeSnapshots, this.proverId.toField());
232
346
  const txIndex = provingState.addNewTx(txProvingState);
233
- this.getOrEnqueueTube(provingState, txIndex);
234
347
  if (txProvingState.requireAvmProof) {
235
- logger.debug(`Enqueueing public VM for tx ${txIndex}`);
348
+ this.getOrEnqueueChonkVerifier(provingState, txIndex);
349
+ this.logger.debug(`Enqueueing public VM for tx ${txIndex}`);
236
350
  this.enqueueVM(provingState, txIndex);
351
+ } else {
352
+ this.logger.debug(`Enqueueing base rollup for private-only tx ${txIndex}`);
353
+ this.enqueueBaseRollup(provingState, txIndex);
237
354
  }
238
355
  } catch (err: any) {
239
356
  throw new Error(`Error adding transaction ${tx.hash.toString()} to block ${blockNumber}: ${err.message}`, {
@@ -241,24 +358,45 @@ export class ProvingOrchestrator implements EpochProver {
241
358
  });
242
359
  }
243
360
  }
361
+
362
+ const endState = await db.getStateReference();
363
+ provingState.setEndState(endState);
364
+
365
+ const blockEndBlobFields = provingState.getBlockEndBlobFields();
366
+ await spongeBlobState.absorb(blockEndBlobFields);
367
+
368
+ provingState.setEndSpongeBlob(spongeBlobState);
369
+
370
+ // Txs have been added to the block. Now try to accumulate the out hashes and blobs as far as we can:
371
+ await this.provingState.accumulateCheckpointOutHashes();
372
+ await this.provingState.setBlobAccumulators();
244
373
  }
245
374
 
246
375
  /**
247
- * Kickstarts tube circuits for the specified txs. These will be used during epoch proving.
248
- * Note that if the tube circuits are not started this way, they will be started nontheless after processing.
376
+ * Kickstarts chonk verifier circuits for the specified txs. These will be used during epoch proving.
377
+ * Note that if the chonk verifier circuits are not started this way, they will be started nontheless after processing.
249
378
  */
250
- @trackSpan('ProvingOrchestrator.startTubeCircuits')
251
- public startTubeCircuits(txs: Tx[]) {
379
+ @trackSpan('ProvingOrchestrator.startChonkVerifierCircuits')
380
+ public startChonkVerifierCircuits(txs: Tx[]) {
252
381
  if (!this.provingState?.verifyState()) {
253
- throw new Error(`Invalid proving state, call startNewEpoch before starting tube circuits`);
382
+ throw new Error(`Empty epoch proving state. call startNewEpoch before starting chonk verifier circuits.`);
254
383
  }
255
- for (const tx of txs) {
384
+ const publicTxs = txs.filter(tx => tx.data.forPublic);
385
+ for (const tx of publicTxs) {
256
386
  const txHash = tx.getTxHash().toString();
257
- const tubeInputs = new TubeInputs(!!tx.data.forPublic, tx.clientIvcProof);
258
- const tubeProof = promiseWithResolvers<ProofAndVerificationKey<typeof TUBE_PROOF_LENGTH>>();
259
- logger.debug(`Starting tube circuit for tx ${txHash}`);
260
- this.doEnqueueTube(txHash, tubeInputs, proof => tubeProof.resolve(proof));
261
- this.provingState?.cachedTubeProofs.set(txHash, tubeProof.promise);
387
+ const privateInputs = getPublicChonkVerifierPrivateInputsFromTx(tx, this.proverId.toField());
388
+ const tubeProof =
389
+ promiseWithResolvers<
390
+ PublicInputsAndRecursiveProof<
391
+ PublicChonkVerifierPublicInputs,
392
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
393
+ >
394
+ >();
395
+ this.logger.debug(`Starting chonk verifier circuit for tx ${txHash}`);
396
+ this.doEnqueueChonkVerifier(txHash, privateInputs, proof => {
397
+ tubeProof.resolve(proof);
398
+ });
399
+ this.provingState.cachedChonkVerifierProofs.set(txHash, tubeProof.promise);
262
400
  }
263
401
  return Promise.resolve();
264
402
  }
@@ -267,109 +405,145 @@ export class ProvingOrchestrator implements EpochProver {
267
405
  * Marks the block as completed.
268
406
  * Computes the block header and updates the archive tree.
269
407
  */
270
- @trackSpan('ProvingOrchestrator.setBlockCompleted', (blockNumber: number) => ({
408
+ @trackSpan('ProvingOrchestrator.setBlockCompleted', (blockNumber: BlockNumber) => ({
271
409
  [Attributes.BLOCK_NUMBER]: blockNumber,
272
410
  }))
273
- public async setBlockCompleted(blockNumber: number, expectedHeader?: BlockHeader): Promise<L2Block> {
411
+ public async setBlockCompleted(blockNumber: BlockNumber, expectedHeader?: BlockHeader): Promise<BlockHeader> {
274
412
  const provingState = this.provingState?.getBlockProvingStateByBlockNumber(blockNumber);
275
413
  if (!provingState) {
276
414
  throw new Error(`Block proving state for ${blockNumber} not found`);
277
415
  }
278
416
 
279
- if (!provingState.spongeBlobState) {
280
- // If we are completing an empty block, initialize the provingState.
281
- // We will have 0 txs and no blob fields.
282
- provingState.startNewBlock(0, 0);
417
+ // Abort with specific error for the block if there's one.
418
+ const error = provingState.getError();
419
+ if (error) {
420
+ throw new Error(`Block proving failed: ${error}`);
283
421
  }
284
422
 
423
+ // Abort if the proving state is not valid due to errors occurred elsewhere.
285
424
  if (!provingState.verifyState()) {
286
- throw new Error(`Block proving failed: ${provingState.error}`);
425
+ throw new Error(`Invalid proving state when completing block ${blockNumber}.`);
287
426
  }
288
427
 
289
- // And build the block header
290
- logger.verbose(`Block ${blockNumber} completed. Assembling header.`);
291
- await this.buildBlock(provingState, expectedHeader);
292
-
293
- logger.debug(`Accumulating blobs for ${blockNumber}`);
294
- await this.provingState?.setBlobAccumulators(blockNumber);
295
-
296
- // If the proofs were faster than the block building, then we need to try the block root rollup again here
297
- await this.checkAndEnqueueBlockRootRollup(provingState);
298
- return provingState.block!;
299
- }
300
-
301
- /** Returns the block as built for a given index. */
302
- public getBlock(index: number): L2Block {
303
- const block = this.provingState?.blocks[index]?.block;
304
- if (!block) {
305
- throw new Error(`Block at index ${index} not available`);
428
+ if (provingState.isAcceptingTxs()) {
429
+ throw new Error(
430
+ `Block ${blockNumber} is still accepting txs. Call setBlockCompleted after all txs have been added.`,
431
+ );
306
432
  }
307
- return block;
308
- }
309
-
310
- private async buildBlock(provingState: BlockProvingState, expectedHeader?: BlockHeader) {
311
- // Collect all new nullifiers, commitments, and contracts from all txs in this block to build body
312
- const txs = provingState.allTxs.map(a => a.processedTx);
313
433
 
314
- // Get db for this block
315
- const db = this.dbs.get(provingState.blockNumber)!;
316
-
317
- // Given we've applied every change from this block, now assemble the block header
318
- // and update the archive tree, so we're ready to start processing the next block
319
- const { header, body } = await buildHeaderAndBodyFromTxs(
320
- txs,
321
- provingState.globalVariables,
322
- provingState.newL1ToL2Messages,
323
- db,
324
- );
434
+ // Given we've applied every change from this block, now assemble the block header:
435
+ this.logger.verbose(`Block ${blockNumber} completed. Assembling header.`);
436
+ const header = await provingState.buildBlockHeader();
325
437
 
326
438
  if (expectedHeader && !header.equals(expectedHeader)) {
327
- logger.error(`Block header mismatch: header=${header} expectedHeader=${expectedHeader}`);
439
+ this.logger.error(`Block header mismatch: header=${header} expectedHeader=${expectedHeader}`);
328
440
  throw new Error('Block header mismatch');
329
441
  }
330
442
 
331
- logger.verbose(
332
- `Updating archive tree with block ${provingState.blockNumber} header ${(await header.hash()).toString()}`,
333
- );
334
- await db.updateArchive(header);
443
+ // Get db for this block and remove from map — no other code should use it after this point.
444
+ const db = this.getDbForBlock(provingState.blockNumber);
445
+ this.dbs.delete(provingState.blockNumber);
335
446
 
336
- // Assemble the L2 block
337
- const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
338
- const l2Block = new L2Block(newArchive, header, body);
447
+ // Update the archive tree, capture the snapshot, and close the fork deterministically.
448
+ try {
449
+ this.logger.verbose(
450
+ `Updating archive tree with block ${provingState.blockNumber} header ${(await header.hash()).toString()}`,
451
+ );
452
+ await db.updateArchive(header);
453
+ provingState.setBuiltArchive(await getTreeSnapshot(MerkleTreeId.ARCHIVE, db));
454
+ } finally {
455
+ await db.close();
456
+ }
339
457
 
340
- await this.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive);
458
+ await this.verifyBuiltBlockAgainstSyncedState(provingState);
341
459
 
342
- logger.verbose(`Orchestrator finalized block ${l2Block.number}`);
343
- provingState.setBlock(l2Block);
460
+ return header;
344
461
  }
345
462
 
346
463
  // Flagged as protected to disable in certain unit tests
347
- protected async verifyBuiltBlockAgainstSyncedState(l2Block: L2Block, newArchive: AppendOnlyTreeSnapshot) {
348
- const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(l2Block.number));
464
+ protected async verifyBuiltBlockAgainstSyncedState(provingState: BlockProvingState) {
465
+ const builtBlockHeader = provingState.getBuiltBlockHeader();
466
+ if (!builtBlockHeader) {
467
+ this.logger.debug('Block header not built yet, skipping header check.');
468
+ return;
469
+ }
470
+
471
+ const output = provingState.getBlockRootRollupOutput();
472
+ if (!output) {
473
+ this.logger.debug('Block root rollup proof not built yet, skipping header check.');
474
+ return;
475
+ }
476
+
477
+ const newArchive = provingState.getBuiltArchive();
478
+ if (!newArchive) {
479
+ this.logger.debug('Archive snapshot not yet captured, skipping header check.');
480
+ return;
481
+ }
482
+
483
+ const header = await buildHeaderFromCircuitOutputs(output);
484
+
485
+ if (!(await header.hash()).equals(await builtBlockHeader.hash())) {
486
+ this.logger.error(`Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(builtBlockHeader)}`);
487
+ provingState.reject(`Block header hash mismatch.`);
488
+ return;
489
+ }
490
+
491
+ const blockNumber = provingState.blockNumber;
492
+ const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(blockNumber));
349
493
  if (!syncedArchive.equals(newArchive)) {
350
- throw new Error(
351
- `Archive tree mismatch for block ${l2Block.number}: world state synced to ${inspect(
494
+ this.logger.error(
495
+ `Archive tree mismatch for block ${blockNumber}: world state synced to ${inspect(
352
496
  syncedArchive,
353
497
  )} but built ${inspect(newArchive)}`,
354
498
  );
499
+ provingState.reject(`Archive tree mismatch.`);
500
+ return;
501
+ }
502
+
503
+ const circuitArchive = output.newArchive;
504
+ if (!newArchive.equals(circuitArchive)) {
505
+ this.logger.error(`New archive mismatch.\nCircuit: ${output.newArchive}\nComputed: ${newArchive}`);
506
+ provingState.reject(`New archive mismatch.`);
507
+ return;
355
508
  }
356
509
  }
357
510
 
358
511
  /**
359
- * Cancel any further proving
512
+ * Cancel any further proving.
513
+ * If cancelJobsOnStop is true, aborts all pending jobs with the broker (which marks them as 'Aborted').
514
+ * If cancelJobsOnStop is false (default), jobs remain in the broker queue and can be reused on restart/reorg.
360
515
  */
361
516
  public cancel() {
362
- for (const controller of this.pendingProvingJobs) {
363
- controller.abort();
517
+ if (this.cancelJobsOnStop) {
518
+ for (const controller of this.pendingProvingJobs) {
519
+ controller.abort();
520
+ }
364
521
  }
365
522
 
366
523
  this.provingState?.cancel();
524
+
525
+ for (const [blockNumber, db] of this.dbs.entries()) {
526
+ void db.close().catch(err => this.logger.error(`Error closing db for block ${blockNumber}`, err));
527
+ }
528
+ this.dbs.clear();
529
+ }
530
+
531
+ private getDbForBlock(blockNumber: BlockNumber): MerkleTreeWriteOperations {
532
+ const db = this.dbs.get(blockNumber);
533
+ if (!db) {
534
+ throw new Error(`World state fork for block ${blockNumber} not found.`);
535
+ }
536
+ return db;
367
537
  }
368
538
 
369
539
  /**
370
540
  * Returns the proof for the current epoch.
371
541
  */
372
- public async finalizeEpoch() {
542
+ public async finalizeEpoch(): Promise<{
543
+ publicInputs: RootRollupPublicInputs;
544
+ proof: Proof;
545
+ batchedBlobInputs: BatchedBlob;
546
+ }> {
373
547
  if (!this.provingState || !this.provingPromise) {
374
548
  throw new Error(`Invalid proving state, an epoch must be proven before it can be finalized`);
375
549
  }
@@ -379,14 +553,7 @@ export class ProvingOrchestrator implements EpochProver {
379
553
  throw new Error(`Epoch proving failed: ${result.reason}`);
380
554
  }
381
555
 
382
- // TODO(MW): Move this? Requires async and don't want to force root methods to be async
383
- // TODO(MW): EpochProvingState uses this.blocks.filter(b => !!b).length as total blocks, use this below:
384
- const finalBlock = this.provingState.blocks[this.provingState.totalNumBlocks - 1];
385
- if (!finalBlock || !finalBlock.endBlobAccumulator) {
386
- throw new Error(`Epoch's final block not ready for finalize`);
387
- }
388
- const finalBatchedBlob = await finalBlock.endBlobAccumulator.finalize();
389
- this.provingState.setFinalBatchedBlob(finalBatchedBlob);
556
+ await this.provingState.finalizeBatchedBlob();
390
557
 
391
558
  const epochProofResult = this.provingState.getEpochProofResult();
392
559
 
@@ -398,20 +565,6 @@ export class ProvingOrchestrator implements EpochProver {
398
565
  return epochProofResult;
399
566
  }
400
567
 
401
- /**
402
- * Starts the proving process for the given transaction and adds it to our state
403
- * @param tx - The transaction whose proving we wish to commence
404
- * @param provingState - The proving state being worked on
405
- */
406
- private async prepareTransaction(tx: ProcessedTx, provingState: BlockProvingState) {
407
- const txInputs = await this.prepareBaseRollupInputs(provingState, tx);
408
- if (!txInputs) {
409
- // This should not be possible
410
- throw new Error(`Unable to add transaction, preparing base inputs failed`);
411
- }
412
- return txInputs;
413
- }
414
-
415
568
  /**
416
569
  * Enqueue a job to be scheduled
417
570
  * @param provingState - The proving state object being operated on
@@ -419,12 +572,12 @@ export class ProvingOrchestrator implements EpochProver {
419
572
  * @param job - The actual job, returns a promise notifying of the job's completion
420
573
  */
421
574
  private deferredProving<T>(
422
- provingState: EpochProvingState | BlockProvingState | undefined,
575
+ provingState: EpochProvingState | CheckpointProvingState | BlockProvingState,
423
576
  request: (signal: AbortSignal) => Promise<T>,
424
577
  callback: (result: T) => void | Promise<void>,
425
578
  ) {
426
- if (!provingState?.verifyState()) {
427
- logger.debug(`Not enqueuing job, state no longer valid`);
579
+ if (!provingState.verifyState()) {
580
+ this.logger.debug(`Not enqueuing job, state no longer valid`);
428
581
  return;
429
582
  }
430
583
 
@@ -441,8 +594,8 @@ export class ProvingOrchestrator implements EpochProver {
441
594
  }
442
595
 
443
596
  const result = await request(controller.signal);
444
- if (!provingState?.verifyState()) {
445
- logger.debug(`State no longer valid, discarding result`);
597
+ if (!provingState.verifyState()) {
598
+ this.logger.debug(`State no longer valid, discarding result`);
446
599
  return;
447
600
  }
448
601
 
@@ -460,7 +613,7 @@ export class ProvingOrchestrator implements EpochProver {
460
613
  return;
461
614
  }
462
615
 
463
- logger.error(`Error thrown when proving job`, err);
616
+ this.logger.error(`Error thrown when proving job`, err);
464
617
  provingState!.reject(`${err}`);
465
618
  } finally {
466
619
  const index = this.pendingProvingJobs.indexOf(controller);
@@ -474,60 +627,58 @@ export class ProvingOrchestrator implements EpochProver {
474
627
  setImmediate(() => void safeJob());
475
628
  }
476
629
 
477
- private async prepareBaseParityInputs(l1ToL2Messages: Fr[], db: MerkleTreeWriteOperations) {
478
- const l1ToL2MessagesPadded = padArrayEnd(
630
+ private async updateL1ToL2MessageTree(l1ToL2Messages: Fr[], db: MerkleTreeWriteOperations) {
631
+ const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
479
632
  l1ToL2Messages,
480
633
  Fr.ZERO,
481
634
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
482
635
  'Too many L1 to L2 messages',
483
636
  );
484
- const baseParityInputs = times(NUM_BASE_PARITY_PER_ROOT_PARITY, i =>
485
- BaseParityInputs.fromSlice(l1ToL2MessagesPadded, i, getVKTreeRoot()),
486
- );
487
637
 
488
- const l1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
489
-
490
- const l1ToL2MessageSubtreeSiblingPath = assertLength(
638
+ const lastL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
639
+ const lastL1ToL2MessageSubtreeRootSiblingPath = assertLength(
491
640
  await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db),
492
- L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
641
+ L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
493
642
  );
494
643
 
495
644
  // Update the local trees to include the new l1 to l2 messages
496
645
  await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
497
- const l1ToL2MessageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
646
+
647
+ const newL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
648
+ const newL1ToL2MessageSubtreeRootSiblingPath = assertLength(
649
+ await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db),
650
+ L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
651
+ );
498
652
 
499
653
  return {
500
- l1ToL2MessageTreeSnapshot,
501
- l1ToL2MessageSubtreeSiblingPath,
502
- l1ToL2MessageTreeSnapshotAfterInsertion,
503
- baseParityInputs,
654
+ lastL1ToL2MessageTreeSnapshot,
655
+ lastL1ToL2MessageSubtreeRootSiblingPath,
656
+ newL1ToL2MessageTreeSnapshot,
657
+ newL1ToL2MessageSubtreeRootSiblingPath,
504
658
  };
505
659
  }
506
660
 
507
661
  // Updates the merkle trees for a transaction. The first enqueued job for a transaction
508
- @trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', (_, tx) => ({
662
+ @trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', tx => ({
509
663
  [Attributes.TX_HASH]: tx.hash.toString(),
510
664
  }))
511
665
  private async prepareBaseRollupInputs(
512
- provingState: BlockProvingState,
513
666
  tx: ProcessedTx,
514
- ): Promise<[BaseRollupHints, TreeSnapshots] | undefined> {
515
- if (!provingState.verifyState() || !provingState.spongeBlobState) {
516
- logger.debug('Not preparing base rollup inputs, state invalid');
517
- return;
518
- }
519
-
520
- const db = this.dbs.get(provingState.blockNumber)!;
521
-
667
+ lastArchive: AppendOnlyTreeSnapshot,
668
+ newL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot,
669
+ startSpongeBlob: SpongeBlob,
670
+ db: MerkleTreeWriteOperations,
671
+ ): Promise<[BaseRollupHints, TreeSnapshots]> {
522
672
  // We build the base rollup inputs using a mock proof and verification key.
523
- // These will be overwritten later once we have proven the tube circuit and any public kernels
673
+ // These will be overwritten later once we have proven the chonk verifier circuit and any public kernels
524
674
  const [ms, hints] = await elapsed(
525
675
  insertSideEffectsAndBuildBaseRollupHints(
526
676
  tx,
527
- provingState.globalVariables,
528
- provingState.l1ToL2MessageTreeSnapshotAfterInsertion,
677
+ lastArchive,
678
+ newL1ToL2MessageTreeSnapshot,
679
+ startSpongeBlob,
680
+ this.proverId.toField(),
529
681
  db,
530
- provingState.spongeBlobState,
531
682
  ),
532
683
  );
533
684
 
@@ -540,10 +691,6 @@ export class ProvingOrchestrator implements EpochProver {
540
691
  );
541
692
  const treeSnapshots: TreeSnapshots = new Map((await Promise.all(promises)).map(obj => [obj.key, obj.value]));
542
693
 
543
- if (!provingState.verifyState()) {
544
- logger.debug(`Discarding proving job, state no longer valid`);
545
- return;
546
- }
547
694
  return [hints, treeSnapshots];
548
695
  }
549
696
 
@@ -551,7 +698,12 @@ export class ProvingOrchestrator implements EpochProver {
551
698
  // Executes the next level of merge if all inputs are available
552
699
  private enqueueBaseRollup(provingState: BlockProvingState, txIndex: number) {
553
700
  if (!provingState.verifyState()) {
554
- logger.debug('Not running base rollup, state invalid');
701
+ this.logger.debug('Not running base rollup, state invalid');
702
+ return;
703
+ }
704
+
705
+ if (!provingState.tryStartProvingBase(txIndex)) {
706
+ this.logger.debug(`Base rollup for tx ${txIndex} already started.`);
555
707
  return;
556
708
  }
557
709
 
@@ -559,76 +711,88 @@ export class ProvingOrchestrator implements EpochProver {
559
711
  const { processedTx } = txProvingState;
560
712
  const { rollupType, inputs } = txProvingState.getBaseRollupTypeAndInputs();
561
713
 
562
- logger.debug(`Enqueuing deferred proving base rollup for ${processedTx.hash.toString()}`);
714
+ this.logger.debug(`Enqueuing deferred proving base rollup for ${processedTx.hash.toString()}`);
563
715
 
564
716
  this.deferredProving(
565
717
  provingState,
566
718
  wrapCallbackInSpan(
567
719
  this.tracer,
568
720
  `ProvingOrchestrator.prover.${
569
- inputs instanceof PrivateBaseRollupInputs ? 'getPrivateBaseRollupProof' : 'getPublicBaseRollupProof'
721
+ inputs instanceof PrivateTxBaseRollupPrivateInputs
722
+ ? 'getPrivateTxBaseRollupProof'
723
+ : 'getPublicTxBaseRollupProof'
570
724
  }`,
571
725
  {
572
726
  [Attributes.TX_HASH]: processedTx.hash.toString(),
573
727
  [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType,
574
728
  },
575
729
  signal => {
576
- if (inputs instanceof PrivateBaseRollupInputs) {
577
- return this.prover.getPrivateBaseRollupProof(inputs, signal, provingState.epochNumber);
730
+ if (inputs instanceof PrivateTxBaseRollupPrivateInputs) {
731
+ return this.prover.getPrivateTxBaseRollupProof(inputs, signal, provingState.epochNumber);
578
732
  } else {
579
- return this.prover.getPublicBaseRollupProof(inputs, signal, provingState.epochNumber);
733
+ return this.prover.getPublicTxBaseRollupProof(inputs, signal, provingState.epochNumber);
580
734
  }
581
735
  },
582
736
  ),
583
- async result => {
584
- logger.debug(`Completed proof for ${rollupType} for tx ${processedTx.hash.toString()}`);
585
- validatePartialState(result.inputs.end, txProvingState.treeSnapshots);
737
+ result => {
738
+ this.logger.debug(`Completed proof for ${rollupType} for tx ${processedTx.hash.toString()}`);
739
+ validatePartialState(result.inputs.endTreeSnapshots, txProvingState.treeSnapshots);
586
740
  const leafLocation = provingState.setBaseRollupProof(txIndex, result);
587
741
  if (provingState.totalNumTxs === 1) {
588
- await this.checkAndEnqueueBlockRootRollup(provingState);
742
+ this.checkAndEnqueueBlockRootRollup(provingState);
589
743
  } else {
590
- await this.checkAndEnqueueNextMergeRollup(provingState, leafLocation);
744
+ this.checkAndEnqueueNextMergeRollup(provingState, leafLocation);
591
745
  }
592
746
  },
593
747
  );
594
748
  }
595
749
 
596
- // Enqueues the tube circuit for a given transaction index, or reuses the one already enqueued
597
- // Once completed, will enqueue the next circuit, either a public kernel or the base rollup
598
- private getOrEnqueueTube(provingState: BlockProvingState, txIndex: number) {
750
+ // Enqueues the public chonk verifier circuit for a given transaction index, or reuses the one already enqueued.
751
+ // Once completed, will enqueue the the public tx base rollup.
752
+ private getOrEnqueueChonkVerifier(provingState: BlockProvingState, txIndex: number) {
599
753
  if (!provingState.verifyState()) {
600
- logger.debug('Not running tube circuit, state invalid');
754
+ this.logger.debug('Not running chonk verifier circuit, state invalid');
601
755
  return;
602
756
  }
603
757
 
604
758
  const txProvingState = provingState.getTxProvingState(txIndex);
605
759
  const txHash = txProvingState.processedTx.hash.toString();
606
-
607
- const handleResult = (result: ProofAndVerificationKey<typeof TUBE_PROOF_LENGTH>) => {
608
- logger.debug(`Got tube proof for tx index: ${txIndex}`, { txHash });
609
- txProvingState.setTubeProof(result);
610
- this.provingState?.cachedTubeProofs.delete(txHash);
611
- this.checkAndEnqueueNextTxCircuit(provingState, txIndex);
760
+ NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH;
761
+ const handleResult = (
762
+ result: PublicInputsAndRecursiveProof<
763
+ PublicChonkVerifierPublicInputs,
764
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
765
+ >,
766
+ ) => {
767
+ this.logger.debug(`Got chonk verifier proof for tx index: ${txIndex}`, { txHash });
768
+ txProvingState.setPublicChonkVerifierProof(result);
769
+ this.provingState?.cachedChonkVerifierProofs.delete(txHash);
770
+ this.checkAndEnqueueBaseRollup(provingState, txIndex);
612
771
  };
613
772
 
614
- if (this.provingState?.cachedTubeProofs.has(txHash)) {
615
- logger.debug(`Tube proof already enqueued for tx index: ${txIndex}`, { txHash });
616
- void this.provingState!.cachedTubeProofs.get(txHash)!.then(handleResult);
773
+ if (this.provingState?.cachedChonkVerifierProofs.has(txHash)) {
774
+ this.logger.debug(`Chonk verifier proof already enqueued for tx index: ${txIndex}`, { txHash });
775
+ void this.provingState!.cachedChonkVerifierProofs.get(txHash)!.then(handleResult);
617
776
  return;
618
777
  }
619
778
 
620
- logger.debug(`Enqueuing tube circuit for tx index: ${txIndex}`);
621
- this.doEnqueueTube(txHash, txProvingState.getTubeInputs(), handleResult);
779
+ this.logger.debug(`Enqueuing chonk verifier circuit for tx index: ${txIndex}`);
780
+ this.doEnqueueChonkVerifier(txHash, txProvingState.getPublicChonkVerifierPrivateInputs(), handleResult);
622
781
  }
623
782
 
624
- private doEnqueueTube(
783
+ private doEnqueueChonkVerifier(
625
784
  txHash: string,
626
- inputs: TubeInputs,
627
- handler: (result: ProofAndVerificationKey<typeof TUBE_PROOF_LENGTH>) => void,
785
+ inputs: PublicChonkVerifierPrivateInputs,
786
+ handler: (
787
+ result: PublicInputsAndRecursiveProof<
788
+ PublicChonkVerifierPublicInputs,
789
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
790
+ >,
791
+ ) => void,
628
792
  provingState: EpochProvingState | BlockProvingState = this.provingState!,
629
793
  ) {
630
- if (!provingState?.verifyState()) {
631
- logger.debug('Not running tube circuit, state invalid');
794
+ if (!provingState.verifyState()) {
795
+ this.logger.debug('Not running chonk verifier circuit, state invalid');
632
796
  return;
633
797
  }
634
798
 
@@ -636,12 +800,12 @@ export class ProvingOrchestrator implements EpochProver {
636
800
  provingState,
637
801
  wrapCallbackInSpan(
638
802
  this.tracer,
639
- 'ProvingOrchestrator.prover.getTubeProof',
803
+ 'ProvingOrchestrator.prover.getPublicChonkVerifierProof',
640
804
  {
641
805
  [Attributes.TX_HASH]: txHash,
642
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-circuit' satisfies CircuitName,
806
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'chonk-verifier-public' satisfies CircuitName,
643
807
  },
644
- signal => this.prover.getTubeProof(inputs, signal, this.provingState!.epochNumber),
808
+ signal => this.prover.getPublicChonkVerifierProof(inputs, signal, provingState.epochNumber),
645
809
  ),
646
810
  handler,
647
811
  );
@@ -651,7 +815,12 @@ export class ProvingOrchestrator implements EpochProver {
651
815
  // Enqueues the next level of merge if all inputs are available
652
816
  private enqueueMergeRollup(provingState: BlockProvingState, location: TreeNodeLocation) {
653
817
  if (!provingState.verifyState()) {
654
- logger.debug('Not running merge rollup. State no longer valid.');
818
+ this.logger.debug('Not running merge rollup. State no longer valid.');
819
+ return;
820
+ }
821
+
822
+ if (!provingState.tryStartProvingMerge(location)) {
823
+ this.logger.debug('Merge rollup already started.');
655
824
  return;
656
825
  }
657
826
 
@@ -661,33 +830,34 @@ export class ProvingOrchestrator implements EpochProver {
661
830
  provingState,
662
831
  wrapCallbackInSpan(
663
832
  this.tracer,
664
- 'ProvingOrchestrator.prover.getMergeRollupProof',
833
+ 'ProvingOrchestrator.prover.getTxMergeRollupProof',
665
834
  {
666
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' satisfies CircuitName,
835
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-tx-merge' satisfies CircuitName,
667
836
  },
668
- signal => this.prover.getMergeRollupProof(inputs, signal, provingState.epochNumber),
837
+ signal => this.prover.getTxMergeRollupProof(inputs, signal, provingState.epochNumber),
669
838
  ),
670
- async result => {
839
+ result => {
671
840
  provingState.setMergeRollupProof(location, result);
672
- await this.checkAndEnqueueNextMergeRollup(provingState, location);
841
+ this.checkAndEnqueueNextMergeRollup(provingState, location);
673
842
  },
674
843
  );
675
844
  }
676
845
 
677
846
  // Executes the block root rollup circuit
678
- private async enqueueBlockRootRollup(provingState: BlockProvingState) {
847
+ private enqueueBlockRootRollup(provingState: BlockProvingState) {
679
848
  if (!provingState.verifyState()) {
680
- logger.debug('Not running block root rollup, state no longer valid');
849
+ this.logger.debug('Not running block root rollup, state no longer valid');
681
850
  return;
682
851
  }
683
852
 
684
- provingState.blockRootRollupStarted = true;
853
+ if (!provingState.tryStartProvingBlockRoot()) {
854
+ this.logger.debug('Block root rollup already started.');
855
+ return;
856
+ }
685
857
 
686
- const { rollupType, inputs } = await provingState.getBlockRootRollupTypeAndInputs();
858
+ const { rollupType, inputs } = provingState.getBlockRootRollupTypeAndInputs();
687
859
 
688
- logger.debug(
689
- `Enqueuing ${rollupType} for block ${provingState.blockNumber} with ${provingState.newL1ToL2Messages.length} l1 to l2 msgs.`,
690
- );
860
+ this.logger.debug(`Enqueuing ${rollupType} for block ${provingState.blockNumber}.`);
691
861
 
692
862
  this.deferredProving(
693
863
  provingState,
@@ -698,56 +868,33 @@ export class ProvingOrchestrator implements EpochProver {
698
868
  [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType,
699
869
  },
700
870
  signal => {
701
- if (inputs instanceof EmptyBlockRootRollupInputs) {
702
- return this.prover.getEmptyBlockRootRollupProof(inputs, signal, provingState.epochNumber);
703
- } else if (inputs instanceof SingleTxBlockRootRollupInputs) {
704
- return this.prover.getSingleTxBlockRootRollupProof(inputs, signal, provingState.epochNumber);
871
+ if (inputs instanceof BlockRootFirstRollupPrivateInputs) {
872
+ return this.prover.getBlockRootFirstRollupProof(inputs, signal, provingState.epochNumber);
873
+ } else if (inputs instanceof BlockRootSingleTxFirstRollupPrivateInputs) {
874
+ return this.prover.getBlockRootSingleTxFirstRollupProof(inputs, signal, provingState.epochNumber);
875
+ } else if (inputs instanceof BlockRootEmptyTxFirstRollupPrivateInputs) {
876
+ return this.prover.getBlockRootEmptyTxFirstRollupProof(inputs, signal, provingState.epochNumber);
877
+ } else if (inputs instanceof BlockRootSingleTxRollupPrivateInputs) {
878
+ return this.prover.getBlockRootSingleTxRollupProof(inputs, signal, provingState.epochNumber);
705
879
  } else {
706
880
  return this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber);
707
881
  }
708
882
  },
709
883
  ),
710
884
  async result => {
711
- provingState.setBlockRootRollupProof(result);
712
- const header = await provingState.buildHeaderFromProvingOutputs();
713
- if (!(await header.hash()).equals(await provingState.block!.header.hash())) {
714
- logger.error(
715
- `Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(provingState.block!.header)}`,
716
- );
717
- provingState.reject(`Block header hash mismatch.`);
718
- }
885
+ this.logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}`);
719
886
 
720
- const dbArchiveRoot = provingState.block!.archive.root;
721
- const circuitArchiveRoot = result.inputs.newArchive.root;
722
- if (!dbArchiveRoot.equals(circuitArchiveRoot)) {
723
- logger.error(
724
- `New archive root mismatch.\nCircuit: ${result.inputs.newArchive.root}\nComputed: ${dbArchiveRoot}`,
725
- );
726
- provingState.reject(`New archive root mismatch.`);
727
- }
887
+ const leafLocation = provingState.setBlockRootRollupProof(result);
888
+ const checkpointProvingState = provingState.parentCheckpoint;
728
889
 
729
- const endBlobAccumulatorPublicInputs = BlobAccumulatorPublicInputs.fromBatchedBlobAccumulator(
730
- provingState.endBlobAccumulator!,
731
- );
732
- const circuitEndBlobAccumulatorState = result.inputs.blobPublicInputs.endBlobAccumulator;
733
- if (!circuitEndBlobAccumulatorState.equals(endBlobAccumulatorPublicInputs)) {
734
- logger.error(
735
- `Blob accumulator state mismatch.\nCircuit: ${inspect(circuitEndBlobAccumulatorState)}\nComputed: ${inspect(
736
- endBlobAccumulatorPublicInputs,
737
- )}`,
738
- );
739
- provingState.reject(`Blob accumulator state mismatch.`);
740
- }
890
+ // Verification is called from both here and setBlockCompleted. Whichever runs last
891
+ // will be the first to see all three pieces (header, proof output, archive) and run the checks.
892
+ await this.verifyBuiltBlockAgainstSyncedState(provingState);
741
893
 
742
- logger.debug(`Completed ${rollupType} proof for block ${provingState.block!.number}`);
743
- // validatePartialState(result.inputs.end, tx.treeSnapshots); // TODO(palla/prover)
744
-
745
- const epochProvingState = this.provingState!;
746
- const leafLocation = epochProvingState.setBlockRootRollupProof(provingState.index, result);
747
- if (epochProvingState.totalNumBlocks === 1) {
748
- this.enqueueEpochPadding(epochProvingState);
894
+ if (checkpointProvingState.totalNumBlocks === 1) {
895
+ await this.checkAndEnqueueCheckpointRootRollup(checkpointProvingState);
749
896
  } else {
750
- this.checkAndEnqueueNextBlockMergeRollup(epochProvingState, leafLocation);
897
+ await this.checkAndEnqueueNextBlockMergeRollup(checkpointProvingState, leafLocation);
751
898
  }
752
899
  },
753
900
  );
@@ -755,24 +902,35 @@ export class ProvingOrchestrator implements EpochProver {
755
902
 
756
903
  // Executes the base parity circuit and stores the intermediate state for the root parity circuit
757
904
  // Enqueues the root parity circuit if all inputs are available
758
- private enqueueBaseParityCircuit(provingState: BlockProvingState, inputs: BaseParityInputs, index: number) {
905
+ private enqueueBaseParityCircuit(
906
+ checkpointProvingState: CheckpointProvingState,
907
+ provingState: BlockProvingState,
908
+ baseParityIndex: number,
909
+ ) {
759
910
  if (!provingState.verifyState()) {
760
- logger.debug('Not running base parity. State no longer valid.');
911
+ this.logger.debug('Not running base parity. State no longer valid.');
912
+ return;
913
+ }
914
+
915
+ if (!provingState.tryStartProvingBaseParity(baseParityIndex)) {
916
+ this.logger.warn(`Base parity ${baseParityIndex} already started.`);
761
917
  return;
762
918
  }
763
919
 
920
+ const inputs = checkpointProvingState.getBaseParityInputs(baseParityIndex);
921
+
764
922
  this.deferredProving(
765
923
  provingState,
766
924
  wrapCallbackInSpan(
767
925
  this.tracer,
768
926
  'ProvingOrchestrator.prover.getBaseParityProof',
769
927
  {
770
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' satisfies CircuitName,
928
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-base' satisfies CircuitName,
771
929
  },
772
930
  signal => this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber),
773
931
  ),
774
932
  provingOutput => {
775
- provingState.setBaseParityProof(index, provingOutput);
933
+ provingState.setBaseParityProof(baseParityIndex, provingOutput);
776
934
  this.checkAndEnqueueRootParityCircuit(provingState);
777
935
  },
778
936
  );
@@ -790,11 +948,16 @@ export class ProvingOrchestrator implements EpochProver {
790
948
  // Enqueues the root rollup proof if all inputs are available
791
949
  private enqueueRootParityCircuit(provingState: BlockProvingState) {
792
950
  if (!provingState.verifyState()) {
793
- logger.debug('Not running root parity. State no longer valid.');
951
+ this.logger.debug('Not running root parity. State no longer valid.');
952
+ return;
953
+ }
954
+
955
+ if (!provingState.tryStartProvingRootParity()) {
956
+ this.logger.debug('Root parity already started.');
794
957
  return;
795
958
  }
796
959
 
797
- const inputs = provingState.getRootParityInputs();
960
+ const inputs = provingState.getParityRootInputs();
798
961
 
799
962
  this.deferredProving(
800
963
  provingState,
@@ -802,22 +965,27 @@ export class ProvingOrchestrator implements EpochProver {
802
965
  this.tracer,
803
966
  'ProvingOrchestrator.prover.getRootParityProof',
804
967
  {
805
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' satisfies CircuitName,
968
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-root' satisfies CircuitName,
806
969
  },
807
970
  signal => this.prover.getRootParityProof(inputs, signal, provingState.epochNumber),
808
971
  ),
809
- async result => {
972
+ result => {
810
973
  provingState.setRootParityProof(result);
811
- await this.checkAndEnqueueBlockRootRollup(provingState);
974
+ this.checkAndEnqueueBlockRootRollup(provingState);
812
975
  },
813
976
  );
814
977
  }
815
978
 
816
979
  // Executes the block merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
817
980
  // Enqueues the next level of merge if all inputs are available
818
- private enqueueBlockMergeRollup(provingState: EpochProvingState, location: TreeNodeLocation) {
981
+ private enqueueBlockMergeRollup(provingState: CheckpointProvingState, location: TreeNodeLocation) {
819
982
  if (!provingState.verifyState()) {
820
- logger.debug('Not running block merge rollup. State no longer valid.');
983
+ this.logger.debug('Not running block merge rollup. State no longer valid.');
984
+ return;
985
+ }
986
+
987
+ if (!provingState.tryStartProvingBlockMerge(location)) {
988
+ this.logger.debug('Block merge rollup already started.');
821
989
  return;
822
990
  }
823
991
 
@@ -828,40 +996,136 @@ export class ProvingOrchestrator implements EpochProver {
828
996
  this.tracer,
829
997
  'ProvingOrchestrator.prover.getBlockMergeRollupProof',
830
998
  {
831
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-merge-rollup' satisfies CircuitName,
999
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-block-merge' satisfies CircuitName,
832
1000
  },
833
1001
  signal => this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber),
834
1002
  ),
835
- result => {
1003
+ async result => {
836
1004
  provingState.setBlockMergeRollupProof(location, result);
837
- this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
1005
+ await this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
1006
+ },
1007
+ );
1008
+ }
1009
+
1010
+ private async enqueueCheckpointRootRollup(provingState: CheckpointProvingState) {
1011
+ if (!provingState.verifyState()) {
1012
+ this.logger.debug('Not running checkpoint root rollup. State no longer valid.');
1013
+ return;
1014
+ }
1015
+
1016
+ if (!provingState.tryStartProvingCheckpointRoot()) {
1017
+ this.logger.debug('Checkpoint root rollup already started.');
1018
+ return;
1019
+ }
1020
+
1021
+ const rollupType = provingState.getCheckpointRootRollupType();
1022
+
1023
+ this.logger.debug(`Enqueuing ${rollupType} for checkpoint ${provingState.index}.`);
1024
+
1025
+ const inputs = await provingState.getCheckpointRootRollupInputs();
1026
+
1027
+ this.deferredProving(
1028
+ provingState,
1029
+ wrapCallbackInSpan(
1030
+ this.tracer,
1031
+ 'ProvingOrchestrator.prover.getCheckpointRootRollupProof',
1032
+ {
1033
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType,
1034
+ },
1035
+ signal => {
1036
+ if (inputs instanceof CheckpointRootSingleBlockRollupPrivateInputs) {
1037
+ return this.prover.getCheckpointRootSingleBlockRollupProof(inputs, signal, provingState.epochNumber);
1038
+ } else {
1039
+ return this.prover.getCheckpointRootRollupProof(inputs, signal, provingState.epochNumber);
1040
+ }
1041
+ },
1042
+ ),
1043
+ result => {
1044
+ const computedEndBlobAccumulatorState = provingState.getEndBlobAccumulator()!.toBlobAccumulator();
1045
+ const circuitEndBlobAccumulatorState = result.inputs.endBlobAccumulator;
1046
+ if (!circuitEndBlobAccumulatorState.equals(computedEndBlobAccumulatorState)) {
1047
+ this.logger.error(
1048
+ `Blob accumulator state mismatch.\nCircuit: ${inspect(circuitEndBlobAccumulatorState)}\nComputed: ${inspect(
1049
+ computedEndBlobAccumulatorState,
1050
+ )}`,
1051
+ );
1052
+ provingState.reject(`Blob accumulator state mismatch.`);
1053
+ return;
1054
+ }
1055
+
1056
+ this.logger.debug(`Completed ${rollupType} proof for checkpoint ${provingState.index}.`);
1057
+
1058
+ const leafLocation = provingState.setCheckpointRootRollupProof(result);
1059
+ const epochProvingState = provingState.parentEpoch;
1060
+
1061
+ if (epochProvingState.totalNumCheckpoints === 1) {
1062
+ this.enqueueEpochPadding(epochProvingState);
1063
+ } else {
1064
+ this.checkAndEnqueueNextCheckpointMergeRollup(epochProvingState, leafLocation);
1065
+ }
1066
+ },
1067
+ );
1068
+ }
1069
+
1070
+ private enqueueCheckpointMergeRollup(provingState: EpochProvingState, location: TreeNodeLocation) {
1071
+ if (!provingState.verifyState()) {
1072
+ this.logger.debug('Not running checkpoint merge rollup. State no longer valid.');
1073
+ return;
1074
+ }
1075
+
1076
+ if (!provingState.tryStartProvingCheckpointMerge(location)) {
1077
+ this.logger.debug('Checkpoint merge rollup already started.');
1078
+ return;
1079
+ }
1080
+
1081
+ const inputs = provingState.getCheckpointMergeRollupInputs(location);
1082
+
1083
+ this.deferredProving(
1084
+ provingState,
1085
+ wrapCallbackInSpan(
1086
+ this.tracer,
1087
+ 'ProvingOrchestrator.prover.getCheckpointMergeRollupProof',
1088
+ {
1089
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-merge' satisfies CircuitName,
1090
+ },
1091
+ signal => this.prover.getCheckpointMergeRollupProof(inputs, signal, provingState.epochNumber),
1092
+ ),
1093
+ result => {
1094
+ this.logger.debug('Completed proof for checkpoint merge rollup.');
1095
+ provingState.setCheckpointMergeRollupProof(location, result);
1096
+ this.checkAndEnqueueNextCheckpointMergeRollup(provingState, location);
838
1097
  },
839
1098
  );
840
1099
  }
841
1100
 
842
1101
  private enqueueEpochPadding(provingState: EpochProvingState) {
843
1102
  if (!provingState.verifyState()) {
844
- logger.debug('Not running epoch padding. State no longer valid.');
1103
+ this.logger.debug('Not running epoch padding. State no longer valid.');
845
1104
  return;
846
1105
  }
847
1106
 
848
- logger.debug('Padding epoch proof with a padding block root proof.');
1107
+ if (!provingState.tryStartProvingPaddingCheckpoint()) {
1108
+ this.logger.debug('Padding checkpoint already started.');
1109
+ return;
1110
+ }
849
1111
 
850
- const inputs = provingState.getPaddingBlockRootInputs();
1112
+ this.logger.debug('Padding epoch proof with a padding block root proof.');
1113
+
1114
+ const inputs = provingState.getPaddingCheckpointInputs();
851
1115
 
852
1116
  this.deferredProving(
853
1117
  provingState,
854
1118
  wrapCallbackInSpan(
855
1119
  this.tracer,
856
- 'ProvingOrchestrator.prover.getPaddingBlockRootRollupProof',
1120
+ 'ProvingOrchestrator.prover.getCheckpointPaddingRollupProof',
857
1121
  {
858
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'padding-block-root-rollup' satisfies CircuitName,
1122
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-padding' satisfies CircuitName,
859
1123
  },
860
- signal => this.prover.getPaddingBlockRootRollupProof(inputs, signal, provingState.epochNumber),
1124
+ signal => this.prover.getCheckpointPaddingRollupProof(inputs, signal, provingState.epochNumber),
861
1125
  ),
862
1126
  result => {
863
- logger.debug('Completed proof for padding block root.');
864
- provingState.setPaddingBlockRootProof(result);
1127
+ this.logger.debug('Completed proof for padding checkpoint.');
1128
+ provingState.setCheckpointPaddingProof(result);
865
1129
  this.checkAndEnqueueRootRollup(provingState);
866
1130
  },
867
1131
  );
@@ -870,11 +1134,11 @@ export class ProvingOrchestrator implements EpochProver {
870
1134
  // Executes the root rollup circuit
871
1135
  private enqueueRootRollup(provingState: EpochProvingState) {
872
1136
  if (!provingState.verifyState()) {
873
- logger.debug('Not running root rollup, state no longer valid');
1137
+ this.logger.debug('Not running root rollup, state no longer valid');
874
1138
  return;
875
1139
  }
876
1140
 
877
- logger.debug(`Preparing root rollup`);
1141
+ this.logger.debug(`Preparing root rollup`);
878
1142
 
879
1143
  const inputs = provingState.getRootRollupInputs();
880
1144
 
@@ -884,60 +1148,66 @@ export class ProvingOrchestrator implements EpochProver {
884
1148
  this.tracer,
885
1149
  'ProvingOrchestrator.prover.getRootRollupProof',
886
1150
  {
887
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup' satisfies CircuitName,
1151
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-root' satisfies CircuitName,
888
1152
  },
889
1153
  signal => this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber),
890
1154
  ),
891
1155
  result => {
892
- logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
1156
+ this.logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
893
1157
  provingState.setRootRollupProof(result);
894
1158
  provingState.resolve({ status: 'success' });
895
1159
  },
896
1160
  );
897
1161
  }
898
1162
 
899
- private async checkAndEnqueueNextMergeRollup(provingState: BlockProvingState, currentLocation: TreeNodeLocation) {
1163
+ private checkAndEnqueueNextMergeRollup(provingState: BlockProvingState, currentLocation: TreeNodeLocation) {
900
1164
  if (!provingState.isReadyForMergeRollup(currentLocation)) {
901
1165
  return;
902
1166
  }
903
1167
 
904
1168
  const parentLocation = provingState.getParentLocation(currentLocation);
905
1169
  if (parentLocation.level === 0) {
906
- await this.checkAndEnqueueBlockRootRollup(provingState);
1170
+ this.checkAndEnqueueBlockRootRollup(provingState);
907
1171
  } else {
908
1172
  this.enqueueMergeRollup(provingState, parentLocation);
909
1173
  }
910
1174
  }
911
1175
 
912
- private async checkAndEnqueueBlockRootRollup(provingState: BlockProvingState) {
913
- const blockNumber = provingState.blockNumber;
914
- // Accumulate as far as we can, in case blocks came in out of order and we are behind:
915
- await this.provingState?.setBlobAccumulators(blockNumber);
1176
+ private checkAndEnqueueBlockRootRollup(provingState: BlockProvingState) {
916
1177
  if (!provingState.isReadyForBlockRootRollup()) {
917
- logger.debug('Not ready for block root rollup');
1178
+ this.logger.debug('Not ready for block root rollup');
918
1179
  return;
919
1180
  }
920
- if (provingState.blockRootRollupStarted) {
921
- logger.debug('Block root rollup already started');
1181
+
1182
+ this.enqueueBlockRootRollup(provingState);
1183
+ }
1184
+
1185
+ private async checkAndEnqueueNextBlockMergeRollup(
1186
+ provingState: CheckpointProvingState,
1187
+ currentLocation: TreeNodeLocation,
1188
+ ) {
1189
+ if (!provingState.isReadyForBlockMerge(currentLocation)) {
922
1190
  return;
923
1191
  }
924
1192
 
925
- // TODO(palla/prover): This closes the fork only on the happy path. If this epoch orchestrator
926
- // is aborted and never reaches this point, it will leak the fork. We need to add a global cleanup,
927
- // but have to make sure it only runs once all operations are completed, otherwise some function here
928
- // will attempt to access the fork after it was closed.
929
- logger.debug(`Cleaning up world state fork for ${blockNumber}`);
930
- void this.dbs
931
- .get(blockNumber)
932
- ?.close()
933
- .then(() => this.dbs.delete(blockNumber))
934
- .catch(err => logger.error(`Error closing db for block ${blockNumber}`, err));
1193
+ const parentLocation = provingState.getParentLocation(currentLocation);
1194
+ if (parentLocation.level === 0) {
1195
+ await this.checkAndEnqueueCheckpointRootRollup(provingState);
1196
+ } else {
1197
+ this.enqueueBlockMergeRollup(provingState, parentLocation);
1198
+ }
1199
+ }
935
1200
 
936
- await this.enqueueBlockRootRollup(provingState);
1201
+ private async checkAndEnqueueCheckpointRootRollup(provingState: CheckpointProvingState) {
1202
+ if (!provingState.isReadyForCheckpointRoot()) {
1203
+ return;
1204
+ }
1205
+
1206
+ await this.enqueueCheckpointRootRollup(provingState);
937
1207
  }
938
1208
 
939
- private checkAndEnqueueNextBlockMergeRollup(provingState: EpochProvingState, currentLocation: TreeNodeLocation) {
940
- if (!provingState.isReadyForBlockMerge(currentLocation)) {
1209
+ private checkAndEnqueueNextCheckpointMergeRollup(provingState: EpochProvingState, currentLocation: TreeNodeLocation) {
1210
+ if (!provingState.isReadyForCheckpointMerge(currentLocation)) {
941
1211
  return;
942
1212
  }
943
1213
 
@@ -945,13 +1215,13 @@ export class ProvingOrchestrator implements EpochProver {
945
1215
  if (parentLocation.level === 0) {
946
1216
  this.checkAndEnqueueRootRollup(provingState);
947
1217
  } else {
948
- this.enqueueBlockMergeRollup(provingState, parentLocation);
1218
+ this.enqueueCheckpointMergeRollup(provingState, parentLocation);
949
1219
  }
950
1220
  }
951
1221
 
952
1222
  private checkAndEnqueueRootRollup(provingState: EpochProvingState) {
953
1223
  if (!provingState.isReadyForRootRollup()) {
954
- logger.debug('Not ready for root rollup');
1224
+ this.logger.debug('Not ready for root rollup');
955
1225
  return;
956
1226
  }
957
1227
 
@@ -966,14 +1236,12 @@ export class ProvingOrchestrator implements EpochProver {
966
1236
  */
967
1237
  private enqueueVM(provingState: BlockProvingState, txIndex: number) {
968
1238
  if (!provingState.verifyState()) {
969
- logger.debug(`Not running VM circuit as state is no longer valid`);
1239
+ this.logger.debug(`Not running VM circuit as state is no longer valid`);
970
1240
  return;
971
1241
  }
972
1242
 
973
1243
  const txProvingState = provingState.getTxProvingState(txIndex);
974
1244
 
975
- // This function tries to do AVM proving. If there is a failure, it fakes the proof unless AVM_PROVING_STRICT is defined.
976
- // Nothing downstream depends on the AVM proof yet. So having this mode lets us incrementally build the AVM circuit.
977
1245
  const doAvmProving = wrapCallbackInSpan(
978
1246
  this.tracer,
979
1247
  'ProvingOrchestrator.prover.getAvmProof',
@@ -982,48 +1250,25 @@ export class ProvingOrchestrator implements EpochProver {
982
1250
  },
983
1251
  async (signal: AbortSignal) => {
984
1252
  const inputs = txProvingState.getAvmInputs();
985
- try {
986
- // TODO(#14234)[Unconditional PIs validation]: Remove the whole try-catch logic and
987
- // just keep the next line but removing the second argument (false).
988
- return await this.prover.getAvmProof(inputs, false, signal, provingState.epochNumber);
989
- } catch (err) {
990
- if (process.env.AVM_PROVING_STRICT) {
991
- logger.error(`Error thrown when proving AVM circuit with AVM_PROVING_STRICT on`, err);
992
- throw err;
993
- } else {
994
- logger.warn(
995
- `Error thrown when proving AVM circuit but AVM_PROVING_STRICT is off. Use snapshotted
996
- AVM inputs and carrying on. ${inspect(err)}.`,
997
- );
998
-
999
- try {
1000
- this.metrics.incAvmFallback();
1001
- const snapshotAvmPrivateInputs = readAvmMinimalPublicTxInputsFromFile();
1002
- return await this.prover.getAvmProof(snapshotAvmPrivateInputs, true, signal, provingState.epochNumber);
1003
- } catch (err) {
1004
- logger.error(`Error thrown when proving snapshotted AVM inputs.`, err);
1005
- throw err;
1006
- }
1007
- }
1008
- }
1253
+ return await this.prover.getAvmProof(inputs, signal, provingState.epochNumber);
1009
1254
  },
1010
1255
  );
1011
1256
 
1012
- this.deferredProving(provingState, doAvmProving, proofAndVk => {
1013
- logger.debug(`Proven VM for tx index: ${txIndex}`);
1014
- txProvingState.setAvmProof(proofAndVk);
1015
- this.checkAndEnqueueNextTxCircuit(provingState, txIndex);
1257
+ this.deferredProving(provingState, doAvmProving, proof => {
1258
+ this.logger.debug(`Proven VM for tx index: ${txIndex}`);
1259
+ txProvingState.setAvmProof(proof);
1260
+ this.checkAndEnqueueBaseRollup(provingState, txIndex);
1016
1261
  });
1017
1262
  }
1018
1263
 
1019
- private checkAndEnqueueNextTxCircuit(provingState: BlockProvingState, txIndex: number) {
1264
+ private checkAndEnqueueBaseRollup(provingState: BlockProvingState, txIndex: number) {
1020
1265
  const txProvingState = provingState.getTxProvingState(txIndex);
1021
1266
  if (!txProvingState.ready()) {
1022
1267
  return;
1023
1268
  }
1024
1269
 
1025
- // We must have completed all proving (tube proof and (if required) vm proof are generated), we now move to the base rollup.
1026
- logger.debug(`Public functions completed for tx ${txIndex} enqueueing base rollup`);
1270
+ // We must have completed all proving (chonk verifier proof and (if required) vm proof are generated), we now move to the base rollup.
1271
+ this.logger.debug(`Public functions completed for tx ${txIndex} enqueueing base rollup`);
1027
1272
 
1028
1273
  this.enqueueBaseRollup(provingState, txIndex);
1029
1274
  }