@aztec/prover-node 0.0.1-commit.5daedc8 → 0.0.1-commit.6201a7b05

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 (53) hide show
  1. package/dest/actions/download-epoch-proving-job.d.ts +1 -1
  2. package/dest/actions/download-epoch-proving-job.js +1 -1
  3. package/dest/actions/rerun-epoch-proving-job.d.ts +5 -3
  4. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  5. package/dest/actions/rerun-epoch-proving-job.js +6 -5
  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.js +5 -2
  9. package/dest/config.d.ts +8 -10
  10. package/dest/config.d.ts.map +1 -1
  11. package/dest/config.js +19 -21
  12. package/dest/factory.d.ts +20 -16
  13. package/dest/factory.d.ts.map +1 -1
  14. package/dest/factory.js +47 -62
  15. package/dest/index.d.ts +2 -1
  16. package/dest/index.d.ts.map +1 -1
  17. package/dest/index.js +1 -0
  18. package/dest/job/epoch-proving-job-data.d.ts +7 -6
  19. package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
  20. package/dest/job/epoch-proving-job-data.js +24 -18
  21. package/dest/job/epoch-proving-job.d.ts +5 -4
  22. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  23. package/dest/job/epoch-proving-job.js +524 -108
  24. package/dest/metrics.d.ts +24 -3
  25. package/dest/metrics.d.ts.map +1 -1
  26. package/dest/metrics.js +76 -97
  27. package/dest/monitors/epoch-monitor.d.ts +1 -1
  28. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  29. package/dest/monitors/epoch-monitor.js +8 -18
  30. package/dest/prover-node-publisher.d.ts +27 -10
  31. package/dest/prover-node-publisher.d.ts.map +1 -1
  32. package/dest/prover-node-publisher.js +97 -42
  33. package/dest/prover-node.d.ts +22 -12
  34. package/dest/prover-node.d.ts.map +1 -1
  35. package/dest/prover-node.js +442 -55
  36. package/dest/prover-publisher-factory.d.ts +10 -6
  37. package/dest/prover-publisher-factory.d.ts.map +1 -1
  38. package/dest/prover-publisher-factory.js +7 -5
  39. package/package.json +26 -25
  40. package/src/actions/download-epoch-proving-job.ts +1 -1
  41. package/src/actions/rerun-epoch-proving-job.ts +12 -5
  42. package/src/actions/upload-epoch-proof-failure.ts +1 -1
  43. package/src/bin/run-failed-epoch.ts +5 -2
  44. package/src/config.ts +27 -33
  45. package/src/factory.ts +80 -104
  46. package/src/index.ts +1 -0
  47. package/src/job/epoch-proving-job-data.ts +28 -23
  48. package/src/job/epoch-proving-job.ts +165 -112
  49. package/src/metrics.ts +101 -81
  50. package/src/monitors/epoch-monitor.ts +6 -14
  51. package/src/prover-node-publisher.ts +139 -59
  52. package/src/prover-node.ts +54 -42
  53. package/src/prover-publisher-factory.ts +19 -11
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';
@@ -13,7 +13,7 @@ import {
13
13
  type TelemetryClient,
14
14
  type Tracer,
15
15
  type UpDownCounter,
16
- ValueType,
16
+ createUpDownCounterWithDefault,
17
17
  } from '@aztec/telemetry-client';
18
18
 
19
19
  import { formatEther, formatUnits } from 'viem';
@@ -21,40 +21,69 @@ 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
 
28
+ private blobProcessingDuration: Gauge;
29
+ private chonkVerifierDuration: Gauge;
30
+ private blockProcessingDuration: Histogram;
31
+ private checkpointProcessingDuration: Histogram;
32
+ private allCheckpointsProcessingDuration: Gauge;
33
+
27
34
  constructor(
28
35
  private meter: Meter,
29
36
  public readonly tracer: Tracer,
30
37
  private logger = createLogger('prover-node:publisher:metrics'),
31
38
  ) {
32
- this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
33
- description: 'Duration of execution of an epoch by the prover',
34
- unit: 'ms',
35
- valueType: ValueType.INT,
36
- });
37
- this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
38
- description: 'Duration of proving job',
39
- unit: 's',
40
- valueType: ValueType.DOUBLE,
41
- });
42
- this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
43
- description: 'Number of blocks in a proven epoch',
44
- valueType: ValueType.INT,
45
- });
46
- this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
47
- description: 'Number of transactions in a proven epoch',
48
- valueType: ValueType.INT,
49
- });
39
+ this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION);
40
+ this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION);
41
+ this.provingJobCheckpoints = this.meter.createGauge(Metrics.PROVER_NODE_JOB_CHECKPOINTS);
42
+ this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS);
43
+ this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS);
44
+
45
+ this.blobProcessingDuration = this.meter.createGauge(Metrics.PROVER_NODE_BLOB_PROCESSING_LAST_DURATION);
46
+ this.chonkVerifierDuration = this.meter.createGauge(Metrics.PROVER_NODE_CHONK_VERIFIER_LAST_DURATION);
47
+ this.blockProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_BLOCK_PROCESSING_DURATION);
48
+ this.checkpointProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_PROCESSING_DURATION);
49
+ this.allCheckpointsProcessingDuration = this.meter.createGauge(
50
+ Metrics.PROVER_NODE_ALL_CHECKPOINTS_PROCESSING_LAST_DURATION,
51
+ );
50
52
  }
51
53
 
52
- public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
54
+ public recordProvingJob(
55
+ executionTimeMs: number,
56
+ totalTimeMs: number,
57
+ numCheckpoints: number,
58
+ numBlocks: number,
59
+ numTxs: number,
60
+ ) {
53
61
  this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
54
62
  this.provingJobDuration.record(totalTimeMs / 1000);
63
+ this.provingJobCheckpoints.record(Math.floor(numCheckpoints));
55
64
  this.provingJobBlocks.record(Math.floor(numBlocks));
56
65
  this.provingJobTransactions.record(Math.floor(numTxs));
57
66
  }
67
+
68
+ public recordBlobProcessing(durationMs: number) {
69
+ this.blobProcessingDuration.record(Math.ceil(durationMs));
70
+ }
71
+
72
+ public recordChonkVerifier(durationMs: number) {
73
+ this.chonkVerifierDuration.record(Math.ceil(durationMs));
74
+ }
75
+
76
+ public recordBlockProcessing(durationMs: number) {
77
+ this.blockProcessingDuration.record(Math.ceil(durationMs));
78
+ }
79
+
80
+ public recordCheckpointProcessing(durationMs: number) {
81
+ this.checkpointProcessingDuration.record(Math.ceil(durationMs));
82
+ }
83
+
84
+ public recordAllCheckpointsProcessing(durationMs: number) {
85
+ this.allCheckpointsProcessingDuration.record(Math.ceil(durationMs));
86
+ }
58
87
  }
59
88
 
60
89
  export class ProverNodeRewardsMetrics {
@@ -69,15 +98,9 @@ export class ProverNodeRewardsMetrics {
69
98
  private rollup: RollupContract,
70
99
  private logger = createLogger('prover-node:publisher:metrics'),
71
100
  ) {
72
- this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
73
- valueType: ValueType.DOUBLE,
74
- description: 'The rewards earned',
75
- });
101
+ this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH);
76
102
 
77
- this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
78
- valueType: ValueType.DOUBLE,
79
- description: 'The rewards earned (total)',
80
- });
103
+ this.accumulatedRewards = createUpDownCounterWithDefault(this.meter, Metrics.PROVER_NODE_REWARDS_TOTAL);
81
104
  }
82
105
 
83
106
  public async start() {
@@ -117,6 +140,13 @@ export class ProverNodeRewardsMetrics {
117
140
  };
118
141
  }
119
142
 
143
+ export type EstimatedSubmitProofStats = {
144
+ gasLimit: bigint;
145
+ baseFeePerGas: bigint;
146
+ maxPriorityFeePerGas: bigint;
147
+ estimatedTotalFee: bigint;
148
+ };
149
+
120
150
  export class ProverNodePublisherMetrics {
121
151
  gasPrice: Histogram;
122
152
  txCount: UpDownCounter;
@@ -128,6 +158,10 @@ export class ProverNodePublisherMetrics {
128
158
  txBlobDataGasCost: Histogram;
129
159
  txTotalFee: Histogram;
130
160
 
161
+ private txGasEstimated: Histogram;
162
+ private gasPriceEstimated: Histogram;
163
+ private txTotalFeeEstimated: Histogram;
164
+
131
165
  private senderBalance: Gauge;
132
166
  private meter: Meter;
133
167
 
@@ -138,68 +172,34 @@ export class ProverNodePublisherMetrics {
138
172
  ) {
139
173
  this.meter = client.getMeter(name);
140
174
 
141
- this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
142
- description: 'The gas price used for transactions',
143
- unit: 'gwei',
144
- valueType: ValueType.DOUBLE,
145
- });
175
+ this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE);
146
176
 
147
- this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
148
- description: 'The number of transactions processed',
177
+ this.txCount = createUpDownCounterWithDefault(this.meter, Metrics.L1_PUBLISHER_TX_COUNT, {
178
+ [Attributes.L1_TX_TYPE]: ['submitProof'],
179
+ [Attributes.OK]: [true, false],
149
180
  });
150
181
 
151
- this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
152
- description: 'The duration of transaction processing',
153
- unit: 'ms',
154
- valueType: ValueType.INT,
155
- });
182
+ this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION);
156
183
 
157
- this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
158
- description: 'The gas consumed by transactions',
159
- unit: 'gas',
160
- valueType: ValueType.INT,
161
- });
184
+ this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS);
162
185
 
163
- this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
164
- description: 'The size of the calldata in transactions',
165
- unit: 'By',
166
- valueType: ValueType.INT,
167
- });
186
+ this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE);
168
187
 
169
- this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
170
- description: 'The gas consumed by the calldata in transactions',
171
- unit: 'gas',
172
- valueType: ValueType.INT,
173
- });
188
+ this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS);
174
189
 
175
- this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
176
- description: 'The amount of blob gas used in transactions',
177
- unit: 'gas',
178
- valueType: ValueType.INT,
179
- });
190
+ this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED);
180
191
 
181
- this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
182
- description: 'The gas cost of blobs in transactions',
183
- unit: 'gwei',
184
- valueType: ValueType.INT,
185
- });
192
+ this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST);
186
193
 
187
- this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
188
- description: 'How much L1 tx costs',
189
- unit: 'gwei',
190
- valueType: ValueType.DOUBLE,
191
- advice: {
192
- explicitBucketBoundaries: [
193
- 0.001, 0.002, 0.004, 0.008, 0.01, 0.02, 0.04, 0.08, 0.1, 0.2, 0.4, 0.8, 1, 1.2, 1.4, 1.8, 2,
194
- ],
195
- },
196
- });
194
+ this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE);
197
195
 
198
- this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
199
- unit: 'eth',
200
- description: 'The balance of the sender address',
201
- valueType: ValueType.DOUBLE,
202
- });
196
+ this.txGasEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS);
197
+
198
+ this.gasPriceEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS_PRICE);
199
+
200
+ this.txTotalFeeEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_TOTAL_FEE);
201
+
202
+ this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE);
203
203
  }
204
204
 
205
205
  recordFailedTx() {
@@ -213,6 +213,26 @@ export class ProverNodePublisherMetrics {
213
213
  this.recordTx(durationMs, stats);
214
214
  }
215
215
 
216
+ public recordEstimatedSubmitProof(stats: EstimatedSubmitProofStats) {
217
+ const attributes = { [Attributes.L1_TX_TYPE]: 'submitProof' } as const;
218
+
219
+ this.txGasEstimated.record(Number(stats.gasLimit), attributes);
220
+
221
+ try {
222
+ this.gasPriceEstimated.record(
223
+ parseInt(formatEther(stats.baseFeePerGas + stats.maxPriorityFeePerGas, 'gwei'), 10),
224
+ );
225
+ } catch {
226
+ // ignore
227
+ }
228
+
229
+ try {
230
+ this.txTotalFeeEstimated.record(parseFloat(formatEther(stats.estimatedTotalFee)));
231
+ } catch {
232
+ // ignore
233
+ }
234
+ }
235
+
216
236
  public recordSenderBalance(wei: bigint, senderAddress: string) {
217
237
  const eth = parseFloat(formatEther(wei, 'wei'));
218
238
  this.senderBalance.record(eth, {
@@ -1,16 +1,10 @@
1
- import { EpochNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
  import { RunningPromise } from '@aztec/foundation/running-promise';
4
4
  import { sleep } from '@aztec/foundation/sleep';
5
5
  import type { L2BlockSource } from '@aztec/stdlib/block';
6
6
  import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
7
- import {
8
- type TelemetryClient,
9
- type Traceable,
10
- type Tracer,
11
- getTelemetryClient,
12
- trackSpan,
13
- } from '@aztec/telemetry-client';
7
+ import { type TelemetryClient, type Traceable, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
14
8
 
15
9
  export interface EpochMonitorHandler {
16
10
  handleEpochReadyToProve(epochNumber: EpochNumber): Promise<boolean>;
@@ -73,12 +67,10 @@ export class EpochMonitor implements Traceable {
73
67
  this.log.info('Stopped EpochMonitor');
74
68
  }
75
69
 
76
- @trackSpan('EpochMonitor.work')
77
70
  public async work() {
78
71
  const { epochToProve, blockNumber, slotNumber } = await this.getEpochNumberToProve();
79
- this.log.debug(`Epoch to prove: ${epochToProve}`, { blockNumber, slotNumber });
80
72
  if (epochToProve === undefined) {
81
- this.log.trace(`Next block to prove ${blockNumber} not yet mined`, { blockNumber });
73
+ this.log.trace(`Next block to prove ${blockNumber} not yet mined`, { epochToProve, blockNumber, slotNumber });
82
74
  return;
83
75
  }
84
76
  if (this.latestEpochNumber !== undefined && epochToProve <= this.latestEpochNumber) {
@@ -93,11 +85,11 @@ export class EpochMonitor implements Traceable {
93
85
  }
94
86
 
95
87
  if (this.options.provingDelayMs) {
96
- this.log.debug(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
88
+ this.log.warn(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
97
89
  await sleep(this.options.provingDelayMs);
98
90
  }
99
91
 
100
- this.log.debug(`Epoch ${epochToProve} is ready to be proven`);
92
+ this.log.verbose(`Epoch ${epochToProve} is ready to be proven`);
101
93
  if (await this.handler?.handleEpochReadyToProve(epochToProve)) {
102
94
  this.latestEpochNumber = epochToProve;
103
95
  }
@@ -105,7 +97,7 @@ export class EpochMonitor implements Traceable {
105
97
 
106
98
  private async getEpochNumberToProve() {
107
99
  const lastBlockProven = await this.l2BlockSource.getProvenBlockNumber();
108
- const firstBlockToProve = lastBlockProven + 1;
100
+ const firstBlockToProve = BlockNumber(lastBlockProven + 1);
109
101
  const firstBlockHeaderToProve = await this.l2BlockSource.getBlockHeader(firstBlockToProve);
110
102
  if (!firstBlockHeaderToProve) {
111
103
  return { epochToProve: undefined, blockNumber: firstBlockToProve };
@@ -1,12 +1,13 @@
1
1
  import { BatchedBlob, getEthBlobEvaluationInputs } from '@aztec/blob-lib';
2
- import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
3
- import type { L1TxUtils, RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum';
2
+ import { MAX_CHECKPOINTS_PER_EPOCH } from '@aztec/constants';
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';
5
- import { EpochNumber } from '@aztec/foundation/branded-types';
6
+ import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
6
7
  import { areArraysEqual } from '@aztec/foundation/collection';
8
+ import { Fr } from '@aztec/foundation/curves/bn254';
7
9
  import { EthAddress } from '@aztec/foundation/eth-address';
8
- import { Fr } from '@aztec/foundation/fields';
9
- import { createLogger } from '@aztec/foundation/log';
10
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
10
11
  import type { Tuple } from '@aztec/foundation/serialize';
11
12
  import { Timer } from '@aztec/foundation/timer';
12
13
  import { RollupAbi } from '@aztec/l1-artifacts';
@@ -18,9 +19,9 @@ import type { L1PublishProofStats } from '@aztec/stdlib/stats';
18
19
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
19
20
 
20
21
  import { inspect } from 'util';
21
- import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
22
+ import { type Hex, type TransactionReceipt, encodeFunctionData, formatEther, formatGwei } from 'viem';
22
23
 
23
- import { ProverNodePublisherMetrics } from './metrics.js';
24
+ import { type EstimatedSubmitProofStats, ProverNodePublisherMetrics } from './metrics.js';
24
25
 
25
26
  /** Arguments to the submitEpochProof method of the rollup contract */
26
27
  export type L1SubmitEpochProofArgs = {
@@ -30,7 +31,7 @@ export type L1SubmitEpochProofArgs = {
30
31
  endTimestamp: Fr;
31
32
  outHash: Fr;
32
33
  proverId: Fr;
33
- fees: Tuple<FeeRecipient, typeof AZTEC_MAX_EPOCH_DURATION>;
34
+ fees: Tuple<FeeRecipient, typeof MAX_CHECKPOINTS_PER_EPOCH>;
34
35
  proof: Proof;
35
36
  };
36
37
 
@@ -38,7 +39,7 @@ export class ProverNodePublisher {
38
39
  private interrupted = false;
39
40
  private metrics: ProverNodePublisherMetrics;
40
41
 
41
- protected log = createLogger('prover-node:l1-tx-publisher');
42
+ protected log: Logger;
42
43
 
43
44
  protected rollupContract: RollupContract;
44
45
 
@@ -51,10 +52,12 @@ export class ProverNodePublisher {
51
52
  l1TxUtils: L1TxUtils;
52
53
  telemetry?: TelemetryClient;
53
54
  },
55
+ bindings?: LoggerBindings,
54
56
  ) {
55
57
  const telemetry = deps.telemetry ?? getTelemetryClient();
56
58
 
57
59
  this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
60
+ this.log = createLogger('prover-node:l1-tx-publisher', bindings);
58
61
 
59
62
  this.rollupContract = deps.rollupContract;
60
63
  this.l1TxUtils = deps.l1TxUtils;
@@ -87,15 +90,15 @@ export class ProverNodePublisher {
87
90
 
88
91
  public async submitEpochProof(args: {
89
92
  epochNumber: EpochNumber;
90
- fromBlock: number;
91
- toBlock: number;
93
+ fromCheckpoint: CheckpointNumber;
94
+ toCheckpoint: CheckpointNumber;
92
95
  publicInputs: RootRollupPublicInputs;
93
96
  proof: Proof;
94
97
  batchedBlobInputs: BatchedBlob;
95
98
  attestations: ViemCommitteeAttestation[];
96
99
  }): Promise<boolean> {
97
- const { epochNumber, fromBlock, toBlock } = args;
98
- const ctx = { epochNumber, fromBlock, toBlock };
100
+ const { epochNumber, fromCheckpoint, toCheckpoint } = args;
101
+ const ctx = { epochNumber, fromCheckpoint, toCheckpoint };
99
102
 
100
103
  if (!this.interrupted) {
101
104
  const timer = new Timer();
@@ -104,6 +107,7 @@ export class ProverNodePublisher {
104
107
 
105
108
  const txReceipt = await this.sendSubmitEpochProofTx(args);
106
109
  if (!txReceipt) {
110
+ this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
107
111
  return false;
108
112
  }
109
113
 
@@ -136,47 +140,51 @@ export class ProverNodePublisher {
136
140
  }
137
141
 
138
142
  this.metrics.recordFailedTx();
139
- this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
143
+ this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
140
144
  }
141
145
 
142
- this.log.verbose('L2 block data syncing interrupted', ctx);
146
+ this.log.verbose('Checkpoint data syncing interrupted', ctx);
143
147
  return false;
144
148
  }
145
149
 
146
150
  private async validateEpochProofSubmission(args: {
147
- fromBlock: number;
148
- toBlock: number;
151
+ fromCheckpoint: CheckpointNumber;
152
+ toCheckpoint: CheckpointNumber;
149
153
  publicInputs: RootRollupPublicInputs;
150
154
  proof: Proof;
151
155
  batchedBlobInputs: BatchedBlob;
152
156
  attestations: ViemCommitteeAttestation[];
153
157
  }) {
154
- const { fromBlock, toBlock, publicInputs, batchedBlobInputs } = args;
158
+ const { fromCheckpoint, toCheckpoint, publicInputs, batchedBlobInputs } = args;
155
159
 
156
- // Check that the block numbers match the expected epoch to be proven
160
+ // Check that the checkpoint numbers match the expected epoch to be proven
157
161
  const { pending, proven } = await this.rollupContract.getTips();
158
- // Don't publish if proven is beyond our toBlock, pointless to do so
159
- if (proven > BigInt(toBlock)) {
160
- throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
162
+ // Don't publish if proven is beyond our toCheckpoint, pointless to do so
163
+ if (proven > toCheckpoint) {
164
+ throw new Error(
165
+ `Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proven checkpoint is ${proven}`,
166
+ );
161
167
  }
162
- // toBlock can't be greater than pending
163
- if (toBlock > pending) {
164
- throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
168
+ // toCheckpoint can't be greater than pending
169
+ if (toCheckpoint > pending) {
170
+ throw new Error(
171
+ `Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proposed checkpoint is ${pending}`,
172
+ );
165
173
  }
166
174
 
167
- // Check the archive for the immediate block before the epoch
168
- const blockLog = await this.rollupContract.getCheckpoint(BigInt(fromBlock - 1));
169
- if (publicInputs.previousArchiveRoot.toString() !== blockLog.archive) {
175
+ // Check the archive for the immediate checkpoint before the epoch
176
+ const checkpointLog = await this.rollupContract.getCheckpoint(CheckpointNumber(fromCheckpoint - 1));
177
+ if (!publicInputs.previousArchiveRoot.equals(checkpointLog.archive)) {
170
178
  throw new Error(
171
- `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${blockLog.archive}`,
179
+ `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive.toString()}`,
172
180
  );
173
181
  }
174
182
 
175
- // Check the archive for the last block in the epoch
176
- const endBlockLog = await this.rollupContract.getCheckpoint(BigInt(toBlock));
177
- if (publicInputs.endArchiveRoot.toString() !== endBlockLog.archive) {
183
+ // Check the archive for the last checkpoint in the epoch
184
+ const endCheckpointLog = await this.rollupContract.getCheckpoint(toCheckpoint);
185
+ if (!publicInputs.endArchiveRoot.equals(endCheckpointLog.archive)) {
178
186
  throw new Error(
179
- `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endBlockLog.archive}`,
187
+ `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endCheckpointLog.archive.toString()}`,
180
188
  );
181
189
  }
182
190
 
@@ -194,7 +202,7 @@ export class ProverNodePublisher {
194
202
  );
195
203
  const argsPublicInputs = [...publicInputs.toFields()];
196
204
 
197
- if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b) => a.equals(b))) {
205
+ if (!areArraysEqual(rollupPublicInputs, argsPublicInputs, (a, b) => a.equals(b))) {
198
206
  const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
199
207
  throw new Error(
200
208
  `Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`,
@@ -202,9 +210,77 @@ export class ProverNodePublisher {
202
210
  }
203
211
  }
204
212
 
213
+ /**
214
+ * Estimates what submitting the epoch proof would have cost on L1 without actually sending it.
215
+ * Runs the same validation as `submitEpochProof`, encodes the calldata, estimates gas, and records metrics.
216
+ * Used when proof publishing is disabled (e.g. PROVER_NODE_DISABLE_PROOF_PUBLISH=true on mainnet).
217
+ */
218
+ public async analyzeEpochProofSubmission(args: {
219
+ epochNumber: EpochNumber;
220
+ fromCheckpoint: CheckpointNumber;
221
+ toCheckpoint: CheckpointNumber;
222
+ publicInputs: RootRollupPublicInputs;
223
+ proof: Proof;
224
+ batchedBlobInputs: BatchedBlob;
225
+ attestations: ViemCommitteeAttestation[];
226
+ }): Promise<void> {
227
+ const { epochNumber, fromCheckpoint, toCheckpoint } = args;
228
+
229
+ await this.validateEpochProofSubmission(args);
230
+
231
+ const data = this.encodeSubmitEpochProofCalldata(args);
232
+ const senderAddress = this.l1TxUtils.getSenderAddress();
233
+
234
+ const [gasLimit, gasPrice, latestBlock] = await Promise.all([
235
+ this.l1TxUtils.estimateGas(senderAddress.toString() as `0x${string}`, { to: this.rollupContract.address, data }),
236
+ this.l1TxUtils.getGasPrice(),
237
+ this.l1TxUtils.client.getBlock({ blockTag: 'latest' }),
238
+ ]);
239
+
240
+ const baseFeePerGas = latestBlock.baseFeePerGas ?? 0n;
241
+ const { maxPriorityFeePerGas } = gasPrice;
242
+
243
+ const effectiveFeePerGas = baseFeePerGas + maxPriorityFeePerGas;
244
+ const estimatedTotalFee = gasLimit * effectiveFeePerGas;
245
+
246
+ const stats: EstimatedSubmitProofStats = {
247
+ gasLimit,
248
+ baseFeePerGas,
249
+ maxPriorityFeePerGas,
250
+ estimatedTotalFee,
251
+ };
252
+
253
+ this.log.info(`Estimated epoch proof submission cost (not submitted)`, {
254
+ epochNumber,
255
+ fromCheckpoint,
256
+ toCheckpoint,
257
+ gasLimit: gasLimit.toString(),
258
+ baseFeePerGas: formatGwei(baseFeePerGas),
259
+ maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
260
+ estimatedTotalFeeEth: formatEther(estimatedTotalFee),
261
+ });
262
+
263
+ this.metrics.recordEstimatedSubmitProof(stats);
264
+ }
265
+
266
+ private encodeSubmitEpochProofCalldata(args: {
267
+ fromCheckpoint: CheckpointNumber;
268
+ toCheckpoint: CheckpointNumber;
269
+ publicInputs: RootRollupPublicInputs;
270
+ proof: Proof;
271
+ batchedBlobInputs: BatchedBlob;
272
+ attestations: ViemCommitteeAttestation[];
273
+ }): Hex {
274
+ return encodeFunctionData({
275
+ abi: RollupAbi,
276
+ functionName: 'submitEpochRootProof',
277
+ args: [this.getSubmitEpochProofArgs(args)],
278
+ });
279
+ }
280
+
205
281
  private async sendSubmitEpochProofTx(args: {
206
- fromBlock: number;
207
- toBlock: number;
282
+ fromCheckpoint: CheckpointNumber;
283
+ toCheckpoint: CheckpointNumber;
208
284
  publicInputs: RootRollupPublicInputs;
209
285
  proof: Proof;
210
286
  batchedBlobInputs: BatchedBlob;
@@ -214,8 +290,8 @@ export class ProverNodePublisher {
214
290
 
215
291
  this.log.info(`Submitting epoch proof to L1 rollup contract`, {
216
292
  proofSize: args.proof.withoutPublicInputs().length,
217
- fromBlock: args.fromBlock,
218
- toBlock: args.toBlock,
293
+ fromCheckpoint: args.fromCheckpoint,
294
+ toCheckpoint: args.toCheckpoint,
219
295
  });
220
296
  const data = encodeFunctionData({
221
297
  abi: RollupAbi,
@@ -224,42 +300,46 @@ export class ProverNodePublisher {
224
300
  });
225
301
  try {
226
302
  const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
303
+ if (receipt.status !== 'success') {
304
+ const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
305
+ data,
306
+ {
307
+ args: [...txArgs],
308
+ functionName: 'submitEpochRootProof',
309
+ abi: RollupAbi,
310
+ address: this.rollupContract.address,
311
+ },
312
+ /*blobInputs*/ undefined,
313
+ /*stateOverride*/ [],
314
+ );
315
+ this.log.error(`Rollup submit epoch proof tx reverted with ${errorMsg ?? 'unknown error'}`);
316
+ return undefined;
317
+ }
227
318
  return receipt;
228
319
  } catch (err) {
229
320
  this.log.error(`Rollup submit epoch proof failed`, err);
230
- const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
231
- data,
232
- {
233
- args: [...txArgs],
234
- functionName: 'submitEpochRootProof',
235
- abi: RollupAbi,
236
- address: this.rollupContract.address,
237
- },
238
- /*blobInputs*/ undefined,
239
- /*stateOverride*/ [],
240
- );
241
- this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
242
321
  return undefined;
243
322
  }
244
323
  }
245
324
 
246
325
  private getEpochProofPublicInputsArgs(args: {
247
- fromBlock: number;
248
- toBlock: number;
326
+ fromCheckpoint: CheckpointNumber;
327
+ toCheckpoint: CheckpointNumber;
249
328
  publicInputs: RootRollupPublicInputs;
250
329
  batchedBlobInputs: BatchedBlob;
251
330
  attestations: ViemCommitteeAttestation[];
252
331
  }) {
253
332
  // Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
254
333
  return [
255
- BigInt(args.fromBlock) /*_start*/,
256
- BigInt(args.toBlock) /*_end*/,
334
+ BigInt(args.fromCheckpoint) /*_start*/,
335
+ BigInt(args.toCheckpoint) /*_end*/,
257
336
  {
258
337
  previousArchive: args.publicInputs.previousArchiveRoot.toString(),
259
338
  endArchive: args.publicInputs.endArchiveRoot.toString(),
339
+ outHash: args.publicInputs.outHash.toString(),
260
340
  proverId: EthAddress.fromField(args.publicInputs.constants.proverId).toString(),
261
341
  } /*_args*/,
262
- makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
342
+ makeTuple(MAX_CHECKPOINTS_PER_EPOCH * 2, i =>
263
343
  i % 2 === 0
264
344
  ? args.publicInputs.fees[i / 2].recipient.toField().toString()
265
345
  : args.publicInputs.fees[(i - 1) / 2].value.toString(),
@@ -269,8 +349,8 @@ export class ProverNodePublisher {
269
349
  }
270
350
 
271
351
  private getSubmitEpochProofArgs(args: {
272
- fromBlock: number;
273
- toBlock: number;
352
+ fromCheckpoint: CheckpointNumber;
353
+ toCheckpoint: CheckpointNumber;
274
354
  publicInputs: RootRollupPublicInputs;
275
355
  proof: Proof;
276
356
  batchedBlobInputs: BatchedBlob;
@@ -284,9 +364,9 @@ export class ProverNodePublisher {
284
364
  end: argsArray[1],
285
365
  args: argsArray[2],
286
366
  fees: argsArray[3],
287
- attestations: new CommitteeAttestationsAndSigners(
367
+ attestations: CommitteeAttestationsAndSigners.packAttestations(
288
368
  args.attestations.map(a => CommitteeAttestation.fromViem(a)),
289
- ).getPackedAttestations(),
369
+ ),
290
370
  blobInputs: argsArray[4],
291
371
  proof: proofHex,
292
372
  };