@aztec/prover-node 0.0.0-test.1 → 0.0.1-commit.1142ef1

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 (82) hide show
  1. package/dest/actions/download-epoch-proving-job.d.ts +18 -0
  2. package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
  3. package/dest/actions/download-epoch-proving-job.js +37 -0
  4. package/dest/actions/index.d.ts +3 -0
  5. package/dest/actions/index.d.ts.map +1 -0
  6. package/dest/actions/index.js +2 -0
  7. package/dest/actions/rerun-epoch-proving-job.d.ts +11 -0
  8. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
  9. package/dest/actions/rerun-epoch-proving-job.js +40 -0
  10. package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
  11. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
  12. package/dest/actions/upload-epoch-proof-failure.js +78 -0
  13. package/dest/bin/run-failed-epoch.d.ts +2 -0
  14. package/dest/bin/run-failed-epoch.d.ts.map +1 -0
  15. package/dest/bin/run-failed-epoch.js +67 -0
  16. package/dest/config.d.ts +15 -11
  17. package/dest/config.d.ts.map +1 -1
  18. package/dest/config.js +83 -15
  19. package/dest/factory.d.ts +14 -12
  20. package/dest/factory.d.ts.map +1 -1
  21. package/dest/factory.js +105 -32
  22. package/dest/index.d.ts +3 -2
  23. package/dest/index.d.ts.map +1 -1
  24. package/dest/index.js +2 -1
  25. package/dest/job/epoch-proving-job-data.d.ts +18 -0
  26. package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
  27. package/dest/job/epoch-proving-job-data.js +59 -0
  28. package/dest/job/epoch-proving-job.d.ts +26 -18
  29. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  30. package/dest/job/epoch-proving-job.js +579 -96
  31. package/dest/metrics.d.ts +30 -5
  32. package/dest/metrics.d.ts.map +1 -1
  33. package/dest/metrics.js +103 -73
  34. package/dest/monitors/epoch-monitor.d.ts +5 -2
  35. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  36. package/dest/monitors/epoch-monitor.js +18 -13
  37. package/dest/monitors/index.d.ts +1 -1
  38. package/dest/prover-node-publisher.d.ts +14 -15
  39. package/dest/prover-node-publisher.d.ts.map +1 -1
  40. package/dest/prover-node-publisher.js +86 -81
  41. package/dest/prover-node.d.ts +49 -44
  42. package/dest/prover-node.d.ts.map +1 -1
  43. package/dest/prover-node.js +581 -129
  44. package/dest/prover-publisher-factory.d.ts +23 -0
  45. package/dest/prover-publisher-factory.d.ts.map +1 -0
  46. package/dest/prover-publisher-factory.js +26 -0
  47. package/dest/test/index.d.ts +5 -3
  48. package/dest/test/index.d.ts.map +1 -1
  49. package/dest/test/index.js +1 -3
  50. package/package.json +39 -33
  51. package/src/actions/download-epoch-proving-job.ts +44 -0
  52. package/src/actions/index.ts +2 -0
  53. package/src/actions/rerun-epoch-proving-job.ts +61 -0
  54. package/src/actions/upload-epoch-proof-failure.ts +88 -0
  55. package/src/bin/run-failed-epoch.ts +77 -0
  56. package/src/config.ts +111 -25
  57. package/src/factory.ts +176 -44
  58. package/src/index.ts +2 -1
  59. package/src/job/epoch-proving-job-data.ts +82 -0
  60. package/src/job/epoch-proving-job.ts +255 -83
  61. package/src/metrics.ts +116 -73
  62. package/src/monitors/epoch-monitor.ts +20 -15
  63. package/src/prover-node-publisher.ts +133 -115
  64. package/src/prover-node.ts +223 -142
  65. package/src/prover-publisher-factory.ts +39 -0
  66. package/src/test/index.ts +7 -4
  67. package/dest/http.d.ts +0 -8
  68. package/dest/http.d.ts.map +0 -1
  69. package/dest/http.js +0 -9
  70. package/dest/prover-coordination/config.d.ts +0 -7
  71. package/dest/prover-coordination/config.d.ts.map +0 -1
  72. package/dest/prover-coordination/config.js +0 -11
  73. package/dest/prover-coordination/factory.d.ts +0 -22
  74. package/dest/prover-coordination/factory.d.ts.map +0 -1
  75. package/dest/prover-coordination/factory.js +0 -42
  76. package/dest/prover-coordination/index.d.ts +0 -3
  77. package/dest/prover-coordination/index.d.ts.map +0 -1
  78. package/dest/prover-coordination/index.js +0 -2
  79. package/src/http.ts +0 -13
  80. package/src/prover-coordination/config.ts +0 -17
  81. package/src/prover-coordination/factory.ts +0 -72
  82. package/src/prover-coordination/index.ts +0 -2
@@ -1,26 +1,43 @@
1
+ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
1
2
  import { asyncPool } from '@aztec/foundation/async-pool';
3
+ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
4
+ import { padArrayEnd } from '@aztec/foundation/collection';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
6
  import { createLogger } from '@aztec/foundation/log';
3
- import { promiseWithResolvers } from '@aztec/foundation/promise';
7
+ import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
4
8
  import { Timer } from '@aztec/foundation/timer';
9
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
11
+ import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
5
12
  import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
6
- import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
13
+ import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
14
+ import type { L2BlockNew, L2BlockSource } from '@aztec/stdlib/block';
15
+ import type { Checkpoint } from '@aztec/stdlib/checkpoint';
7
16
  import {
8
17
  type EpochProver,
9
18
  type EpochProvingJobState,
10
19
  EpochProvingJobTerminalState,
11
20
  type ForkMerkleTreeOperations,
12
21
  } from '@aztec/stdlib/interfaces/server';
13
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
22
+ import { CheckpointConstantData } from '@aztec/stdlib/rollup';
23
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
14
24
  import type { ProcessedTx, Tx } from '@aztec/stdlib/tx';
15
25
  import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
16
26
 
17
27
  import * as crypto from 'node:crypto';
18
28
 
19
- import type { ProverNodeMetrics } from '../metrics.js';
29
+ import type { ProverNodeJobMetrics } from '../metrics.js';
20
30
  import type { ProverNodePublisher } from '../prover-node-publisher.js';
31
+ import { type EpochProvingJobData, validateEpochProvingJobData } from './epoch-proving-job-data.js';
32
+
33
+ export type EpochProvingJobOptions = {
34
+ parallelBlockLimit?: number;
35
+ skipEpochCheck?: boolean;
36
+ skipSubmitProof?: boolean;
37
+ };
21
38
 
22
39
  /**
23
- * Job that grabs a range of blocks from the unfinalised chain from L1, gets their txs given their hashes,
40
+ * Job that grabs a range of blocks from the unfinalized chain from L1, gets their txs given their hashes,
24
41
  * re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
25
42
  * world state as part of public call execution via the public processor.
26
43
  */
@@ -30,27 +47,25 @@ export class EpochProvingJob implements Traceable {
30
47
  private uuid: string;
31
48
 
32
49
  private runPromise: Promise<void> | undefined;
50
+ private epochCheckPromise: RunningPromise | undefined;
33
51
  private deadlineTimeoutHandler: NodeJS.Timeout | undefined;
34
52
 
35
53
  public readonly tracer: Tracer;
36
54
 
37
55
  constructor(
38
- private dbProvider: ForkMerkleTreeOperations,
39
- private epochNumber: bigint,
40
- private blocks: L2Block[],
41
- private txs: Tx[],
56
+ private data: EpochProvingJobData,
57
+ private dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>,
42
58
  private prover: EpochProver,
43
59
  private publicProcessorFactory: PublicProcessorFactory,
44
- private publisher: ProverNodePublisher,
45
- private l2BlockSource: L2BlockSource,
46
- private l1ToL2MessageSource: L1ToL2MessageSource,
47
- private metrics: ProverNodeMetrics,
60
+ private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
61
+ private l2BlockSource: L2BlockSource | undefined,
62
+ private metrics: ProverNodeJobMetrics,
48
63
  private deadline: Date | undefined,
49
- private config: { parallelBlockLimit: number } = { parallelBlockLimit: 32 },
50
- private cleanUp: (job: EpochProvingJob) => Promise<void> = () => Promise.resolve(),
64
+ private config: EpochProvingJobOptions,
51
65
  ) {
66
+ validateEpochProvingJobData(data);
52
67
  this.uuid = crypto.randomUUID();
53
- this.tracer = metrics.client.getTracer('EpochProvingJob');
68
+ this.tracer = metrics.tracer;
54
69
  }
55
70
 
56
71
  public getId(): string {
@@ -61,27 +76,63 @@ export class EpochProvingJob implements Traceable {
61
76
  return this.state;
62
77
  }
63
78
 
64
- public getEpochNumber(): bigint {
65
- return this.epochNumber;
79
+ public getEpochNumber(): EpochNumber {
80
+ return this.data.epochNumber;
81
+ }
82
+
83
+ public getDeadline(): Date | undefined {
84
+ return this.deadline;
85
+ }
86
+
87
+ public getProvingData(): EpochProvingJobData {
88
+ return this.data;
89
+ }
90
+
91
+ private get epochNumber() {
92
+ return this.data.epochNumber;
93
+ }
94
+
95
+ private get checkpoints() {
96
+ return this.data.checkpoints;
97
+ }
98
+
99
+ private get txs() {
100
+ return this.data.txs;
101
+ }
102
+
103
+ private get attestations() {
104
+ return this.data.attestations;
66
105
  }
67
106
 
68
107
  /**
69
108
  * Proves the given epoch and submits the proof to L1.
70
109
  */
71
110
  @trackSpan('EpochProvingJob.run', function () {
72
- return { [Attributes.EPOCH_NUMBER]: Number(this.epochNumber) };
111
+ return { [Attributes.EPOCH_NUMBER]: this.data.epochNumber };
73
112
  })
74
113
  public async run() {
75
114
  this.scheduleDeadlineStop();
115
+ if (!this.config.skipEpochCheck) {
116
+ await this.scheduleEpochCheck();
117
+ }
76
118
 
77
- const epochNumber = Number(this.epochNumber);
78
- const epochSizeBlocks = this.blocks.length;
79
- const epochSizeTxs = this.blocks.reduce((total, current) => total + current.body.txEffects.length, 0);
80
- const [fromBlock, toBlock] = [this.blocks[0].number, this.blocks.at(-1)!.number];
81
- this.log.info(`Starting epoch ${epochNumber} proving job with blocks ${fromBlock} to ${toBlock}`, {
119
+ const attestations = this.attestations.map(attestation => attestation.toViem());
120
+ const epochNumber = this.epochNumber;
121
+ const epochSizeCheckpoints = this.checkpoints.length;
122
+ const epochSizeBlocks = this.checkpoints.reduce((accum, checkpoint) => accum + checkpoint.blocks.length, 0);
123
+ const epochSizeTxs = this.checkpoints.reduce(
124
+ (accum, checkpoint) =>
125
+ accum + checkpoint.blocks.reduce((accumC, block) => accumC + block.body.txEffects.length, 0),
126
+ 0,
127
+ );
128
+ const fromCheckpoint = this.checkpoints[0].number;
129
+ const toCheckpoint = this.checkpoints.at(-1)!.number;
130
+ const fromBlock = this.checkpoints[0].blocks[0].number;
131
+ const toBlock = this.checkpoints.at(-1)!.blocks.at(-1)!.number;
132
+ this.log.info(`Starting epoch ${epochNumber} proving job with checkpoints ${fromCheckpoint} to ${toCheckpoint}`, {
82
133
  fromBlock,
83
134
  toBlock,
84
- epochSizeBlocks,
135
+ epochSizeTxs,
85
136
  epochNumber,
86
137
  uuid: this.uuid,
87
138
  });
@@ -92,96 +143,186 @@ export class EpochProvingJob implements Traceable {
92
143
  this.runPromise = promise;
93
144
 
94
145
  try {
95
- this.prover.startNewEpoch(epochNumber, fromBlock, epochSizeBlocks);
96
- await this.prover.startTubeCircuits(this.txs);
146
+ const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
147
+ const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
148
+
149
+ this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
150
+ await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
151
+
152
+ // Everything in the epoch should have the same chainId and version.
153
+ const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
97
154
 
98
- await asyncPool(this.config.parallelBlockLimit, this.blocks, async block => {
155
+ const previousBlockHeaders = this.gatherPreviousBlockHeaders();
156
+
157
+ await asyncPool(this.config.parallelBlockLimit ?? 32, this.checkpoints, async checkpoint => {
99
158
  this.checkState();
100
159
 
101
- const globalVariables = block.header.globalVariables;
102
- const txs = await this.getTxs(block);
103
- const l1ToL2Messages = await this.getL1ToL2Messages(block);
104
- const previousHeader = (await this.getBlockHeader(block.number - 1))!;
105
-
106
- this.log.verbose(`Starting processing block ${block.number}`, {
107
- number: block.number,
108
- blockHash: (await block.hash()).toString(),
109
- lastArchive: block.header.lastArchive.root,
110
- noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
111
- nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
112
- publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
113
- previousHeader: previousHeader.hash(),
114
- uuid: this.uuid,
115
- ...globalVariables,
160
+ const checkpointIndex = checkpoint.number - fromCheckpoint;
161
+ const checkpointConstants = CheckpointConstantData.from({
162
+ chainId,
163
+ version,
164
+ vkTreeRoot: getVKTreeRoot(),
165
+ protocolContractsHash: protocolContractsHash,
166
+ proverId: this.prover.getProverId().toField(),
167
+ slotNumber: checkpoint.header.slotNumber,
168
+ coinbase: checkpoint.header.coinbase,
169
+ feeRecipient: checkpoint.header.feeRecipient,
170
+ gasFees: checkpoint.header.gasFees,
116
171
  });
172
+ const previousHeader = previousBlockHeaders[checkpointIndex];
173
+ const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
117
174
 
118
- // Start block proving
119
- await this.prover.startNewBlock(globalVariables, l1ToL2Messages, previousHeader);
120
-
121
- // Process public fns
122
- const db = await this.dbProvider.fork(block.number - 1);
123
- const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, true);
124
- const processed = await this.processTxs(publicProcessor, txs);
125
- await this.prover.addTxs(processed);
126
- await db.close();
127
- this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
128
- blockNumber: block.number,
129
- blockHash: (await block.hash()).toString(),
175
+ this.log.verbose(`Starting processing checkpoint ${checkpoint.number}`, {
176
+ number: checkpoint.number,
177
+ checkpointHash: checkpoint.hash().toString(),
178
+ lastArchive: checkpoint.header.lastArchiveRoot,
179
+ previousHeader: previousHeader.hash(),
130
180
  uuid: this.uuid,
131
181
  });
132
182
 
133
- // Mark block as completed to pad it
134
- await this.prover.setBlockCompleted(block.number, block.header);
183
+ await this.prover.startNewCheckpoint(
184
+ checkpointIndex,
185
+ checkpointConstants,
186
+ l1ToL2Messages,
187
+ checkpoint.blocks.length,
188
+ previousHeader,
189
+ );
190
+
191
+ for (const block of checkpoint.blocks) {
192
+ const globalVariables = block.header.globalVariables;
193
+ const txs = this.getTxs(block);
194
+
195
+ this.log.verbose(`Starting processing block ${block.number}`, {
196
+ number: block.number,
197
+ blockHash: (await block.hash()).toString(),
198
+ lastArchive: block.header.lastArchive.root,
199
+ noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
200
+ nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
201
+ publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
202
+ ...globalVariables,
203
+ numTxs: txs.length,
204
+ });
205
+
206
+ // Start block proving
207
+ await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
208
+
209
+ // Process public fns
210
+ const db = await this.createFork(BlockNumber(block.number - 1), l1ToL2Messages);
211
+ const config = PublicSimulatorConfig.from({
212
+ proverId: this.prover.getProverId().toField(),
213
+ skipFeeEnforcement: false,
214
+ collectDebugLogs: false,
215
+ collectHints: true,
216
+ collectPublicInputs: true,
217
+ collectStatistics: false,
218
+ });
219
+ const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
220
+ const processed = await this.processTxs(publicProcessor, txs);
221
+ await this.prover.addTxs(processed);
222
+ await db.close();
223
+ this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
224
+ blockNumber: block.number,
225
+ blockHash: (await block.hash()).toString(),
226
+ uuid: this.uuid,
227
+ });
228
+
229
+ // Mark block as completed to pad it
230
+ const expectedBlockHeader = block.header;
231
+ await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
232
+ }
135
233
  });
136
234
 
137
235
  const executionTime = timer.ms();
138
236
 
139
237
  this.progressState('awaiting-prover');
140
- const { publicInputs, proof } = await this.prover.finaliseEpoch();
141
- this.log.info(`Finalised proof for epoch ${epochNumber}`, { epochNumber, uuid: this.uuid, duration: timer.ms() });
238
+ const { publicInputs, proof, batchedBlobInputs } = await this.prover.finalizeEpoch();
239
+ this.log.info(`Finalized proof for epoch ${epochNumber}`, { epochNumber, uuid: this.uuid, duration: timer.ms() });
142
240
 
143
241
  this.progressState('publishing-proof');
144
- const success = await this.publisher.submitEpochProof({ fromBlock, toBlock, epochNumber, publicInputs, proof });
242
+
243
+ if (this.config.skipSubmitProof) {
244
+ this.log.info(
245
+ `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
246
+ );
247
+ this.state = 'completed';
248
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
249
+ return;
250
+ }
251
+
252
+ const success = await this.publisher.submitEpochProof({
253
+ fromCheckpoint,
254
+ toCheckpoint,
255
+ epochNumber,
256
+ publicInputs,
257
+ proof,
258
+ batchedBlobInputs,
259
+ attestations,
260
+ });
145
261
  if (!success) {
146
262
  throw new Error('Failed to submit epoch proof to L1');
147
263
  }
148
264
 
149
- this.log.info(`Submitted proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`, {
265
+ this.log.info(`Submitted proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`, {
150
266
  epochNumber,
151
267
  uuid: this.uuid,
152
268
  });
153
269
  this.state = 'completed';
154
- this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
270
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
155
271
  } catch (err: any) {
156
272
  if (err && err.name === 'HaltExecutionError') {
157
- this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, { uuid: this.uuid, epochNumber });
273
+ this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
274
+ uuid: this.uuid,
275
+ epochNumber,
276
+ details: err.message,
277
+ });
158
278
  return;
159
279
  }
160
280
  this.log.error(`Error running epoch ${epochNumber} prover job`, err, { uuid: this.uuid, epochNumber });
161
- this.state = 'failed';
281
+ if (this.state === 'processing' || this.state === 'awaiting-prover' || this.state === 'publishing-proof') {
282
+ this.state = 'failed';
283
+ }
162
284
  } finally {
163
285
  clearTimeout(this.deadlineTimeoutHandler);
164
- await this.cleanUp(this);
286
+ await this.epochCheckPromise?.stop();
165
287
  await this.prover.stop();
166
288
  resolve();
167
289
  }
168
290
  }
169
291
 
292
+ /**
293
+ * Create a new db fork for tx processing, inserting all L1 to L2.
294
+ * 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.
295
+ */
296
+ private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) {
297
+ const db = await this.dbProvider.fork(blockNumber);
298
+ const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
299
+ l1ToL2Messages,
300
+ Fr.ZERO,
301
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
302
+ 'Too many L1 to L2 messages',
303
+ );
304
+ this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
305
+ blockNumber,
306
+ l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
307
+ });
308
+ await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
309
+ return db;
310
+ }
311
+
170
312
  private progressState(state: EpochProvingJobState) {
171
313
  this.checkState();
172
314
  this.state = state;
173
315
  }
174
316
 
175
317
  private checkState() {
176
- if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed') {
318
+ if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed' || this.state === 'reorg') {
177
319
  throw new HaltExecutionError(this.state);
178
320
  }
179
321
  }
180
322
 
181
- public async stop(state: EpochProvingJobState = 'stopped') {
323
+ public async stop(state: EpochProvingJobTerminalState = 'stopped') {
182
324
  this.state = state;
183
325
  this.prover.cancel();
184
- // TODO(palla/prover): Stop the publisher as well
185
326
  if (this.runPromise) {
186
327
  await this.runPromise;
187
328
  }
@@ -207,24 +348,52 @@ export class EpochProvingJob implements Traceable {
207
348
  }
208
349
  }
209
350
 
210
- /* Returns the header for the given block number, or the genesis block for block zero. */
211
- private async getBlockHeader(blockNumber: number) {
212
- if (blockNumber === 0) {
213
- return (await this.dbProvider.fork()).getInitialHeader();
351
+ /**
352
+ * Kicks off a running promise that queries the archiver for the set of L2 blocks of the current epoch.
353
+ * If those change, stops the proving job with a `rerun` state, so the node re-enqueues it.
354
+ */
355
+ private async scheduleEpochCheck() {
356
+ const l2BlockSource = this.l2BlockSource;
357
+ if (!l2BlockSource) {
358
+ this.log.warn(`No L2 block source available, skipping epoch check`);
359
+ return;
214
360
  }
215
- return this.l2BlockSource.getBlockHeader(blockNumber);
361
+
362
+ const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
363
+ this.epochCheckPromise = new RunningPromise(
364
+ async () => {
365
+ const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
366
+ const blockHashes = await Promise.all(blocks.map(block => block.hash()));
367
+ const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
368
+ const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
369
+ if (blocks.length !== thisBlocks.length || !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))) {
370
+ this.log.warn('Epoch blocks changed underfoot', {
371
+ uuid: this.uuid,
372
+ epochNumber: this.epochNumber,
373
+ oldBlockHashes: thisBlockHashes,
374
+ newBlockHashes: blockHashes,
375
+ });
376
+ void this.stop('reorg');
377
+ }
378
+ },
379
+ this.log,
380
+ intervalMs,
381
+ ).start();
382
+ this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
383
+ }
384
+
385
+ /* Returns the last block header in the previous checkpoint for all checkpoints in the epoch */
386
+ private gatherPreviousBlockHeaders() {
387
+ const lastBlocks = this.checkpoints.map(checkpoint => checkpoint.blocks.at(-1)!);
388
+ return [this.data.previousBlockHeader, ...lastBlocks.map(block => block.header).slice(0, -1)];
216
389
  }
217
390
 
218
- private async getTxs(block: L2Block): Promise<Tx[]> {
219
- const txHashes = block.body.txEffects.map(tx => tx.txHash.toBigInt());
220
- const txsAndHashes = await Promise.all(this.txs.map(async tx => ({ tx, hash: await tx.getTxHash() })));
221
- return txsAndHashes
222
- .filter(txAndHash => txHashes.includes(txAndHash.hash.toBigInt()))
223
- .map(txAndHash => txAndHash.tx);
391
+ private getTxs(block: L2BlockNew): Tx[] {
392
+ return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
224
393
  }
225
394
 
226
- private getL1ToL2Messages(block: L2Block) {
227
- return this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(block.number));
395
+ private getL1ToL2Messages(checkpoint: Checkpoint) {
396
+ return this.data.l1ToL2Messages[checkpoint.number];
228
397
  }
229
398
 
230
399
  private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
@@ -232,8 +401,11 @@ export class EpochProvingJob implements Traceable {
232
401
  const [processedTxs, failedTxs] = await publicProcessor.process(txs, { deadline });
233
402
 
234
403
  if (failedTxs.length) {
404
+ const failedTxHashes = await Promise.all(failedTxs.map(({ tx }) => tx.getTxHash()));
235
405
  throw new Error(
236
- `Txs failed processing: ${failedTxs.map(({ tx, error }) => `${tx.getTxHash()} (${error})`).join(', ')}`,
406
+ `Txs failed processing: ${failedTxs
407
+ .map(({ error }, index) => `${failedTxHashes[index]} (${error})`)
408
+ .join(', ')}`,
237
409
  );
238
410
  }
239
411
 
@@ -246,7 +418,7 @@ export class EpochProvingJob implements Traceable {
246
418
  }
247
419
 
248
420
  class HaltExecutionError extends Error {
249
- constructor(state: EpochProvingJobState) {
421
+ constructor(public readonly state: EpochProvingJobState) {
250
422
  super(`Halted execution due to state ${state}`);
251
423
  this.name = 'HaltExecutionError';
252
424
  }