@aztec/prover-client 3.0.0-nightly.20250917 → 3.0.0-nightly.20250919

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 (65) hide show
  1. package/dest/block-factory/light.d.ts +5 -3
  2. package/dest/block-factory/light.d.ts.map +1 -1
  3. package/dest/block-factory/light.js +16 -9
  4. package/dest/mocks/fixtures.d.ts +3 -1
  5. package/dest/mocks/fixtures.d.ts.map +1 -1
  6. package/dest/mocks/fixtures.js +19 -2
  7. package/dest/mocks/test_context.d.ts +30 -9
  8. package/dest/mocks/test_context.d.ts.map +1 -1
  9. package/dest/mocks/test_context.js +68 -15
  10. package/dest/orchestrator/block-building-helpers.d.ts +16 -14
  11. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  12. package/dest/orchestrator/block-building-helpers.js +69 -66
  13. package/dest/orchestrator/block-proving-state.d.ts +59 -52
  14. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  15. package/dest/orchestrator/block-proving-state.js +215 -187
  16. package/dest/orchestrator/checkpoint-proving-state.d.ts +62 -0
  17. package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -0
  18. package/dest/orchestrator/checkpoint-proving-state.js +208 -0
  19. package/dest/orchestrator/epoch-proving-state.d.ts +32 -25
  20. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  21. package/dest/orchestrator/epoch-proving-state.js +132 -81
  22. package/dest/orchestrator/orchestrator.d.ts +25 -24
  23. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  24. package/dest/orchestrator/orchestrator.js +331 -203
  25. package/dest/orchestrator/tx-proving-state.d.ts +3 -3
  26. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  27. package/dest/orchestrator/tx-proving-state.js +5 -5
  28. package/dest/prover-client/server-epoch-prover.d.ts +8 -7
  29. package/dest/prover-client/server-epoch-prover.d.ts.map +1 -1
  30. package/dest/prover-client/server-epoch-prover.js +7 -7
  31. package/dest/proving_broker/broker_prover_facade.d.ts +18 -13
  32. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
  33. package/dest/proving_broker/broker_prover_facade.js +36 -21
  34. package/dest/proving_broker/fixtures.js +1 -1
  35. package/dest/proving_broker/proof_store/index.d.ts +1 -0
  36. package/dest/proving_broker/proof_store/index.d.ts.map +1 -1
  37. package/dest/proving_broker/proof_store/index.js +1 -0
  38. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  39. package/dest/proving_broker/proving_broker.js +27 -16
  40. package/dest/proving_broker/proving_job_controller.d.ts.map +1 -1
  41. package/dest/proving_broker/proving_job_controller.js +34 -14
  42. package/dest/test/mock_prover.d.ts +18 -13
  43. package/dest/test/mock_prover.d.ts.map +1 -1
  44. package/dest/test/mock_prover.js +31 -16
  45. package/package.json +16 -17
  46. package/src/block-factory/light.ts +33 -9
  47. package/src/mocks/fixtures.ts +25 -7
  48. package/src/mocks/test_context.ts +113 -21
  49. package/src/orchestrator/block-building-helpers.ts +107 -93
  50. package/src/orchestrator/block-proving-state.ts +232 -244
  51. package/src/orchestrator/checkpoint-proving-state.ts +294 -0
  52. package/src/orchestrator/epoch-proving-state.ts +169 -121
  53. package/src/orchestrator/orchestrator.ts +483 -262
  54. package/src/orchestrator/tx-proving-state.ts +6 -6
  55. package/src/prover-client/server-epoch-prover.ts +30 -16
  56. package/src/proving_broker/broker_prover_facade.ts +151 -83
  57. package/src/proving_broker/fixtures.ts +1 -1
  58. package/src/proving_broker/proof_store/index.ts +1 -0
  59. package/src/proving_broker/proving_broker.ts +34 -16
  60. package/src/proving_broker/proving_job_controller.ts +34 -14
  61. package/src/test/mock_prover.ts +125 -54
  62. package/dest/bin/get-proof-inputs.d.ts +0 -2
  63. package/dest/bin/get-proof-inputs.d.ts.map +0 -1
  64. package/dest/bin/get-proof-inputs.js +0 -51
  65. package/src/bin/get-proof-inputs.ts +0 -59
@@ -6,7 +6,7 @@ function _ts_decorate(decorators, target, key, desc) {
6
6
  }
7
7
  import { BlobAccumulatorPublicInputs } from '@aztec/blob-lib';
8
8
  import { L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY } from '@aztec/constants';
9
- import { padArrayEnd, times } from '@aztec/foundation/collection';
9
+ import { padArrayEnd } from '@aztec/foundation/collection';
10
10
  import { AbortError } from '@aztec/foundation/error';
11
11
  import { Fr } from '@aztec/foundation/fields';
12
12
  import { createLogger } from '@aztec/foundation/log';
@@ -14,16 +14,13 @@ import { promiseWithResolvers } from '@aztec/foundation/promise';
14
14
  import { assertLength } from '@aztec/foundation/serialize';
15
15
  import { pushTestData } from '@aztec/foundation/testing';
16
16
  import { elapsed } from '@aztec/foundation/timer';
17
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
18
17
  import { readAvmMinimalPublicTxInputsFromFile } from '@aztec/simulator/public/fixtures';
19
- import { L2Block } from '@aztec/stdlib/block';
20
- import { BaseParityInputs } from '@aztec/stdlib/parity';
21
- import { EmptyBlockRootRollupInputs, PrivateBaseRollupInputs, SingleTxBlockRootRollupInputs } from '@aztec/stdlib/rollup';
18
+ import { createBlockEndMarker } from '@aztec/stdlib/block';
19
+ import { BlockRootEmptyTxFirstRollupPrivateInputs, BlockRootFirstRollupPrivateInputs, BlockRootSingleTxFirstRollupPrivateInputs, BlockRootSingleTxRollupPrivateInputs, CheckpointRootSingleBlockRollupPrivateInputs, PrivateTxBaseRollupPrivateInputs } from '@aztec/stdlib/rollup';
22
20
  import { MerkleTreeId } from '@aztec/stdlib/trees';
23
- import { toNumBlobFields } from '@aztec/stdlib/tx';
24
21
  import { Attributes, getTelemetryClient, trackSpan, wrapCallbackInSpan } from '@aztec/telemetry-client';
25
22
  import { inspect } from 'util';
26
- import { buildHeaderAndBodyFromTxs, getLastSiblingPath, getPublicTubePrivateInputsFromTx, getRootTreeSiblingPath, getSubtreeSiblingPath, getTreeSnapshot, insertSideEffectsAndBuildBaseRollupHints, validatePartialState, validateTx } from './block-building-helpers.js';
23
+ import { buildBlockHeaderFromTxs, buildHeaderFromCircuitOutputs, getLastSiblingPath, getPublicTubePrivateInputsFromTx, getRootTreeSiblingPath, getSubtreeSiblingPath, getTreeSnapshot, insertSideEffectsAndBuildBaseRollupHints, validatePartialState, validateTx } from './block-building-helpers.js';
27
24
  import { EpochProvingState } from './epoch-proving-state.js';
28
25
  import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js';
29
26
  import { TxProvingState } from './tx-proving-state.js';
@@ -68,51 +65,92 @@ const logger = createLogger('prover-client:orchestrator');
68
65
  this.cancel();
69
66
  return Promise.resolve();
70
67
  }
71
- startNewEpoch(epochNumber, firstBlockNumber, totalNumBlocks, finalBlobBatchingChallenges) {
68
+ startNewEpoch(epochNumber, firstCheckpointNumber, totalNumCheckpoints, finalBlobBatchingChallenges) {
69
+ if (this.provingState?.verifyState()) {
70
+ throw new Error(`Cannot start epoch ${epochNumber} when epoch ${this.provingState.epochNumber} is still being processed.`);
71
+ }
72
72
  const { promise: _promise, resolve, reject } = promiseWithResolvers();
73
73
  const promise = _promise.catch((reason)=>({
74
74
  status: 'failure',
75
75
  reason
76
76
  }));
77
- if (totalNumBlocks <= 0 || !Number.isInteger(totalNumBlocks)) {
78
- throw new Error(`Invalid number of blocks for epoch (got ${totalNumBlocks})`);
79
- }
80
- logger.info(`Starting epoch ${epochNumber} with ${totalNumBlocks} blocks`);
81
- this.provingState = new EpochProvingState(epochNumber, firstBlockNumber, totalNumBlocks, finalBlobBatchingChallenges, resolve, reject);
77
+ logger.info(`Starting epoch ${epochNumber} with ${totalNumCheckpoints} checkpoints.`);
78
+ this.provingState = new EpochProvingState(epochNumber, firstCheckpointNumber, totalNumCheckpoints, finalBlobBatchingChallenges, (provingState)=>this.checkAndEnqueueCheckpointRootRollup(provingState), resolve, reject);
82
79
  this.provingPromise = promise;
83
80
  }
81
+ async startNewCheckpoint(constants, l1ToL2Messages, totalNumBlocks, totalNumBlobFields, headerOfLastBlockInPreviousCheckpoint) {
82
+ if (!this.provingState) {
83
+ throw new Error('Empty epoch proving state. Call startNewEpoch before starting a checkpoint.');
84
+ }
85
+ if (!this.provingState.isAcceptingCheckpoints()) {
86
+ throw new Error(`Epoch not accepting further checkpoints.`);
87
+ }
88
+ // Fork world state at the end of the immediately previous block.
89
+ const lastBlockNumber = headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber;
90
+ const db = await this.dbProvider.fork(lastBlockNumber);
91
+ const firstBlockNumber = lastBlockNumber + 1;
92
+ this.dbs.set(firstBlockNumber, db);
93
+ // Get archive sibling path before any block in this checkpoint lands.
94
+ const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db);
95
+ // Insert all the l1 to l2 messages into the db. And get the states before and after the insertion.
96
+ const { lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeSiblingPath } = await this.updateL1ToL2MessageTree(l1ToL2Messages, db);
97
+ this.provingState.startNewCheckpoint(constants, totalNumBlocks, totalNumBlobFields, headerOfLastBlockInPreviousCheckpoint, lastArchiveSiblingPath, l1ToL2Messages, lastL1ToL2MessageTreeSnapshot, lastL1ToL2MessageSubtreeSiblingPath, newL1ToL2MessageTreeSnapshot, newL1ToL2MessageSubtreeSiblingPath);
98
+ }
84
99
  /**
85
100
  * Starts off a new block
86
- * @param globalVariables - The global variables for the block
87
- * @param l1ToL2Messages - The l1 to l2 messages for the block
88
- * @returns A proving ticket, containing a promise notifying of proving completion
89
- */ async startNewBlock(globalVariables, l1ToL2Messages, previousBlockHeader) {
101
+ * @param blockNumber - The block number
102
+ * @param timestamp - The timestamp of the block. This is only required for constructing the private inputs for the
103
+ * block that doesn't have any txs.
104
+ * @param totalNumTxs - The total number of txs in the block
105
+ */ async startNewBlock(blockNumber, timestamp, totalNumTxs) {
90
106
  if (!this.provingState) {
91
- throw new Error(`Invalid proving state, call startNewEpoch before starting a block`);
92
- }
93
- if (!this.provingState?.isAcceptingBlocks()) {
94
- throw new Error(`Epoch not accepting further blocks`);
95
- }
96
- logger.info(`Starting block ${globalVariables.blockNumber} for slot ${globalVariables.slotNumber.toNumber()}`);
97
- // Fork world state at the end of the immediately previous block
98
- const db = await this.dbProvider.fork(globalVariables.blockNumber - 1);
99
- this.dbs.set(globalVariables.blockNumber, db);
100
- // we start the block by enqueueing all of the base parity circuits
101
- const { l1ToL2MessageTreeSnapshot, l1ToL2MessageSubtreeSiblingPath, l1ToL2MessageTreeSnapshotAfterInsertion, baseParityInputs } = await this.prepareBaseParityInputs(l1ToL2Messages, db);
102
- // Get archive snapshot before this block lands
103
- const lastArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
104
- const lastArchiveSiblingPath = await getLastSiblingPath(MerkleTreeId.ARCHIVE, db);
105
- const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
106
- const blockProvingState = this.provingState.startNewBlock(globalVariables, l1ToL2Messages, l1ToL2MessageTreeSnapshot, l1ToL2MessageSubtreeSiblingPath, l1ToL2MessageTreeSnapshotAfterInsertion, lastArchive, lastArchiveSiblingPath, newArchiveSiblingPath, previousBlockHeader, this.proverId);
107
- // Enqueue base parity circuits for the block
108
- for(let i = 0; i < baseParityInputs.length; i++){
109
- this.enqueueBaseParityCircuit(blockProvingState, baseParityInputs[i], i);
107
+ throw new Error('Empty epoch proving state. Call startNewEpoch before starting a block.');
108
+ }
109
+ const checkpointProvingState = this.provingState.getCheckpointProvingStateByBlockNumber(blockNumber);
110
+ if (!checkpointProvingState) {
111
+ throw new Error(`Checkpoint not started. Call startNewCheckpoint first.`);
112
+ }
113
+ if (!checkpointProvingState.isAcceptingBlocks()) {
114
+ throw new Error(`Checkpoint not accepting further blocks.`);
115
+ }
116
+ const constants = checkpointProvingState.constants;
117
+ logger.info(`Starting block ${blockNumber} for slot ${constants.slotNumber.toNumber()}.`);
118
+ // Fork the db only when it's not already set. The db for the first block is set in `startNewCheckpoint`.
119
+ if (!this.dbs.has(blockNumber)) {
120
+ // Fork world state at the end of the immediately previous block
121
+ const db = await this.dbProvider.fork(blockNumber - 1);
122
+ this.dbs.set(blockNumber, db);
123
+ }
124
+ const db = this.dbs.get(blockNumber);
125
+ // Get archive snapshot and sibling path before any txs in this block lands.
126
+ const lastArchiveTreeSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
127
+ const lastArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, db);
128
+ const blockProvingState = checkpointProvingState.startNewBlock(blockNumber, timestamp, totalNumTxs, lastArchiveTreeSnapshot, lastArchiveSiblingPath);
129
+ // Enqueue base parity circuits for the first block in the checkpoint.
130
+ if (blockProvingState.index === 0) {
131
+ for(let i = 0; i < NUM_BASE_PARITY_PER_ROOT_PARITY; i++){
132
+ this.enqueueBaseParityCircuit(checkpointProvingState, blockProvingState, i);
133
+ }
134
+ }
135
+ // Because `addTxs` won't be called for a block without txs, and that's where the sponge blob state is computed.
136
+ // We need to set its end sponge blob here, which will become the start sponge blob for the next block.
137
+ if (totalNumTxs === 0) {
138
+ const endSpongeBlob = blockProvingState.getStartSpongeBlob().clone();
139
+ await endSpongeBlob.absorb([
140
+ createBlockEndMarker(0)
141
+ ]);
142
+ blockProvingState.setEndSpongeBlob(endSpongeBlob);
143
+ // And also try to accumulate the blobs as far as we can:
144
+ await this.provingState.setBlobAccumulators();
110
145
  }
111
146
  }
112
147
  /**
113
148
  * The interface to add simulated transactions to the scheduler. This can only be called once per block.
114
149
  * @param txs - The transactions to be proven
115
150
  */ async addTxs(txs) {
151
+ if (!this.provingState) {
152
+ throw new Error(`Empty epoch proving state. Call startNewEpoch before adding txs.`);
153
+ }
116
154
  if (!txs.length) {
117
155
  // To avoid an ugly throw below. If we require an empty block, we can just call setBlockCompleted
118
156
  // on a block with no txs. We cannot do that here because we cannot find the blockNumber without any txs.
@@ -120,16 +158,21 @@ const logger = createLogger('prover-client:orchestrator');
120
158
  return;
121
159
  }
122
160
  const blockNumber = txs[0].globalVariables.blockNumber;
123
- const provingState = this.provingState?.getBlockProvingStateByBlockNumber(blockNumber);
161
+ const provingState = this.provingState.getBlockProvingStateByBlockNumber(blockNumber);
124
162
  if (!provingState) {
125
- throw new Error(`Block proving state for ${blockNumber} not found`);
163
+ throw new Error(`Proving state for block ${blockNumber} not found. Call startNewBlock first.`);
164
+ }
165
+ if (provingState.totalNumTxs !== txs.length) {
166
+ throw new Error(`Block ${blockNumber} should be filled with ${provingState.totalNumTxs} txs. Received ${txs.length} txs.`);
126
167
  }
127
- if (provingState.totalNumTxs) {
168
+ if (!provingState.isAcceptingTxs()) {
128
169
  throw new Error(`Block ${blockNumber} has been initialized with transactions.`);
129
170
  }
130
- const numBlobFields = toNumBlobFields(txs);
131
- provingState.startNewBlock(txs.length, numBlobFields);
132
- logger.info(`Adding ${txs.length} transactions with ${numBlobFields} blob fields to block ${provingState.blockNumber}`);
171
+ logger.info(`Adding ${txs.length} transactions to block ${blockNumber}`);
172
+ const db = this.dbs.get(blockNumber);
173
+ const lastArchive = provingState.lastArchiveTreeSnapshot;
174
+ const newL1ToL2MessageTreeSnapshot = provingState.newL1ToL2MessageTreeSnapshot;
175
+ const spongeBlobState = provingState.getStartSpongeBlob().clone();
133
176
  for (const tx of txs){
134
177
  try {
135
178
  if (!provingState.verifyState()) {
@@ -137,7 +180,12 @@ const logger = createLogger('prover-client:orchestrator');
137
180
  }
138
181
  validateTx(tx);
139
182
  logger.info(`Received transaction: ${tx.hash}`);
140
- const [hints, treeSnapshots] = await this.prepareTransaction(tx, provingState);
183
+ const startSpongeBlob = spongeBlobState.clone();
184
+ const [hints, treeSnapshots] = await this.prepareBaseRollupInputs(tx, lastArchive, newL1ToL2MessageTreeSnapshot, startSpongeBlob, db);
185
+ if (!provingState.verifyState()) {
186
+ throw new Error(`Unable to add transaction, preparing base inputs failed`);
187
+ }
188
+ await spongeBlobState.absorb(tx.txEffect.toBlobFields());
141
189
  const txProvingState = new TxProvingState(tx, hints, treeSnapshots);
142
190
  const txIndex = provingState.addNewTx(txProvingState);
143
191
  if (txProvingState.requireAvmProof) {
@@ -154,13 +202,19 @@ const logger = createLogger('prover-client:orchestrator');
154
202
  });
155
203
  }
156
204
  }
205
+ await spongeBlobState.absorb([
206
+ createBlockEndMarker(txs.length)
207
+ ]);
208
+ provingState.setEndSpongeBlob(spongeBlobState);
209
+ // Txs have been added to the block. Now try to accumulate the blobs as far as we can:
210
+ await this.provingState.setBlobAccumulators();
157
211
  }
158
212
  /**
159
213
  * Kickstarts tube circuits for the specified txs. These will be used during epoch proving.
160
214
  * Note that if the tube circuits are not started this way, they will be started nontheless after processing.
161
215
  */ startTubeCircuits(txs) {
162
216
  if (!this.provingState?.verifyState()) {
163
- throw new Error(`Invalid proving state, call startNewEpoch before starting tube circuits`);
217
+ throw new Error(`Empty epoch proving state. call startNewEpoch before starting tube circuits.`);
164
218
  }
165
219
  const publicTxs = txs.filter((tx)=>tx.data.forPublic);
166
220
  for (const tx of publicTxs){
@@ -171,7 +225,7 @@ const logger = createLogger('prover-client:orchestrator');
171
225
  this.doEnqueueTube(txHash, privateInputs, (proof)=>{
172
226
  tubeProof.resolve(proof);
173
227
  });
174
- this.provingState?.cachedTubeProofs.set(txHash, tubeProof.promise);
228
+ this.provingState.cachedTubeProofs.set(txHash, tubeProof.promise);
175
229
  }
176
230
  return Promise.resolve();
177
231
  }
@@ -183,57 +237,82 @@ const logger = createLogger('prover-client:orchestrator');
183
237
  if (!provingState) {
184
238
  throw new Error(`Block proving state for ${blockNumber} not found`);
185
239
  }
186
- if (!provingState.spongeBlobState) {
187
- // If we are completing an empty block, initialize the provingState.
188
- // We will have 0 txs and no blob fields.
189
- provingState.startNewBlock(0, 0);
240
+ // Abort with specific error for the block if there's one.
241
+ const error = provingState.getError();
242
+ if (error) {
243
+ throw new Error(`Block proving failed: ${error}`);
190
244
  }
245
+ // Abort if the proving state is not valid due to errors occurred elsewhere.
191
246
  if (!provingState.verifyState()) {
192
- throw new Error(`Block proving failed: ${provingState.error}`);
247
+ throw new Error(`Invalid proving state when completing block ${blockNumber}.`);
248
+ }
249
+ if (provingState.isAcceptingTxs()) {
250
+ throw new Error(`Block ${blockNumber} is still accepting txs. Call setBlockCompleted after all txs have been added.`);
193
251
  }
194
252
  // And build the block header
195
253
  logger.verbose(`Block ${blockNumber} completed. Assembling header.`);
196
- await this.buildBlock(provingState, expectedHeader);
197
- logger.debug(`Accumulating blobs for ${blockNumber}`);
198
- await this.provingState?.setBlobAccumulators(blockNumber);
199
- // If the proofs were faster than the block building, then we need to try the block root rollup again here
200
- await this.checkAndEnqueueBlockRootRollup(provingState);
201
- return provingState.block;
202
- }
203
- /** Returns the block as built for a given index. */ getBlock(index) {
204
- const block = this.provingState?.blocks[index]?.block;
205
- if (!block) {
206
- throw new Error(`Block at index ${index} not available`);
207
- }
208
- return block;
209
- }
210
- async buildBlock(provingState, expectedHeader) {
211
- // Collect all new nullifiers, commitments, and contracts from all txs in this block to build body
212
- const txs = provingState.allTxs.map((a)=>a.processedTx);
254
+ const header = await this.buildL2BlockHeader(provingState, expectedHeader);
255
+ await this.verifyBuiltBlockAgainstSyncedState(provingState);
256
+ return header;
257
+ }
258
+ async buildL2BlockHeader(provingState, expectedHeader) {
259
+ // Collect all txs in this block to build the header. The function calling this has made sure that all txs have been added.
260
+ const txs = provingState.getProcessedTxs();
261
+ const startSpongeBlob = provingState.getStartSpongeBlob();
213
262
  // Get db for this block
214
263
  const db = this.dbs.get(provingState.blockNumber);
215
264
  // Given we've applied every change from this block, now assemble the block header
216
265
  // and update the archive tree, so we're ready to start processing the next block
217
- const { header, body } = await buildHeaderAndBodyFromTxs(txs, provingState.globalVariables, provingState.newL1ToL2Messages, db);
266
+ const header = await buildBlockHeaderFromTxs(txs, provingState.getGlobalVariables(), startSpongeBlob, db);
218
267
  if (expectedHeader && !header.equals(expectedHeader)) {
219
268
  logger.error(`Block header mismatch: header=${header} expectedHeader=${expectedHeader}`);
220
269
  throw new Error('Block header mismatch');
221
270
  }
222
271
  logger.verbose(`Updating archive tree with block ${provingState.blockNumber} header ${(await header.hash()).toString()}`);
223
272
  await db.updateArchive(header);
224
- // Assemble the L2 block
225
- const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
226
- const l2Block = new L2Block(newArchive, header, body);
227
- await this.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive);
228
- logger.verbose(`Orchestrator finalized block ${l2Block.number}`);
229
- provingState.setBlock(l2Block);
273
+ provingState.setBuiltBlockHeader(header);
274
+ return header;
230
275
  }
231
276
  // Flagged as protected to disable in certain unit tests
232
- async verifyBuiltBlockAgainstSyncedState(l2Block, newArchive) {
233
- const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(l2Block.number));
277
+ async verifyBuiltBlockAgainstSyncedState(provingState) {
278
+ const builtBlockHeader = provingState.getBuiltBlockHeader();
279
+ if (!builtBlockHeader) {
280
+ logger.debug('Block header not built yet, skipping header check.');
281
+ return;
282
+ }
283
+ const output = provingState.getBlockRootRollupOutput();
284
+ if (!output) {
285
+ logger.debug('Block root rollup proof not built yet, skipping header check.');
286
+ return;
287
+ }
288
+ const header = await buildHeaderFromCircuitOutputs(output);
289
+ if (!(await header.hash()).equals(await builtBlockHeader.hash())) {
290
+ logger.error(`Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(builtBlockHeader)}`);
291
+ provingState.reject(`Block header hash mismatch.`);
292
+ return;
293
+ }
294
+ // Get db for this block
295
+ const blockNumber = provingState.blockNumber;
296
+ const db = this.dbs.get(blockNumber);
297
+ const newArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, db);
298
+ const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(blockNumber));
234
299
  if (!syncedArchive.equals(newArchive)) {
235
- throw new Error(`Archive tree mismatch for block ${l2Block.number}: world state synced to ${inspect(syncedArchive)} but built ${inspect(newArchive)}`);
300
+ logger.error(`Archive tree mismatch for block ${blockNumber}: world state synced to ${inspect(syncedArchive)} but built ${inspect(newArchive)}`);
301
+ provingState.reject(`Archive tree mismatch.`);
302
+ return;
236
303
  }
304
+ const circuitArchive = output.newArchive;
305
+ if (!newArchive.equals(circuitArchive)) {
306
+ logger.error(`New archive mismatch.\nCircuit: ${output.newArchive}\nComputed: ${newArchive}`);
307
+ provingState.reject(`New archive mismatch.`);
308
+ return;
309
+ }
310
+ // TODO(palla/prover): This closes the fork only on the happy path. If this epoch orchestrator
311
+ // is aborted and never reaches this point, it will leak the fork. We need to add a global cleanup,
312
+ // but have to make sure it only runs once all operations are completed, otherwise some function here
313
+ // will attempt to access the fork after it was closed.
314
+ logger.debug(`Cleaning up world state fork for ${blockNumber}`);
315
+ void this.dbs.get(blockNumber)?.close().then(()=>this.dbs.delete(blockNumber)).catch((err)=>logger.error(`Error closing db for block ${blockNumber}`, err));
237
316
  }
238
317
  /**
239
318
  * Cancel any further proving
@@ -253,14 +332,7 @@ const logger = createLogger('prover-client:orchestrator');
253
332
  if (result.status === 'failure') {
254
333
  throw new Error(`Epoch proving failed: ${result.reason}`);
255
334
  }
256
- // TODO(MW): Move this? Requires async and don't want to force root methods to be async
257
- // TODO(MW): EpochProvingState uses this.blocks.filter(b => !!b).length as total blocks, use this below:
258
- const finalBlock = this.provingState.blocks[this.provingState.totalNumBlocks - 1];
259
- if (!finalBlock || !finalBlock.endBlobAccumulator) {
260
- throw new Error(`Epoch's final block not ready for finalize`);
261
- }
262
- const finalBatchedBlob = await finalBlock.endBlobAccumulator.finalize();
263
- this.provingState.setFinalBatchedBlob(finalBatchedBlob);
335
+ await this.provingState.finalizeBatchedBlob();
264
336
  const epochProofResult = this.provingState.getEpochProofResult();
265
337
  pushTestData('epochProofResult', {
266
338
  proof: epochProofResult.proof.toString(),
@@ -269,24 +341,12 @@ const logger = createLogger('prover-client:orchestrator');
269
341
  return epochProofResult;
270
342
  }
271
343
  /**
272
- * Starts the proving process for the given transaction and adds it to our state
273
- * @param tx - The transaction whose proving we wish to commence
274
- * @param provingState - The proving state being worked on
275
- */ async prepareTransaction(tx, provingState) {
276
- const txInputs = await this.prepareBaseRollupInputs(provingState, tx);
277
- if (!txInputs) {
278
- // This should not be possible
279
- throw new Error(`Unable to add transaction, preparing base inputs failed`);
280
- }
281
- return txInputs;
282
- }
283
- /**
284
344
  * Enqueue a job to be scheduled
285
345
  * @param provingState - The proving state object being operated on
286
346
  * @param jobType - The type of job to be queued
287
347
  * @param job - The actual job, returns a promise notifying of the job's completion
288
348
  */ deferredProving(provingState, request, callback) {
289
- if (!provingState?.verifyState()) {
349
+ if (!provingState.verifyState()) {
290
350
  logger.debug(`Not enqueuing job, state no longer valid`);
291
351
  return;
292
352
  }
@@ -301,7 +361,7 @@ const logger = createLogger('prover-client:orchestrator');
301
361
  return;
302
362
  }
303
363
  const result = await request(controller.signal);
304
- if (!provingState?.verifyState()) {
364
+ if (!provingState.verifyState()) {
305
365
  logger.debug(`State no longer valid, discarding result`);
306
366
  return;
307
367
  }
@@ -329,29 +389,26 @@ const logger = createLogger('prover-client:orchestrator');
329
389
  // let the callstack unwind before adding the job to the queue
330
390
  setImmediate(()=>void safeJob());
331
391
  }
332
- async prepareBaseParityInputs(l1ToL2Messages, db) {
392
+ async updateL1ToL2MessageTree(l1ToL2Messages, db) {
333
393
  const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages');
334
- const baseParityInputs = times(NUM_BASE_PARITY_PER_ROOT_PARITY, (i)=>BaseParityInputs.fromSlice(l1ToL2MessagesPadded, i, getVKTreeRoot()));
335
- const l1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
336
- const l1ToL2MessageSubtreeSiblingPath = assertLength(await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH);
394
+ const lastL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
395
+ const lastL1ToL2MessageSubtreeSiblingPath = assertLength(await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH);
337
396
  // Update the local trees to include the new l1 to l2 messages
338
397
  await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
339
- const l1ToL2MessageTreeSnapshotAfterInsertion = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
398
+ const newL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
399
+ const newL1ToL2MessageSubtreeSiblingPath = assertLength(await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db), L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH);
340
400
  return {
341
- l1ToL2MessageTreeSnapshot,
342
- l1ToL2MessageSubtreeSiblingPath,
343
- l1ToL2MessageTreeSnapshotAfterInsertion,
344
- baseParityInputs
401
+ lastL1ToL2MessageTreeSnapshot,
402
+ lastL1ToL2MessageSubtreeSiblingPath,
403
+ newL1ToL2MessageTreeSnapshot,
404
+ newL1ToL2MessageSubtreeSiblingPath
345
405
  };
346
406
  }
347
407
  // Updates the merkle trees for a transaction. The first enqueued job for a transaction
348
- async prepareBaseRollupInputs(provingState, tx) {
349
- if (!provingState.verifyState() || !provingState.spongeBlobState) {
350
- logger.debug('Not preparing base rollup inputs, state invalid');
351
- return;
352
- }
353
- const db = this.dbs.get(provingState.blockNumber);
354
- const [ms, hints] = await elapsed(insertSideEffectsAndBuildBaseRollupHints(tx, provingState.globalVariables, provingState.l1ToL2MessageTreeSnapshotAfterInsertion, db, provingState.spongeBlobState));
408
+ async prepareBaseRollupInputs(tx, lastArchive, newL1ToL2MessageTreeSnapshot, startSpongeBlob, db) {
409
+ // We build the base rollup inputs using a mock proof and verification key.
410
+ // These will be overwritten later once we have proven the tube circuit and any public kernels
411
+ const [ms, hints] = await elapsed(insertSideEffectsAndBuildBaseRollupHints(tx, lastArchive, newL1ToL2MessageTreeSnapshot, startSpongeBlob, this.proverId.toField(), db));
355
412
  this.metrics.recordBaseRollupInputs(ms);
356
413
  const promises = [
357
414
  MerkleTreeId.NOTE_HASH_TREE,
@@ -367,10 +424,6 @@ const logger = createLogger('prover-client:orchestrator');
367
424
  obj.key,
368
425
  obj.value
369
426
  ]));
370
- if (!provingState.verifyState()) {
371
- logger.debug(`Discarding proving job, state no longer valid`);
372
- return;
373
- }
374
427
  return [
375
428
  hints,
376
429
  treeSnapshots
@@ -383,27 +436,31 @@ const logger = createLogger('prover-client:orchestrator');
383
436
  logger.debug('Not running base rollup, state invalid');
384
437
  return;
385
438
  }
439
+ if (!provingState.tryStartProvingBase(txIndex)) {
440
+ logger.debug(`Base rollup for tx ${txIndex} already started.`);
441
+ return;
442
+ }
386
443
  const txProvingState = provingState.getTxProvingState(txIndex);
387
444
  const { processedTx } = txProvingState;
388
445
  const { rollupType, inputs } = txProvingState.getBaseRollupTypeAndInputs();
389
446
  logger.debug(`Enqueuing deferred proving base rollup for ${processedTx.hash.toString()}`);
390
- this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, `ProvingOrchestrator.prover.${inputs instanceof PrivateBaseRollupInputs ? 'getPrivateBaseRollupProof' : 'getPublicBaseRollupProof'}`, {
447
+ this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, `ProvingOrchestrator.prover.${inputs instanceof PrivateTxBaseRollupPrivateInputs ? 'getPrivateTxBaseRollupProof' : 'getPublicTxBaseRollupProof'}`, {
391
448
  [Attributes.TX_HASH]: processedTx.hash.toString(),
392
449
  [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
393
450
  }, (signal)=>{
394
- if (inputs instanceof PrivateBaseRollupInputs) {
395
- return this.prover.getPrivateBaseRollupProof(inputs, signal, provingState.epochNumber);
451
+ if (inputs instanceof PrivateTxBaseRollupPrivateInputs) {
452
+ return this.prover.getPrivateTxBaseRollupProof(inputs, signal, provingState.epochNumber);
396
453
  } else {
397
- return this.prover.getPublicBaseRollupProof(inputs, signal, provingState.epochNumber);
454
+ return this.prover.getPublicTxBaseRollupProof(inputs, signal, provingState.epochNumber);
398
455
  }
399
- }), async (result)=>{
456
+ }), (result)=>{
400
457
  logger.debug(`Completed proof for ${rollupType} for tx ${processedTx.hash.toString()}`);
401
- validatePartialState(result.inputs.end, txProvingState.treeSnapshots);
458
+ validatePartialState(result.inputs.endPartialState, txProvingState.treeSnapshots);
402
459
  const leafLocation = provingState.setBaseRollupProof(txIndex, result);
403
460
  if (provingState.totalNumTxs === 1) {
404
- await this.checkAndEnqueueBlockRootRollup(provingState);
461
+ this.checkAndEnqueueBlockRootRollup(provingState);
405
462
  } else {
406
- await this.checkAndEnqueueNextMergeRollup(provingState, leafLocation);
463
+ this.checkAndEnqueueNextMergeRollup(provingState, leafLocation);
407
464
  }
408
465
  });
409
466
  }
@@ -436,14 +493,14 @@ const logger = createLogger('prover-client:orchestrator');
436
493
  this.doEnqueueTube(txHash, txProvingState.getPublicTubePrivateInputs(), handleResult);
437
494
  }
438
495
  doEnqueueTube(txHash, inputs, handler, provingState = this.provingState) {
439
- if (!provingState?.verifyState()) {
496
+ if (!provingState.verifyState()) {
440
497
  logger.debug('Not running tube circuit, state invalid');
441
498
  return;
442
499
  }
443
500
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getPublicTubeProof', {
444
501
  [Attributes.TX_HASH]: txHash,
445
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'public-tube'
446
- }, (signal)=>this.prover.getPublicTubeProof(inputs, signal, this.provingState.epochNumber)), handler);
502
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'tube-public'
503
+ }, (signal)=>this.prover.getPublicTubeProof(inputs, signal, provingState.epochNumber)), handler);
447
504
  }
448
505
  // Executes the merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
449
506
  // Enqueues the next level of merge if all inputs are available
@@ -452,74 +509,73 @@ const logger = createLogger('prover-client:orchestrator');
452
509
  logger.debug('Not running merge rollup. State no longer valid.');
453
510
  return;
454
511
  }
512
+ if (!provingState.tryStartProvingMerge(location)) {
513
+ logger.debug('Merge rollup already started.');
514
+ return;
515
+ }
455
516
  const inputs = provingState.getMergeRollupInputs(location);
456
- this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getMergeRollupProof', {
457
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup'
458
- }, (signal)=>this.prover.getMergeRollupProof(inputs, signal, provingState.epochNumber)), async (result)=>{
517
+ this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getTxMergeRollupProof', {
518
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-tx-merge'
519
+ }, (signal)=>this.prover.getTxMergeRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
459
520
  provingState.setMergeRollupProof(location, result);
460
- await this.checkAndEnqueueNextMergeRollup(provingState, location);
521
+ this.checkAndEnqueueNextMergeRollup(provingState, location);
461
522
  });
462
523
  }
463
524
  // Executes the block root rollup circuit
464
- async enqueueBlockRootRollup(provingState) {
525
+ enqueueBlockRootRollup(provingState) {
465
526
  if (!provingState.verifyState()) {
466
527
  logger.debug('Not running block root rollup, state no longer valid');
467
528
  return;
468
529
  }
469
- provingState.blockRootRollupStarted = true;
470
- const { rollupType, inputs } = await provingState.getBlockRootRollupTypeAndInputs();
471
- logger.debug(`Enqueuing ${rollupType} for block ${provingState.blockNumber} with ${provingState.newL1ToL2Messages.length} l1 to l2 msgs.`);
530
+ if (!provingState.tryStartProvingBlockRoot()) {
531
+ logger.debug('Block root rollup already started.');
532
+ return;
533
+ }
534
+ const { rollupType, inputs } = provingState.getBlockRootRollupTypeAndInputs();
535
+ logger.debug(`Enqueuing ${rollupType} for block ${provingState.blockNumber}.`);
472
536
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getBlockRootRollupProof', {
473
537
  [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
474
538
  }, (signal)=>{
475
- if (inputs instanceof EmptyBlockRootRollupInputs) {
476
- return this.prover.getEmptyBlockRootRollupProof(inputs, signal, provingState.epochNumber);
477
- } else if (inputs instanceof SingleTxBlockRootRollupInputs) {
478
- return this.prover.getSingleTxBlockRootRollupProof(inputs, signal, provingState.epochNumber);
539
+ if (inputs instanceof BlockRootFirstRollupPrivateInputs) {
540
+ return this.prover.getBlockRootFirstRollupProof(inputs, signal, provingState.epochNumber);
541
+ } else if (inputs instanceof BlockRootSingleTxFirstRollupPrivateInputs) {
542
+ return this.prover.getBlockRootSingleTxFirstRollupProof(inputs, signal, provingState.epochNumber);
543
+ } else if (inputs instanceof BlockRootEmptyTxFirstRollupPrivateInputs) {
544
+ return this.prover.getBlockRootEmptyTxFirstRollupProof(inputs, signal, provingState.epochNumber);
545
+ } else if (inputs instanceof BlockRootSingleTxRollupPrivateInputs) {
546
+ return this.prover.getBlockRootSingleTxRollupProof(inputs, signal, provingState.epochNumber);
479
547
  } else {
480
548
  return this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber);
481
549
  }
482
550
  }), async (result)=>{
483
- provingState.setBlockRootRollupProof(result);
484
- const header = await provingState.buildHeaderFromProvingOutputs();
485
- if (!(await header.hash()).equals(await provingState.block.header.hash())) {
486
- logger.error(`Block header mismatch.\nCircuit: ${inspect(header)}\nComputed: ${inspect(provingState.block.header)}`);
487
- provingState.reject(`Block header hash mismatch.`);
488
- }
489
- const dbArchiveRoot = provingState.block.archive.root;
490
- const circuitArchiveRoot = result.inputs.newArchive.root;
491
- if (!dbArchiveRoot.equals(circuitArchiveRoot)) {
492
- logger.error(`New archive root mismatch.\nCircuit: ${result.inputs.newArchive.root}\nComputed: ${dbArchiveRoot}`);
493
- provingState.reject(`New archive root mismatch.`);
494
- }
495
- const endBlobAccumulatorPublicInputs = BlobAccumulatorPublicInputs.fromBatchedBlobAccumulator(provingState.endBlobAccumulator);
496
- const circuitEndBlobAccumulatorState = result.inputs.blobPublicInputs.endBlobAccumulator;
497
- if (!circuitEndBlobAccumulatorState.equals(endBlobAccumulatorPublicInputs)) {
498
- logger.error(`Blob accumulator state mismatch.\nCircuit: ${inspect(circuitEndBlobAccumulatorState)}\nComputed: ${inspect(endBlobAccumulatorPublicInputs)}`);
499
- provingState.reject(`Blob accumulator state mismatch.`);
500
- }
501
- logger.debug(`Completed ${rollupType} proof for block ${provingState.block.number}`);
502
- // validatePartialState(result.inputs.end, tx.treeSnapshots); // TODO(palla/prover)
503
- const epochProvingState = this.provingState;
504
- const leafLocation = epochProvingState.setBlockRootRollupProof(provingState.index, result);
505
- if (epochProvingState.totalNumBlocks === 1) {
506
- this.enqueueEpochPadding(epochProvingState);
551
+ // If the proofs were slower than the block header building, then we need to try validating the block header hashes here.
552
+ await this.verifyBuiltBlockAgainstSyncedState(provingState);
553
+ logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}`);
554
+ const leafLocation = provingState.setBlockRootRollupProof(result);
555
+ const checkpointProvingState = provingState.parentCheckpoint;
556
+ if (checkpointProvingState.totalNumBlocks === 1) {
557
+ this.checkAndEnqueueCheckpointRootRollup(checkpointProvingState);
507
558
  } else {
508
- this.checkAndEnqueueNextBlockMergeRollup(epochProvingState, leafLocation);
559
+ this.checkAndEnqueueNextBlockMergeRollup(checkpointProvingState, leafLocation);
509
560
  }
510
561
  });
511
562
  }
512
563
  // Executes the base parity circuit and stores the intermediate state for the root parity circuit
513
564
  // Enqueues the root parity circuit if all inputs are available
514
- enqueueBaseParityCircuit(provingState, inputs, index) {
565
+ enqueueBaseParityCircuit(checkpointProvingState, provingState, baseParityIndex) {
515
566
  if (!provingState.verifyState()) {
516
567
  logger.debug('Not running base parity. State no longer valid.');
517
568
  return;
518
569
  }
570
+ if (!provingState.tryStartProvingBaseParity(baseParityIndex)) {
571
+ logger.warn(`Base parity ${baseParityIndex} already started.`);
572
+ return;
573
+ }
574
+ const inputs = checkpointProvingState.getBaseParityInputs(baseParityIndex);
519
575
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getBaseParityProof', {
520
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity'
576
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-base'
521
577
  }, (signal)=>this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber)), (provingOutput)=>{
522
- provingState.setBaseParityProof(index, provingOutput);
578
+ provingState.setBaseParityProof(baseParityIndex, provingOutput);
523
579
  this.checkAndEnqueueRootParityCircuit(provingState);
524
580
  });
525
581
  }
@@ -536,12 +592,16 @@ const logger = createLogger('prover-client:orchestrator');
536
592
  logger.debug('Not running root parity. State no longer valid.');
537
593
  return;
538
594
  }
539
- const inputs = provingState.getRootParityInputs();
595
+ if (!provingState.tryStartProvingRootParity()) {
596
+ logger.debug('Root parity already started.');
597
+ return;
598
+ }
599
+ const inputs = provingState.getParityRootInputs();
540
600
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getRootParityProof', {
541
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity'
542
- }, (signal)=>this.prover.getRootParityProof(inputs, signal, provingState.epochNumber)), async (result)=>{
601
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'parity-root'
602
+ }, (signal)=>this.prover.getRootParityProof(inputs, signal, provingState.epochNumber)), (result)=>{
543
603
  provingState.setRootParityProof(result);
544
- await this.checkAndEnqueueBlockRootRollup(provingState);
604
+ this.checkAndEnqueueBlockRootRollup(provingState);
545
605
  });
546
606
  }
547
607
  // Executes the block merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit
@@ -551,26 +611,90 @@ const logger = createLogger('prover-client:orchestrator');
551
611
  logger.debug('Not running block merge rollup. State no longer valid.');
552
612
  return;
553
613
  }
614
+ if (!provingState.tryStartProvingBlockMerge(location)) {
615
+ logger.debug('Block merge rollup already started.');
616
+ return;
617
+ }
554
618
  const inputs = provingState.getBlockMergeRollupInputs(location);
555
619
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getBlockMergeRollupProof', {
556
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'block-merge-rollup'
620
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-block-merge'
557
621
  }, (signal)=>this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
558
622
  provingState.setBlockMergeRollupProof(location, result);
559
623
  this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
560
624
  });
561
625
  }
626
+ enqueueCheckpointRootRollup(provingState) {
627
+ if (!provingState.verifyState()) {
628
+ logger.debug('Not running checkpoint root rollup. State no longer valid.');
629
+ return;
630
+ }
631
+ if (!provingState.tryStartProvingCheckpointRoot()) {
632
+ logger.debug('Checkpoint root rollup already started.');
633
+ return;
634
+ }
635
+ const rollupType = provingState.getCheckpointRootRollupType();
636
+ logger.debug(`Enqueuing ${rollupType} for checkpoint ${provingState.index}.`);
637
+ this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointRootRollupProof', {
638
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType
639
+ }, async (signal)=>{
640
+ const inputs = await provingState.getCheckpointRootRollupInputs();
641
+ if (inputs instanceof CheckpointRootSingleBlockRollupPrivateInputs) {
642
+ return this.prover.getCheckpointRootSingleBlockRollupProof(inputs, signal, provingState.epochNumber);
643
+ } else {
644
+ return this.prover.getCheckpointRootRollupProof(inputs, signal, provingState.epochNumber);
645
+ }
646
+ }), (result)=>{
647
+ const computedEndBlobAccumulatorState = BlobAccumulatorPublicInputs.fromBatchedBlobAccumulator(provingState.getEndBlobAccumulator());
648
+ const circuitEndBlobAccumulatorState = result.inputs.endBlobAccumulator;
649
+ if (!circuitEndBlobAccumulatorState.equals(computedEndBlobAccumulatorState)) {
650
+ logger.error(`Blob accumulator state mismatch.\nCircuit: ${inspect(circuitEndBlobAccumulatorState)}\nComputed: ${inspect(computedEndBlobAccumulatorState)}`);
651
+ provingState.reject(`Blob accumulator state mismatch.`);
652
+ return;
653
+ }
654
+ logger.debug(`Completed ${rollupType} proof for checkpoint ${provingState.index}.`);
655
+ const leafLocation = provingState.setCheckpointRootRollupProof(result);
656
+ const epochProvingState = provingState.parentEpoch;
657
+ if (epochProvingState.totalNumCheckpoints === 1) {
658
+ this.enqueueEpochPadding(epochProvingState);
659
+ } else {
660
+ this.checkAndEnqueueNextCheckpointMergeRollup(epochProvingState, leafLocation);
661
+ }
662
+ });
663
+ }
664
+ enqueueCheckpointMergeRollup(provingState, location) {
665
+ if (!provingState.verifyState()) {
666
+ logger.debug('Not running checkpoint merge rollup. State no longer valid.');
667
+ return;
668
+ }
669
+ if (!provingState.tryStartProvingCheckpointMerge(location)) {
670
+ logger.debug('Checkpoint merge rollup already started.');
671
+ return;
672
+ }
673
+ const inputs = provingState.getCheckpointMergeRollupInputs(location);
674
+ this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointMergeRollupProof', {
675
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-merge'
676
+ }, (signal)=>this.prover.getCheckpointMergeRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
677
+ logger.debug('Completed proof for checkpoint merge rollup.');
678
+ provingState.setCheckpointMergeRollupProof(location, result);
679
+ this.checkAndEnqueueNextCheckpointMergeRollup(provingState, location);
680
+ });
681
+ }
562
682
  enqueueEpochPadding(provingState) {
563
683
  if (!provingState.verifyState()) {
564
684
  logger.debug('Not running epoch padding. State no longer valid.');
565
685
  return;
566
686
  }
687
+ if (!provingState.tryStartProvingPaddingCheckpoint()) {
688
+ logger.debug('Padding checkpoint already started.');
689
+ return;
690
+ }
567
691
  logger.debug('Padding epoch proof with a padding block root proof.');
568
- const inputs = provingState.getPaddingBlockRootInputs();
569
- this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getPaddingBlockRootRollupProof', {
570
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'padding-block-root-rollup'
571
- }, (signal)=>this.prover.getPaddingBlockRootRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
572
- logger.debug('Completed proof for padding block root.');
573
- provingState.setPaddingBlockRootProof(result);
692
+ const inputs = provingState.getPaddingCheckpointInputs();
693
+ this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getCheckpointPaddingRollupProof', {
694
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-padding'
695
+ }, (signal)=>this.prover.getCheckpointPaddingRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
696
+ logger.debug('Completed proof for padding checkpoint.');
697
+ provingState.setCheckpointPaddingProof(result);
574
698
  this.checkAndEnqueueRootRollup(provingState);
575
699
  });
576
700
  }
@@ -583,7 +707,7 @@ const logger = createLogger('prover-client:orchestrator');
583
707
  logger.debug(`Preparing root rollup`);
584
708
  const inputs = provingState.getRootRollupInputs();
585
709
  this.deferredProving(provingState, wrapCallbackInSpan(this.tracer, 'ProvingOrchestrator.prover.getRootRollupProof', {
586
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup'
710
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-root'
587
711
  }, (signal)=>this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber)), (result)=>{
588
712
  logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
589
713
  provingState.setRootRollupProof(result);
@@ -592,36 +716,23 @@ const logger = createLogger('prover-client:orchestrator');
592
716
  });
593
717
  });
594
718
  }
595
- async checkAndEnqueueNextMergeRollup(provingState, currentLocation) {
719
+ checkAndEnqueueNextMergeRollup(provingState, currentLocation) {
596
720
  if (!provingState.isReadyForMergeRollup(currentLocation)) {
597
721
  return;
598
722
  }
599
723
  const parentLocation = provingState.getParentLocation(currentLocation);
600
724
  if (parentLocation.level === 0) {
601
- await this.checkAndEnqueueBlockRootRollup(provingState);
725
+ this.checkAndEnqueueBlockRootRollup(provingState);
602
726
  } else {
603
727
  this.enqueueMergeRollup(provingState, parentLocation);
604
728
  }
605
729
  }
606
- async checkAndEnqueueBlockRootRollup(provingState) {
607
- const blockNumber = provingState.blockNumber;
608
- // Accumulate as far as we can, in case blocks came in out of order and we are behind:
609
- await this.provingState?.setBlobAccumulators(blockNumber);
730
+ checkAndEnqueueBlockRootRollup(provingState) {
610
731
  if (!provingState.isReadyForBlockRootRollup()) {
611
732
  logger.debug('Not ready for block root rollup');
612
733
  return;
613
734
  }
614
- if (provingState.blockRootRollupStarted) {
615
- logger.debug('Block root rollup already started');
616
- return;
617
- }
618
- // TODO(palla/prover): This closes the fork only on the happy path. If this epoch orchestrator
619
- // is aborted and never reaches this point, it will leak the fork. We need to add a global cleanup,
620
- // but have to make sure it only runs once all operations are completed, otherwise some function here
621
- // will attempt to access the fork after it was closed.
622
- logger.debug(`Cleaning up world state fork for ${blockNumber}`);
623
- void this.dbs.get(blockNumber)?.close().then(()=>this.dbs.delete(blockNumber)).catch((err)=>logger.error(`Error closing db for block ${blockNumber}`, err));
624
- await this.enqueueBlockRootRollup(provingState);
735
+ this.enqueueBlockRootRollup(provingState);
625
736
  }
626
737
  checkAndEnqueueNextBlockMergeRollup(provingState, currentLocation) {
627
738
  if (!provingState.isReadyForBlockMerge(currentLocation)) {
@@ -629,11 +740,28 @@ const logger = createLogger('prover-client:orchestrator');
629
740
  }
630
741
  const parentLocation = provingState.getParentLocation(currentLocation);
631
742
  if (parentLocation.level === 0) {
632
- this.checkAndEnqueueRootRollup(provingState);
743
+ this.checkAndEnqueueCheckpointRootRollup(provingState);
633
744
  } else {
634
745
  this.enqueueBlockMergeRollup(provingState, parentLocation);
635
746
  }
636
747
  }
748
+ checkAndEnqueueCheckpointRootRollup(provingState) {
749
+ if (!provingState.isReadyForCheckpointRoot()) {
750
+ return;
751
+ }
752
+ this.enqueueCheckpointRootRollup(provingState);
753
+ }
754
+ checkAndEnqueueNextCheckpointMergeRollup(provingState, currentLocation) {
755
+ if (!provingState.isReadyForCheckpointMerge(currentLocation)) {
756
+ return;
757
+ }
758
+ const parentLocation = provingState.getParentLocation(currentLocation);
759
+ if (parentLocation.level === 0) {
760
+ this.checkAndEnqueueRootRollup(provingState);
761
+ } else {
762
+ this.enqueueCheckpointMergeRollup(provingState, parentLocation);
763
+ }
764
+ }
637
765
  checkAndEnqueueRootRollup(provingState) {
638
766
  if (!provingState.isReadyForRootRollup()) {
639
767
  logger.debug('Not ready for root rollup');
@@ -697,8 +825,8 @@ const logger = createLogger('prover-client:orchestrator');
697
825
  }
698
826
  }
699
827
  _ts_decorate([
700
- trackSpan('ProvingOrchestrator.startNewBlock', (globalVariables)=>({
701
- [Attributes.BLOCK_NUMBER]: globalVariables.blockNumber
828
+ trackSpan('ProvingOrchestrator.startNewBlock', (blockNumber)=>({
829
+ [Attributes.BLOCK_NUMBER]: blockNumber
702
830
  }))
703
831
  ], ProvingOrchestrator.prototype, "startNewBlock", null);
704
832
  _ts_decorate([
@@ -715,7 +843,7 @@ _ts_decorate([
715
843
  }))
716
844
  ], ProvingOrchestrator.prototype, "setBlockCompleted", null);
717
845
  _ts_decorate([
718
- trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', (_, tx)=>({
846
+ trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', (tx)=>({
719
847
  [Attributes.TX_HASH]: tx.hash.toString()
720
848
  }))
721
849
  ], ProvingOrchestrator.prototype, "prepareBaseRollupInputs", null);