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

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 (46) 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 +2 -2
  4. package/dest/actions/upload-epoch-proof-failure.d.ts +1 -1
  5. package/dest/bin/run-failed-epoch.d.ts +1 -1
  6. package/dest/config.d.ts +5 -4
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +4 -3
  9. package/dest/factory.d.ts +4 -4
  10. package/dest/factory.d.ts.map +1 -1
  11. package/dest/factory.js +11 -7
  12. package/dest/index.d.ts +1 -1
  13. package/dest/job/epoch-proving-job-data.d.ts +8 -6
  14. package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
  15. package/dest/job/epoch-proving-job-data.js +25 -18
  16. package/dest/job/epoch-proving-job.d.ts +5 -12
  17. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  18. package/dest/job/epoch-proving-job.js +93 -83
  19. package/dest/metrics.d.ts +4 -3
  20. package/dest/metrics.d.ts.map +1 -1
  21. package/dest/metrics.js +8 -2
  22. package/dest/monitors/epoch-monitor.d.ts +3 -2
  23. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  24. package/dest/monitors/epoch-monitor.js +2 -1
  25. package/dest/monitors/index.d.ts +1 -1
  26. package/dest/prover-node-publisher.d.ts +9 -7
  27. package/dest/prover-node-publisher.d.ts.map +1 -1
  28. package/dest/prover-node-publisher.js +43 -37
  29. package/dest/prover-node.d.ts +8 -7
  30. package/dest/prover-node.d.ts.map +1 -1
  31. package/dest/prover-node.js +35 -32
  32. package/dest/prover-publisher-factory.d.ts +4 -2
  33. package/dest/prover-publisher-factory.d.ts.map +1 -1
  34. package/dest/test/index.d.ts +1 -1
  35. package/dest/test/index.d.ts.map +1 -1
  36. package/package.json +26 -25
  37. package/src/bin/run-failed-epoch.ts +1 -1
  38. package/src/config.ts +6 -4
  39. package/src/factory.ts +13 -8
  40. package/src/job/epoch-proving-job-data.ts +31 -25
  41. package/src/job/epoch-proving-job.ts +107 -100
  42. package/src/metrics.ts +15 -3
  43. package/src/monitors/epoch-monitor.ts +4 -3
  44. package/src/prover-node-publisher.ts +62 -52
  45. package/src/prover-node.ts +47 -43
  46. package/src/prover-publisher-factory.ts +3 -1
@@ -1,7 +1,8 @@
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 { Fr } from '@aztec/foundation/curves/bn254';
5
6
  import { createLogger } from '@aztec/foundation/log';
6
7
  import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
7
8
  import { Timer } from '@aztec/foundation/timer';
@@ -9,7 +10,9 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
9
10
  import { protocolContractsHash } from '@aztec/protocol-contracts';
10
11
  import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
11
12
  import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
12
- 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';
13
16
  import {
14
17
  type EpochProver,
15
18
  type EpochProvingJobState,
@@ -73,7 +76,7 @@ export class EpochProvingJob implements Traceable {
73
76
  return this.state;
74
77
  }
75
78
 
76
- public getEpochNumber(): bigint {
79
+ public getEpochNumber(): EpochNumber {
77
80
  return this.data.epochNumber;
78
81
  }
79
82
 
@@ -89,8 +92,8 @@ export class EpochProvingJob implements Traceable {
89
92
  return this.data.epochNumber;
90
93
  }
91
94
 
92
- private get blocks() {
93
- return this.data.blocks;
95
+ private get checkpoints() {
96
+ return this.data.checkpoints;
94
97
  }
95
98
 
96
99
  private get txs() {
@@ -105,7 +108,7 @@ export class EpochProvingJob implements Traceable {
105
108
  * Proves the given epoch and submits the proof to L1.
106
109
  */
107
110
  @trackSpan('EpochProvingJob.run', function () {
108
- return { [Attributes.EPOCH_NUMBER]: Number(this.data.epochNumber) };
111
+ return { [Attributes.EPOCH_NUMBER]: this.data.epochNumber };
109
112
  })
110
113
  public async run() {
111
114
  this.scheduleDeadlineStop();
@@ -114,14 +117,22 @@ export class EpochProvingJob implements Traceable {
114
117
  }
115
118
 
116
119
  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}`, {
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}`, {
122
133
  fromBlock,
123
134
  toBlock,
124
- epochSizeBlocks,
135
+ epochSizeTxs,
125
136
  epochNumber,
126
137
  uuid: this.uuid,
127
138
  });
@@ -132,83 +143,93 @@ export class EpochProvingJob implements Traceable {
132
143
  this.runPromise = promise;
133
144
 
134
145
  try {
135
- const blobFieldsPerCheckpoint = this.blocks.map(block => block.getCheckpointBlobFields());
146
+ const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
136
147
  const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
137
148
 
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);
149
+ this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
143
150
  await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
144
151
 
145
- await asyncPool(this.config.parallelBlockLimit ?? 32, this.blocks, async block => {
146
- this.checkState();
152
+ // Everything in the epoch should have the same chainId and version.
153
+ const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
147
154
 
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
- });
155
+ const previousBlockHeaders = this.gatherPreviousBlockHeaders();
164
156
 
157
+ await asyncPool(this.config.parallelBlockLimit ?? 32, this.checkpoints, async checkpoint => {
158
+ this.checkState();
159
+
160
+ const checkpointIndex = checkpoint.number - fromCheckpoint;
165
161
  const checkpointConstants = CheckpointConstantData.from({
166
- chainId: globalVariables.chainId,
167
- version: globalVariables.version,
162
+ chainId,
163
+ version,
168
164
  vkTreeRoot: getVKTreeRoot(),
169
165
  protocolContractsHash: protocolContractsHash,
170
166
  proverId: this.prover.getProverId().toField(),
171
- slotNumber: globalVariables.slotNumber,
172
- coinbase: globalVariables.coinbase,
173
- feeRecipient: globalVariables.feeRecipient,
174
- gasFees: globalVariables.gasFees,
167
+ slotNumber: checkpoint.header.slotNumber,
168
+ coinbase: checkpoint.header.coinbase,
169
+ feeRecipient: checkpoint.header.feeRecipient,
170
+ gasFees: checkpoint.header.gasFees,
171
+ });
172
+ const previousHeader = previousBlockHeaders[checkpointIndex];
173
+ const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
174
+
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(),
180
+ uuid: this.uuid,
175
181
  });
176
182
 
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
183
  await this.prover.startNewCheckpoint(
182
184
  checkpointIndex,
183
185
  checkpointConstants,
184
186
  l1ToL2Messages,
185
- totalNumBlocks,
186
- blobFieldsPerCheckpoint[checkpointIndex].length,
187
+ checkpoint.blocks.length,
187
188
  previousHeader,
188
189
  );
189
190
 
190
- // Start block proving
191
- await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
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
+ });
192
205
 
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
- });
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
+ });
208
228
 
209
- // Mark block as completed to pad it
210
- const expectedBlockHeader = block.getBlockHeader();
211
- await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
229
+ // Mark block as completed to pad it
230
+ const expectedBlockHeader = block.header;
231
+ await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
232
+ }
212
233
  });
213
234
 
214
235
  const executionTime = timer.ms();
@@ -221,16 +242,16 @@ export class EpochProvingJob implements Traceable {
221
242
 
222
243
  if (this.config.skipSubmitProof) {
223
244
  this.log.info(
224
- `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`,
245
+ `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
225
246
  );
226
247
  this.state = 'completed';
227
- this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
248
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
228
249
  return;
229
250
  }
230
251
 
231
252
  const success = await this.publisher.submitEpochProof({
232
- fromBlock,
233
- toBlock,
253
+ fromCheckpoint,
254
+ toCheckpoint,
234
255
  epochNumber,
235
256
  publicInputs,
236
257
  proof,
@@ -241,12 +262,12 @@ export class EpochProvingJob implements Traceable {
241
262
  throw new Error('Failed to submit epoch proof to L1');
242
263
  }
243
264
 
244
- 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})`, {
245
266
  epochNumber,
246
267
  uuid: this.uuid,
247
268
  });
248
269
  this.state = 'completed';
249
- this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
270
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
250
271
  } catch (err: any) {
251
272
  if (err && err.name === 'HaltExecutionError') {
252
273
  this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
@@ -272,7 +293,7 @@ export class EpochProvingJob implements Traceable {
272
293
  * Create a new db fork for tx processing, inserting all L1 to L2.
273
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.
274
295
  */
275
- private async createFork(blockNumber: number, l1ToL2Messages: Fr[]) {
296
+ private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) {
276
297
  const db = await this.dbProvider.fork(blockNumber);
277
298
  const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
278
299
  l1ToL2Messages,
@@ -282,7 +303,7 @@ export class EpochProvingJob implements Traceable {
282
303
  );
283
304
  this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
284
305
  blockNumber,
285
- l1ToL2Messages: l1ToL2MessagesPadded.map(m => m.toString()),
306
+ l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
286
307
  });
287
308
  await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
288
309
  return db;
@@ -343,11 +364,9 @@ export class EpochProvingJob implements Traceable {
343
364
  async () => {
344
365
  const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
345
366
  const blockHashes = await Promise.all(blocks.map(block => block.hash()));
346
- const thisBlockHashes = await Promise.all(this.blocks.map(block => block.hash()));
347
- if (
348
- blocks.length !== this.blocks.length ||
349
- !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))
350
- ) {
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]))) {
351
370
  this.log.warn('Epoch blocks changed underfoot', {
352
371
  uuid: this.uuid,
353
372
  epochNumber: this.epochNumber,
@@ -363,30 +382,18 @@ export class EpochProvingJob implements Traceable {
363
382
  this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
364
383
  }
365
384
 
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
- );
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)];
382
389
  }
383
390
 
384
- private getTxs(block: L2Block): Tx[] {
391
+ private getTxs(block: L2BlockNew): Tx[] {
385
392
  return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
386
393
  }
387
394
 
388
- private getL1ToL2Messages(block: L2Block) {
389
- return this.data.l1ToL2Messages[block.number];
395
+ private getL1ToL2Messages(checkpoint: Checkpoint) {
396
+ return this.data.l1ToL2Messages[checkpoint.number];
390
397
  }
391
398
 
392
399
  private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
package/src/metrics.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { RollupContract } from '@aztec/ethereum';
1
+ import type { RollupContract } from '@aztec/ethereum/contracts';
2
2
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import type { L1PublishProofStats, L1PublishStats } from '@aztec/stdlib/stats';
@@ -21,6 +21,7 @@ import { formatEther, formatUnits } from 'viem';
21
21
  export class ProverNodeJobMetrics {
22
22
  proverEpochExecutionDuration: Histogram;
23
23
  provingJobDuration: Histogram;
24
+ provingJobCheckpoints: Gauge;
24
25
  provingJobBlocks: Gauge;
25
26
  provingJobTransactions: Gauge;
26
27
 
@@ -39,6 +40,10 @@ export class ProverNodeJobMetrics {
39
40
  unit: 's',
40
41
  valueType: ValueType.DOUBLE,
41
42
  });
43
+ this.provingJobCheckpoints = this.meter.createGauge(Metrics.PROVER_NODE_JOB_CHECKPOINTS, {
44
+ description: 'Number of checkpoints in a proven epoch',
45
+ valueType: ValueType.INT,
46
+ });
42
47
  this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
43
48
  description: 'Number of blocks in a proven epoch',
44
49
  valueType: ValueType.INT,
@@ -49,9 +54,16 @@ export class ProverNodeJobMetrics {
49
54
  });
50
55
  }
51
56
 
52
- public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
57
+ public recordProvingJob(
58
+ executionTimeMs: number,
59
+ totalTimeMs: number,
60
+ numCheckpoints: number,
61
+ numBlocks: number,
62
+ numTxs: number,
63
+ ) {
53
64
  this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
54
65
  this.provingJobDuration.record(totalTimeMs / 1000);
66
+ this.provingJobCheckpoints.record(Math.floor(numCheckpoints));
55
67
  this.provingJobBlocks.record(Math.floor(numBlocks));
56
68
  this.provingJobTransactions.record(Math.floor(numTxs));
57
69
  }
@@ -97,7 +109,7 @@ export class ProverNodeRewardsMetrics {
97
109
  // look at the prev epoch so that we get an accurate value, after proof submission window has closed
98
110
  // For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
99
111
  // Similarly, if the proof submission window is 0, and we are in epoch 1, we should be looking at epoch 0.
100
- const closedEpoch = epoch - BigInt(this.proofSubmissionEpochs) - 1n;
112
+ const closedEpoch = BigInt(epoch) - BigInt(this.proofSubmissionEpochs) - 1n;
101
113
  const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
102
114
 
103
115
  const fmt = parseFloat(formatUnits(rewards, 18));
@@ -1,3 +1,4 @@
1
+ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
1
2
  import { createLogger } from '@aztec/foundation/log';
2
3
  import { RunningPromise } from '@aztec/foundation/running-promise';
3
4
  import { sleep } from '@aztec/foundation/sleep';
@@ -12,7 +13,7 @@ import {
12
13
  } from '@aztec/telemetry-client';
13
14
 
14
15
  export interface EpochMonitorHandler {
15
- handleEpochReadyToProve(epochNumber: bigint): Promise<boolean>;
16
+ handleEpochReadyToProve(epochNumber: EpochNumber): Promise<boolean>;
16
17
  }
17
18
 
18
19
  /**
@@ -32,7 +33,7 @@ export class EpochMonitor implements Traceable {
32
33
  public readonly tracer: Tracer;
33
34
 
34
35
  private handler: EpochMonitorHandler | undefined;
35
- private latestEpochNumber: bigint | undefined;
36
+ private latestEpochNumber: EpochNumber | undefined;
36
37
 
37
38
  constructor(
38
39
  private readonly l2BlockSource: L2BlockSource,
@@ -104,7 +105,7 @@ export class EpochMonitor implements Traceable {
104
105
 
105
106
  private async getEpochNumberToProve() {
106
107
  const lastBlockProven = await this.l2BlockSource.getProvenBlockNumber();
107
- const firstBlockToProve = lastBlockProven + 1;
108
+ const firstBlockToProve = BlockNumber(lastBlockProven + 1);
108
109
  const firstBlockHeaderToProve = await this.l2BlockSource.getBlockHeader(firstBlockToProve);
109
110
  if (!firstBlockHeaderToProve) {
110
111
  return { epochToProve: undefined, blockNumber: firstBlockToProve };
@@ -1,10 +1,12 @@
1
- import type { BatchedBlob } from '@aztec/blob-lib';
1
+ import { BatchedBlob, getEthBlobEvaluationInputs } from '@aztec/blob-lib';
2
2
  import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
3
- import type { L1TxUtils, RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum';
3
+ import type { RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum/contracts';
4
+ import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
4
5
  import { makeTuple } from '@aztec/foundation/array';
6
+ import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
5
7
  import { areArraysEqual } from '@aztec/foundation/collection';
8
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
9
  import { EthAddress } from '@aztec/foundation/eth-address';
7
- import { Fr } from '@aztec/foundation/fields';
8
10
  import { createLogger } from '@aztec/foundation/log';
9
11
  import type { Tuple } from '@aztec/foundation/serialize';
10
12
  import { Timer } from '@aztec/foundation/timer';
@@ -85,16 +87,16 @@ export class ProverNodePublisher {
85
87
  }
86
88
 
87
89
  public async submitEpochProof(args: {
88
- epochNumber: number;
89
- fromBlock: number;
90
- toBlock: number;
90
+ epochNumber: EpochNumber;
91
+ fromCheckpoint: CheckpointNumber;
92
+ toCheckpoint: CheckpointNumber;
91
93
  publicInputs: RootRollupPublicInputs;
92
94
  proof: Proof;
93
95
  batchedBlobInputs: BatchedBlob;
94
96
  attestations: ViemCommitteeAttestation[];
95
97
  }): Promise<boolean> {
96
- const { epochNumber, fromBlock, toBlock } = args;
97
- const ctx = { epochNumber, fromBlock, toBlock };
98
+ const { epochNumber, fromCheckpoint, toCheckpoint } = args;
99
+ const ctx = { epochNumber, fromCheckpoint, toCheckpoint };
98
100
 
99
101
  if (!this.interrupted) {
100
102
  const timer = new Timer();
@@ -103,6 +105,7 @@ export class ProverNodePublisher {
103
105
 
104
106
  const txReceipt = await this.sendSubmitEpochProofTx(args);
105
107
  if (!txReceipt) {
108
+ this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
106
109
  return false;
107
110
  }
108
111
 
@@ -135,47 +138,51 @@ export class ProverNodePublisher {
135
138
  }
136
139
 
137
140
  this.metrics.recordFailedTx();
138
- this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
141
+ this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
139
142
  }
140
143
 
141
- this.log.verbose('L2 block data syncing interrupted', ctx);
144
+ this.log.verbose('Checkpoint data syncing interrupted', ctx);
142
145
  return false;
143
146
  }
144
147
 
145
148
  private async validateEpochProofSubmission(args: {
146
- fromBlock: number;
147
- toBlock: number;
149
+ fromCheckpoint: CheckpointNumber;
150
+ toCheckpoint: CheckpointNumber;
148
151
  publicInputs: RootRollupPublicInputs;
149
152
  proof: Proof;
150
153
  batchedBlobInputs: BatchedBlob;
151
154
  attestations: ViemCommitteeAttestation[];
152
155
  }) {
153
- const { fromBlock, toBlock, publicInputs, batchedBlobInputs } = args;
156
+ const { fromCheckpoint, toCheckpoint, publicInputs, batchedBlobInputs } = args;
154
157
 
155
- // Check that the block numbers match the expected epoch to be proven
156
- const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
157
- // Don't publish if proven is beyond our toBlock, pointless to do so
158
- if (proven > BigInt(toBlock)) {
159
- throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
158
+ // Check that the checkpoint numbers match the expected epoch to be proven
159
+ const { pending, proven } = await this.rollupContract.getTips();
160
+ // Don't publish if proven is beyond our toCheckpoint, pointless to do so
161
+ if (proven > toCheckpoint) {
162
+ throw new Error(
163
+ `Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proven checkpoint is ${proven}`,
164
+ );
160
165
  }
161
- // toBlock can't be greater than pending
162
- if (toBlock > pending) {
163
- throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
166
+ // toCheckpoint can't be greater than pending
167
+ if (toCheckpoint > pending) {
168
+ throw new Error(
169
+ `Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as pending checkpoint is ${pending}`,
170
+ );
164
171
  }
165
172
 
166
- // Check the archive for the immediate block before the epoch
167
- const blockLog = await this.rollupContract.getBlock(BigInt(fromBlock - 1));
168
- if (publicInputs.previousArchiveRoot.toString() !== blockLog.archive) {
173
+ // Check the archive for the immediate checkpoint before the epoch
174
+ const checkpointLog = await this.rollupContract.getCheckpoint(CheckpointNumber(fromCheckpoint - 1));
175
+ if (publicInputs.previousArchiveRoot.toString() !== checkpointLog.archive) {
169
176
  throw new Error(
170
- `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${blockLog.archive}`,
177
+ `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive}`,
171
178
  );
172
179
  }
173
180
 
174
- // Check the archive for the last block in the epoch
175
- const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
176
- if (publicInputs.endArchiveRoot.toString() !== endBlockLog.archive) {
181
+ // Check the archive for the last checkpoint in the epoch
182
+ const endCheckpointLog = await this.rollupContract.getCheckpoint(toCheckpoint);
183
+ if (publicInputs.endArchiveRoot.toString() !== endCheckpointLog.archive) {
177
184
  throw new Error(
178
- `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endBlockLog.archive}`,
185
+ `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endCheckpointLog.archive}`,
179
186
  );
180
187
  }
181
188
 
@@ -202,8 +209,8 @@ export class ProverNodePublisher {
202
209
  }
203
210
 
204
211
  private async sendSubmitEpochProofTx(args: {
205
- fromBlock: number;
206
- toBlock: number;
212
+ fromCheckpoint: CheckpointNumber;
213
+ toCheckpoint: CheckpointNumber;
207
214
  publicInputs: RootRollupPublicInputs;
208
215
  proof: Proof;
209
216
  batchedBlobInputs: BatchedBlob;
@@ -213,8 +220,8 @@ export class ProverNodePublisher {
213
220
 
214
221
  this.log.info(`Submitting epoch proof to L1 rollup contract`, {
215
222
  proofSize: args.proof.withoutPublicInputs().length,
216
- fromBlock: args.fromBlock,
217
- toBlock: args.toBlock,
223
+ fromCheckpoint: args.fromCheckpoint,
224
+ toCheckpoint: args.toCheckpoint,
218
225
  });
219
226
  const data = encodeFunctionData({
220
227
  abi: RollupAbi,
@@ -223,36 +230,39 @@ export class ProverNodePublisher {
223
230
  });
224
231
  try {
225
232
  const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
233
+ if (receipt.status !== 'success') {
234
+ const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
235
+ data,
236
+ {
237
+ args: [...txArgs],
238
+ functionName: 'submitEpochRootProof',
239
+ abi: RollupAbi,
240
+ address: this.rollupContract.address,
241
+ },
242
+ /*blobInputs*/ undefined,
243
+ /*stateOverride*/ [],
244
+ );
245
+ this.log.error(`Rollup submit epoch proof tx reverted with ${errorMsg ?? 'unknown error'}`);
246
+ return undefined;
247
+ }
226
248
  return receipt;
227
249
  } catch (err) {
228
250
  this.log.error(`Rollup submit epoch proof failed`, err);
229
- const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
230
- data,
231
- {
232
- args: [...txArgs],
233
- functionName: 'submitEpochRootProof',
234
- abi: RollupAbi,
235
- address: this.rollupContract.address,
236
- },
237
- /*blobInputs*/ undefined,
238
- /*stateOverride*/ [],
239
- );
240
- this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
241
251
  return undefined;
242
252
  }
243
253
  }
244
254
 
245
255
  private getEpochProofPublicInputsArgs(args: {
246
- fromBlock: number;
247
- toBlock: number;
256
+ fromCheckpoint: CheckpointNumber;
257
+ toCheckpoint: CheckpointNumber;
248
258
  publicInputs: RootRollupPublicInputs;
249
259
  batchedBlobInputs: BatchedBlob;
250
260
  attestations: ViemCommitteeAttestation[];
251
261
  }) {
252
262
  // Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
253
263
  return [
254
- BigInt(args.fromBlock) /*_start*/,
255
- BigInt(args.toBlock) /*_end*/,
264
+ BigInt(args.fromCheckpoint) /*_start*/,
265
+ BigInt(args.toCheckpoint) /*_end*/,
256
266
  {
257
267
  previousArchive: args.publicInputs.previousArchiveRoot.toString(),
258
268
  endArchive: args.publicInputs.endArchiveRoot.toString(),
@@ -263,13 +273,13 @@ export class ProverNodePublisher {
263
273
  ? args.publicInputs.fees[i / 2].recipient.toField().toString()
264
274
  : args.publicInputs.fees[(i - 1) / 2].value.toString(),
265
275
  ) /*_fees*/,
266
- args.batchedBlobInputs.getEthBlobEvaluationInputs() /*_blobPublicInputs*/,
276
+ getEthBlobEvaluationInputs(args.batchedBlobInputs) /*_blobPublicInputs*/,
267
277
  ] as const;
268
278
  }
269
279
 
270
280
  private getSubmitEpochProofArgs(args: {
271
- fromBlock: number;
272
- toBlock: number;
281
+ fromCheckpoint: CheckpointNumber;
282
+ toCheckpoint: CheckpointNumber;
273
283
  publicInputs: RootRollupPublicInputs;
274
284
  proof: Proof;
275
285
  batchedBlobInputs: BatchedBlob;