@aztec/prover-node 0.0.1-commit.b655e406 → 0.0.1-commit.b6e433891

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 (56) hide show
  1. package/dest/actions/download-epoch-proving-job.d.ts +4 -4
  2. package/dest/actions/index.d.ts +1 -1
  3. package/dest/actions/rerun-epoch-proving-job.d.ts +4 -3
  4. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  5. package/dest/actions/rerun-epoch-proving-job.js +2 -2
  6. package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
  7. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
  8. package/dest/bin/run-failed-epoch.d.ts +1 -1
  9. package/dest/bin/run-failed-epoch.js +5 -2
  10. package/dest/config.d.ts +8 -10
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +19 -21
  13. package/dest/factory.d.ts +20 -16
  14. package/dest/factory.d.ts.map +1 -1
  15. package/dest/factory.js +28 -62
  16. package/dest/index.d.ts +2 -1
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +1 -0
  19. package/dest/job/epoch-proving-job-data.d.ts +8 -6
  20. package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
  21. package/dest/job/epoch-proving-job-data.js +25 -18
  22. package/dest/job/epoch-proving-job.d.ts +7 -13
  23. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  24. package/dest/job/epoch-proving-job.js +513 -105
  25. package/dest/metrics.d.ts +14 -3
  26. package/dest/metrics.d.ts.map +1 -1
  27. package/dest/metrics.js +55 -98
  28. package/dest/monitors/epoch-monitor.d.ts +3 -2
  29. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  30. package/dest/monitors/epoch-monitor.js +8 -18
  31. package/dest/monitors/index.d.ts +1 -1
  32. package/dest/prover-node-publisher.d.ts +14 -11
  33. package/dest/prover-node-publisher.d.ts.map +1 -1
  34. package/dest/prover-node-publisher.js +49 -42
  35. package/dest/prover-node.d.ts +27 -16
  36. package/dest/prover-node.d.ts.map +1 -1
  37. package/dest/prover-node.js +445 -59
  38. package/dest/prover-publisher-factory.d.ts +9 -5
  39. package/dest/prover-publisher-factory.d.ts.map +1 -1
  40. package/dest/prover-publisher-factory.js +4 -2
  41. package/dest/test/index.d.ts +1 -1
  42. package/dest/test/index.d.ts.map +1 -1
  43. package/package.json +27 -25
  44. package/src/actions/rerun-epoch-proving-job.ts +5 -3
  45. package/src/actions/upload-epoch-proof-failure.ts +1 -1
  46. package/src/bin/run-failed-epoch.ts +5 -2
  47. package/src/config.ts +27 -33
  48. package/src/factory.ts +64 -102
  49. package/src/index.ts +1 -0
  50. package/src/job/epoch-proving-job-data.ts +31 -25
  51. package/src/job/epoch-proving-job.ts +155 -110
  52. package/src/metrics.ts +65 -82
  53. package/src/monitors/epoch-monitor.ts +8 -15
  54. package/src/prover-node-publisher.ts +71 -58
  55. package/src/prover-node.ts +65 -53
  56. package/src/prover-publisher-factory.ts +16 -8
@@ -1,49 +1,55 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
3
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
- import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
4
+ import { CommitteeAttestation } from '@aztec/stdlib/block';
5
+ import { Checkpoint } from '@aztec/stdlib/checkpoint';
4
6
  import { BlockHeader, Tx } from '@aztec/stdlib/tx';
5
7
 
6
8
  /** All data from an epoch used in proving. */
7
9
  export type EpochProvingJobData = {
8
- epochNumber: bigint;
9
- blocks: L2Block[];
10
+ epochNumber: EpochNumber;
11
+ checkpoints: Checkpoint[];
10
12
  txs: Map<string, Tx>;
11
- l1ToL2Messages: Record<number, Fr[]>;
13
+ l1ToL2Messages: Record<CheckpointNumber, Fr[]>;
12
14
  previousBlockHeader: BlockHeader;
13
15
  attestations: CommitteeAttestation[];
14
16
  };
15
17
 
16
18
  export function validateEpochProvingJobData(data: EpochProvingJobData) {
17
- if (data.blocks.length > 0 && data.previousBlockHeader.getBlockNumber() + 1 !== data.blocks[0].number) {
19
+ if (data.checkpoints.length === 0) {
20
+ throw new Error('No checkpoints to prove');
21
+ }
22
+
23
+ const firstBlockNumber = data.checkpoints[0].blocks[0].number;
24
+ const previousBlockNumber = data.previousBlockHeader.getBlockNumber();
25
+ if (previousBlockNumber + 1 !== firstBlockNumber) {
18
26
  throw new Error(
19
- `Initial block number ${
20
- data.blocks[0].number
21
- } does not match previous block header ${data.previousBlockHeader.getBlockNumber()}`,
27
+ `Initial block number ${firstBlockNumber} does not match previous block header ${previousBlockNumber}`,
22
28
  );
23
29
  }
24
30
 
25
- for (const blockNumber of data.blocks.map(block => block.number)) {
26
- if (!(blockNumber in data.l1ToL2Messages)) {
27
- throw new Error(`Missing L1 to L2 messages for block number ${blockNumber}`);
31
+ for (const checkpoint of data.checkpoints) {
32
+ if (!(checkpoint.number in data.l1ToL2Messages)) {
33
+ throw new Error(`Missing L1 to L2 messages for checkpoint number ${checkpoint.number}`);
28
34
  }
29
35
  }
30
36
  }
31
37
 
32
38
  export function serializeEpochProvingJobData(data: EpochProvingJobData): Buffer {
33
- const blocks = data.blocks.map(block => block.toBuffer());
39
+ const checkpoints = data.checkpoints.map(checkpoint => checkpoint.toBuffer());
34
40
  const txs = Array.from(data.txs.values()).map(tx => tx.toBuffer());
35
- const l1ToL2Messages = Object.entries(data.l1ToL2Messages).map(([blockNumber, messages]) => [
36
- Number(blockNumber),
41
+ const l1ToL2Messages = Object.entries(data.l1ToL2Messages).map(([checkpointNumber, messages]) => [
42
+ Number(checkpointNumber),
37
43
  messages.length,
38
44
  ...messages,
39
45
  ]);
40
46
  const attestations = data.attestations.map(attestation => attestation.toBuffer());
41
47
 
42
48
  return serializeToBuffer(
43
- Number(data.epochNumber),
49
+ data.epochNumber,
44
50
  data.previousBlockHeader,
45
- blocks.length,
46
- ...blocks,
51
+ checkpoints.length,
52
+ ...checkpoints,
47
53
  txs.length,
48
54
  ...txs,
49
55
  l1ToL2Messages.length,
@@ -55,22 +61,22 @@ export function serializeEpochProvingJobData(data: EpochProvingJobData): Buffer
55
61
 
56
62
  export function deserializeEpochProvingJobData(buf: Buffer): EpochProvingJobData {
57
63
  const reader = BufferReader.asReader(buf);
58
- const epochNumber = BigInt(reader.readNumber());
64
+ const epochNumber = EpochNumber(reader.readNumber());
59
65
  const previousBlockHeader = reader.readObject(BlockHeader);
60
- const blocks = reader.readVector(L2Block);
66
+ const checkpoints = reader.readVector(Checkpoint);
61
67
  const txArray = reader.readVector(Tx);
62
68
 
63
- const l1ToL2MessageBlockCount = reader.readNumber();
69
+ const l1ToL2MessageCheckpointCount = reader.readNumber();
64
70
  const l1ToL2Messages: Record<number, Fr[]> = {};
65
- for (let i = 0; i < l1ToL2MessageBlockCount; i++) {
66
- const blockNumber = reader.readNumber();
71
+ for (let i = 0; i < l1ToL2MessageCheckpointCount; i++) {
72
+ const checkpointNumber = CheckpointNumber(reader.readNumber());
67
73
  const messages = reader.readVector(Fr);
68
- l1ToL2Messages[blockNumber] = messages;
74
+ l1ToL2Messages[checkpointNumber] = messages;
69
75
  }
70
76
 
71
77
  const attestations = reader.readVector(CommitteeAttestation);
72
78
 
73
79
  const txs = new Map<string, Tx>(txArray.map(tx => [tx.getTxHash().toString(), tx]));
74
80
 
75
- return { epochNumber, previousBlockHeader, blocks, txs, l1ToL2Messages, attestations };
81
+ return { epochNumber, previousBlockHeader, checkpoints, txs, l1ToL2Messages, attestations };
76
82
  }
@@ -1,15 +1,19 @@
1
1
  import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
2
  import { asyncPool } from '@aztec/foundation/async-pool';
3
+ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
3
4
  import { padArrayEnd } from '@aztec/foundation/collection';
4
- import { Fr } from '@aztec/foundation/fields';
5
- import { createLogger } from '@aztec/foundation/log';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
6
7
  import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
7
8
  import { Timer } from '@aztec/foundation/timer';
9
+ import { AVM_MAX_CONCURRENT_SIMULATIONS } from '@aztec/native';
8
10
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
9
11
  import { protocolContractsHash } from '@aztec/protocol-contracts';
10
12
  import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
11
13
  import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
14
+ import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
12
15
  import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
16
+ import type { Checkpoint } from '@aztec/stdlib/checkpoint';
13
17
  import {
14
18
  type EpochProver,
15
19
  type EpochProvingJobState,
@@ -40,7 +44,7 @@ export type EpochProvingJobOptions = {
40
44
  */
41
45
  export class EpochProvingJob implements Traceable {
42
46
  private state: EpochProvingJobState = 'initialized';
43
- private log = createLogger('prover-node:epoch-proving-job');
47
+ private log: Logger;
44
48
  private uuid: string;
45
49
 
46
50
  private runPromise: Promise<void> | undefined;
@@ -59,9 +63,14 @@ export class EpochProvingJob implements Traceable {
59
63
  private metrics: ProverNodeJobMetrics,
60
64
  private deadline: Date | undefined,
61
65
  private config: EpochProvingJobOptions,
66
+ bindings?: LoggerBindings,
62
67
  ) {
63
68
  validateEpochProvingJobData(data);
64
69
  this.uuid = crypto.randomUUID();
70
+ this.log = createLogger('prover-node:epoch-proving-job', {
71
+ ...bindings,
72
+ instanceId: `epoch-${data.epochNumber}`,
73
+ });
65
74
  this.tracer = metrics.tracer;
66
75
  }
67
76
 
@@ -73,7 +82,7 @@ export class EpochProvingJob implements Traceable {
73
82
  return this.state;
74
83
  }
75
84
 
76
- public getEpochNumber(): bigint {
85
+ public getEpochNumber(): EpochNumber {
77
86
  return this.data.epochNumber;
78
87
  }
79
88
 
@@ -89,8 +98,8 @@ export class EpochProvingJob implements Traceable {
89
98
  return this.data.epochNumber;
90
99
  }
91
100
 
92
- private get blocks() {
93
- return this.data.blocks;
101
+ private get checkpoints() {
102
+ return this.data.checkpoints;
94
103
  }
95
104
 
96
105
  private get txs() {
@@ -105,7 +114,7 @@ export class EpochProvingJob implements Traceable {
105
114
  * Proves the given epoch and submits the proof to L1.
106
115
  */
107
116
  @trackSpan('EpochProvingJob.run', function () {
108
- return { [Attributes.EPOCH_NUMBER]: Number(this.data.epochNumber) };
117
+ return { [Attributes.EPOCH_NUMBER]: this.data.epochNumber };
109
118
  })
110
119
  public async run() {
111
120
  this.scheduleDeadlineStop();
@@ -114,14 +123,22 @@ export class EpochProvingJob implements Traceable {
114
123
  }
115
124
 
116
125
  const attestations = this.attestations.map(attestation => attestation.toViem());
117
- const epochNumber = Number(this.epochNumber);
118
- const epochSizeBlocks = this.blocks.length;
119
- const epochSizeTxs = this.blocks.reduce((total, current) => total + current.body.txEffects.length, 0);
120
- const [fromBlock, toBlock] = [this.blocks[0].number, this.blocks.at(-1)!.number];
121
- this.log.info(`Starting epoch ${epochNumber} proving job with blocks ${fromBlock} to ${toBlock}`, {
126
+ const epochNumber = this.epochNumber;
127
+ const epochSizeCheckpoints = this.checkpoints.length;
128
+ const epochSizeBlocks = this.checkpoints.reduce((accum, checkpoint) => accum + checkpoint.blocks.length, 0);
129
+ const epochSizeTxs = this.checkpoints.reduce(
130
+ (accum, checkpoint) =>
131
+ accum + checkpoint.blocks.reduce((accumC, block) => accumC + block.body.txEffects.length, 0),
132
+ 0,
133
+ );
134
+ const fromCheckpoint = this.checkpoints[0].number;
135
+ const toCheckpoint = this.checkpoints.at(-1)!.number;
136
+ const fromBlock = this.checkpoints[0].blocks[0].number;
137
+ const toBlock = this.checkpoints.at(-1)!.blocks.at(-1)!.number;
138
+ this.log.info(`Starting epoch ${epochNumber} proving job with checkpoints ${fromCheckpoint} to ${toCheckpoint}`, {
122
139
  fromBlock,
123
140
  toBlock,
124
- epochSizeBlocks,
141
+ epochSizeTxs,
125
142
  epochNumber,
126
143
  uuid: this.uuid,
127
144
  });
@@ -132,84 +149,116 @@ export class EpochProvingJob implements Traceable {
132
149
  this.runPromise = promise;
133
150
 
134
151
  try {
135
- const blobFieldsPerCheckpoint = this.blocks.map(block => block.getCheckpointBlobFields());
152
+ const blobTimer = new Timer();
153
+ const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
136
154
  const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
155
+ this.metrics.recordBlobProcessing(blobTimer.ms());
137
156
 
138
- // TODO(#17027): Enable multiple blocks per checkpoint.
139
- // Total number of checkpoints equals number of blocks because we currently build a checkpoint with only one block.
140
- const totalNumCheckpoints = epochSizeBlocks;
141
-
142
- this.prover.startNewEpoch(epochNumber, totalNumCheckpoints, finalBlobBatchingChallenges);
157
+ this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
158
+ const chonkTimer = new Timer();
143
159
  await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
160
+ this.metrics.recordChonkVerifier(chonkTimer.ms());
144
161
 
145
- await asyncPool(this.config.parallelBlockLimit ?? 32, this.blocks, async block => {
146
- this.checkState();
162
+ // Everything in the epoch should have the same chainId and version.
163
+ const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
147
164
 
148
- const globalVariables = block.header.globalVariables;
149
- const txs = this.getTxs(block);
150
- const l1ToL2Messages = this.getL1ToL2Messages(block);
151
- const previousHeader = this.getBlockHeader(block.number - 1)!;
152
-
153
- this.log.verbose(`Starting processing block ${block.number}`, {
154
- number: block.number,
155
- blockHash: (await block.hash()).toString(),
156
- lastArchive: block.header.lastArchive.root,
157
- noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
158
- nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
159
- publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
160
- previousHeader: previousHeader.hash(),
161
- uuid: this.uuid,
162
- ...globalVariables,
163
- });
165
+ const previousBlockHeaders = this.gatherPreviousBlockHeaders();
166
+
167
+ const allCheckpointsTimer = new Timer();
168
+
169
+ const parallelism = this.config.parallelBlockLimit
170
+ ? this.config.parallelBlockLimit
171
+ : AVM_MAX_CONCURRENT_SIMULATIONS > 0
172
+ ? AVM_MAX_CONCURRENT_SIMULATIONS
173
+ : this.checkpoints.length;
164
174
 
175
+ await asyncPool(parallelism, this.checkpoints, async checkpoint => {
176
+ this.checkState();
177
+ const checkpointTimer = new Timer();
178
+
179
+ const checkpointIndex = checkpoint.number - fromCheckpoint;
165
180
  const checkpointConstants = CheckpointConstantData.from({
166
- chainId: globalVariables.chainId,
167
- version: globalVariables.version,
181
+ chainId,
182
+ version,
168
183
  vkTreeRoot: getVKTreeRoot(),
169
184
  protocolContractsHash: protocolContractsHash,
170
185
  proverId: this.prover.getProverId().toField(),
171
- slotNumber: globalVariables.slotNumber,
172
- coinbase: globalVariables.coinbase,
173
- feeRecipient: globalVariables.feeRecipient,
174
- gasFees: globalVariables.gasFees,
186
+ slotNumber: checkpoint.header.slotNumber,
187
+ coinbase: checkpoint.header.coinbase,
188
+ feeRecipient: checkpoint.header.feeRecipient,
189
+ gasFees: checkpoint.header.gasFees,
190
+ });
191
+ const previousHeader = previousBlockHeaders[checkpointIndex];
192
+ const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
193
+
194
+ this.log.verbose(`Starting processing checkpoint ${checkpoint.number}`, {
195
+ number: checkpoint.number,
196
+ checkpointHash: checkpoint.hash().toString(),
197
+ lastArchive: checkpoint.header.lastArchiveRoot,
198
+ previousHeader: previousHeader.hash(),
199
+ uuid: this.uuid,
175
200
  });
176
201
 
177
- // TODO(#17027): Enable multiple blocks per checkpoint.
178
- // Each checkpoint has only one block.
179
- const totalNumBlocks = 1;
180
- const checkpointIndex = block.number - fromBlock;
181
202
  await this.prover.startNewCheckpoint(
182
203
  checkpointIndex,
183
204
  checkpointConstants,
184
205
  l1ToL2Messages,
185
- totalNumBlocks,
186
- blobFieldsPerCheckpoint[checkpointIndex].length,
206
+ checkpoint.blocks.length,
187
207
  previousHeader,
188
208
  );
189
209
 
190
- // Start block proving
191
- await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
210
+ for (let blockIndex = 0; blockIndex < checkpoint.blocks.length; blockIndex++) {
211
+ const blockTimer = new Timer();
212
+ const block = checkpoint.blocks[blockIndex];
213
+ const globalVariables = block.header.globalVariables;
214
+ const txs = this.getTxs(block);
215
+
216
+ this.log.verbose(`Starting processing block ${block.number}`, {
217
+ number: block.number,
218
+ blockHash: (await block.hash()).toString(),
219
+ lastArchive: block.header.lastArchive.root,
220
+ noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
221
+ nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
222
+ publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
223
+ ...globalVariables,
224
+ numTxs: txs.length,
225
+ });
192
226
 
193
- // Process public fns
194
- const db = await this.createFork(block.number - 1, l1ToL2Messages);
195
- const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, {
196
- skipFeeEnforcement: true,
197
- clientInitiatedSimulation: false,
198
- proverId: this.prover.getProverId().toField(),
199
- });
200
- const processed = await this.processTxs(publicProcessor, txs);
201
- await this.prover.addTxs(processed);
202
- await db.close();
203
- this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
204
- blockNumber: block.number,
205
- blockHash: (await block.hash()).toString(),
206
- uuid: this.uuid,
207
- });
227
+ // Start block proving
228
+ await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
229
+
230
+ // Process public fns. L1 to L2 messages are only inserted for the first block of a checkpoint,
231
+ // as the fork for subsequent blocks already includes them from the previous block's synced state.
232
+ const db = await this.createFork(
233
+ BlockNumber(block.number - 1),
234
+ blockIndex === 0 ? l1ToL2Messages : undefined,
235
+ );
236
+ const config = PublicSimulatorConfig.from({
237
+ proverId: this.prover.getProverId().toField(),
238
+ skipFeeEnforcement: false,
239
+ collectDebugLogs: false,
240
+ collectHints: true,
241
+ collectPublicInputs: true,
242
+ collectStatistics: false,
243
+ });
244
+ const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
245
+ const processed = await this.processTxs(publicProcessor, txs);
246
+ await this.prover.addTxs(processed);
247
+ await db.close();
248
+ this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
249
+ blockNumber: block.number,
250
+ blockHash: (await block.hash()).toString(),
251
+ uuid: this.uuid,
252
+ });
208
253
 
209
- // Mark block as completed to pad it
210
- const expectedBlockHeader = block.getBlockHeader();
211
- await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
254
+ // Mark block as completed to pad it
255
+ const expectedBlockHeader = block.header;
256
+ await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
257
+ this.metrics.recordBlockProcessing(blockTimer.ms());
258
+ }
259
+ this.metrics.recordCheckpointProcessing(checkpointTimer.ms());
212
260
  });
261
+ this.metrics.recordAllCheckpointsProcessing(allCheckpointsTimer.ms());
213
262
 
214
263
  const executionTime = timer.ms();
215
264
 
@@ -221,16 +270,16 @@ export class EpochProvingJob implements Traceable {
221
270
 
222
271
  if (this.config.skipSubmitProof) {
223
272
  this.log.info(
224
- `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`,
273
+ `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
225
274
  );
226
275
  this.state = 'completed';
227
- this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
276
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
228
277
  return;
229
278
  }
230
279
 
231
280
  const success = await this.publisher.submitEpochProof({
232
- fromBlock,
233
- toBlock,
281
+ fromCheckpoint,
282
+ toCheckpoint,
234
283
  epochNumber,
235
284
  publicInputs,
236
285
  proof,
@@ -241,12 +290,12 @@ export class EpochProvingJob implements Traceable {
241
290
  throw new Error('Failed to submit epoch proof to L1');
242
291
  }
243
292
 
244
- this.log.info(`Submitted proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`, {
293
+ this.log.info(`Submitted proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`, {
245
294
  epochNumber,
246
295
  uuid: this.uuid,
247
296
  });
248
297
  this.state = 'completed';
249
- this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
298
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
250
299
  } catch (err: any) {
251
300
  if (err && err.name === 'HaltExecutionError') {
252
301
  this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
@@ -269,22 +318,29 @@ export class EpochProvingJob implements Traceable {
269
318
  }
270
319
 
271
320
  /**
272
- * Create a new db fork for tx processing, inserting all L1 to L2.
321
+ * Create a new db fork for tx processing, optionally inserting L1 to L2 messages.
322
+ * L1 to L2 messages should only be inserted for the first block in a checkpoint,
323
+ * as subsequent blocks' synced state already includes them.
273
324
  * REFACTOR: The prover already spawns a db fork of its own for each block, so we may be able to do away with just one fork.
274
325
  */
275
- private async createFork(blockNumber: number, l1ToL2Messages: Fr[]) {
326
+ private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[] | undefined) {
327
+ this.log.verbose(`Creating fork at ${blockNumber}`, { blockNumber });
276
328
  const db = await this.dbProvider.fork(blockNumber);
277
- const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
278
- l1ToL2Messages,
279
- Fr.ZERO,
280
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
281
- 'Too many L1 to L2 messages',
282
- );
283
- this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
284
- blockNumber,
285
- l1ToL2Messages: l1ToL2MessagesPadded.map(m => m.toString()),
286
- });
287
- await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
329
+
330
+ if (l1ToL2Messages !== undefined) {
331
+ this.log.verbose(`Inserting ${l1ToL2Messages.length} L1 to L2 messages in fork`, {
332
+ blockNumber,
333
+ l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
334
+ });
335
+ const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
336
+ l1ToL2Messages,
337
+ Fr.ZERO,
338
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
339
+ 'Too many L1 to L2 messages',
340
+ );
341
+ await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
342
+ }
343
+
288
344
  return db;
289
345
  }
290
346
 
@@ -341,11 +397,12 @@ export class EpochProvingJob implements Traceable {
341
397
  const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
342
398
  this.epochCheckPromise = new RunningPromise(
343
399
  async () => {
344
- const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
345
- const blockHashes = await Promise.all(blocks.map(block => block.hash()));
346
- const thisBlockHashes = await Promise.all(this.blocks.map(block => block.hash()));
400
+ const blockHeaders = await l2BlockSource.getCheckpointedBlockHeadersForEpoch(this.epochNumber);
401
+ const blockHashes = await Promise.all(blockHeaders.map(header => header.hash()));
402
+ const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
403
+ const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
347
404
  if (
348
- blocks.length !== this.blocks.length ||
405
+ blockHeaders.length !== thisBlocks.length ||
349
406
  !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))
350
407
  ) {
351
408
  this.log.warn('Epoch blocks changed underfoot', {
@@ -363,30 +420,18 @@ export class EpochProvingJob implements Traceable {
363
420
  this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
364
421
  }
365
422
 
366
- /* Returns the header for the given block number based on the epoch proving job data. */
367
- private getBlockHeader(blockNumber: number) {
368
- const block = this.blocks.find(b => b.number === blockNumber);
369
- if (block) {
370
- return block.getBlockHeader();
371
- }
372
-
373
- if (blockNumber === Number(this.data.previousBlockHeader.getBlockNumber())) {
374
- return this.data.previousBlockHeader;
375
- }
376
-
377
- throw new Error(
378
- `Block header not found for block number ${blockNumber} (got ${this.blocks
379
- .map(b => b.number)
380
- .join(', ')} and previous header ${this.data.previousBlockHeader.getBlockNumber()})`,
381
- );
423
+ /* Returns the last block header in the previous checkpoint for all checkpoints in the epoch */
424
+ private gatherPreviousBlockHeaders() {
425
+ const lastBlocks = this.checkpoints.map(checkpoint => checkpoint.blocks.at(-1)!);
426
+ return [this.data.previousBlockHeader, ...lastBlocks.map(block => block.header).slice(0, -1)];
382
427
  }
383
428
 
384
429
  private getTxs(block: L2Block): Tx[] {
385
430
  return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
386
431
  }
387
432
 
388
- private getL1ToL2Messages(block: L2Block) {
389
- return this.data.l1ToL2Messages[block.number];
433
+ private getL1ToL2Messages(checkpoint: Checkpoint) {
434
+ return this.data.l1ToL2Messages[checkpoint.number];
390
435
  }
391
436
 
392
437
  private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {