@aztec/prover-client 0.0.0-test.1 → 0.0.1-commit.001888fc

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