@aztec/prover-client 0.0.0-test.1 → 0.0.1-commit.017a351

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