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

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 (81) 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 +12 -9
  17. package/dest/config.d.ts.map +1 -1
  18. package/dest/config.js +81 -14
  19. package/dest/factory.d.ts +12 -8
  20. package/dest/factory.d.ts.map +1 -1
  21. package/dest/factory.js +95 -31
  22. package/dest/index.d.ts +1 -1
  23. package/dest/index.d.ts.map +1 -1
  24. package/dest/index.js +1 -1
  25. package/dest/job/epoch-proving-job-data.d.ts +16 -0
  26. package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
  27. package/dest/job/epoch-proving-job-data.js +52 -0
  28. package/dest/job/epoch-proving-job.d.ts +30 -15
  29. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  30. package/dest/job/epoch-proving-job.js +149 -50
  31. package/dest/metrics.d.ts +28 -4
  32. package/dest/metrics.d.ts.map +1 -1
  33. package/dest/metrics.js +141 -35
  34. package/dest/monitors/epoch-monitor.d.ts +3 -1
  35. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  36. package/dest/monitors/epoch-monitor.js +15 -2
  37. package/dest/prover-node-publisher.d.ts +7 -10
  38. package/dest/prover-node-publisher.d.ts.map +1 -1
  39. package/dest/prover-node-publisher.js +59 -60
  40. package/dest/prover-node.d.ts +43 -39
  41. package/dest/prover-node.d.ts.map +1 -1
  42. package/dest/prover-node.js +171 -100
  43. package/dest/prover-publisher-factory.d.ts +21 -0
  44. package/dest/prover-publisher-factory.d.ts.map +1 -0
  45. package/dest/prover-publisher-factory.js +26 -0
  46. package/dest/test/index.d.ts +4 -2
  47. package/dest/test/index.d.ts.map +1 -1
  48. package/dest/test/index.js +1 -3
  49. package/package.json +36 -31
  50. package/src/actions/download-epoch-proving-job.ts +44 -0
  51. package/src/actions/index.ts +2 -0
  52. package/src/actions/rerun-epoch-proving-job.ts +61 -0
  53. package/src/actions/upload-epoch-proof-failure.ts +88 -0
  54. package/src/bin/run-failed-epoch.ts +77 -0
  55. package/src/config.ts +108 -24
  56. package/src/factory.ts +161 -43
  57. package/src/index.ts +1 -1
  58. package/src/job/epoch-proving-job-data.ts +76 -0
  59. package/src/job/epoch-proving-job.ts +215 -50
  60. package/src/metrics.ts +135 -37
  61. package/src/monitors/epoch-monitor.ts +16 -5
  62. package/src/prover-node-publisher.ts +93 -86
  63. package/src/prover-node.ts +203 -126
  64. package/src/prover-publisher-factory.ts +37 -0
  65. package/src/test/index.ts +7 -4
  66. package/dest/http.d.ts +0 -8
  67. package/dest/http.d.ts.map +0 -1
  68. package/dest/http.js +0 -9
  69. package/dest/prover-coordination/config.d.ts +0 -7
  70. package/dest/prover-coordination/config.d.ts.map +0 -1
  71. package/dest/prover-coordination/config.js +0 -11
  72. package/dest/prover-coordination/factory.d.ts +0 -22
  73. package/dest/prover-coordination/factory.d.ts.map +0 -1
  74. package/dest/prover-coordination/factory.js +0 -42
  75. package/dest/prover-coordination/index.d.ts +0 -3
  76. package/dest/prover-coordination/index.d.ts.map +0 -1
  77. package/dest/prover-coordination/index.js +0 -2
  78. package/src/http.ts +0 -13
  79. package/src/prover-coordination/config.ts +0 -17
  80. package/src/prover-coordination/factory.ts +0 -72
  81. package/src/prover-coordination/index.ts +0 -2
package/src/metrics.ts CHANGED
@@ -1,106 +1,201 @@
1
+ import type { RollupContract } from '@aztec/ethereum';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
1
3
  import { createLogger } from '@aztec/foundation/log';
2
4
  import type { L1PublishProofStats, L1PublishStats } from '@aztec/stdlib/stats';
3
5
  import {
4
6
  Attributes,
7
+ type BatchObservableResult,
5
8
  type Gauge,
6
9
  type Histogram,
10
+ type Meter,
7
11
  Metrics,
12
+ type ObservableGauge,
8
13
  type TelemetryClient,
14
+ type Tracer,
9
15
  type UpDownCounter,
10
16
  ValueType,
11
17
  } from '@aztec/telemetry-client';
12
18
 
13
- import { formatEther } from 'viem';
19
+ import { formatEther, formatUnits } from 'viem';
14
20
 
15
- export class ProverNodeMetrics {
21
+ export class ProverNodeJobMetrics {
16
22
  proverEpochExecutionDuration: Histogram;
17
23
  provingJobDuration: Histogram;
18
24
  provingJobBlocks: Gauge;
19
25
  provingJobTransactions: Gauge;
20
26
 
21
- gasPrice: Histogram;
22
- txCount: UpDownCounter;
23
- txDuration: Histogram;
24
- txGas: Histogram;
25
- txCalldataSize: Histogram;
26
- txCalldataGas: Histogram;
27
- txBlobDataGasUsed: Histogram;
28
- txBlobDataGasCost: Histogram;
29
-
30
- private senderBalance: Gauge;
31
-
32
27
  constructor(
33
- public readonly client: TelemetryClient,
34
- name = 'ProverNode',
28
+ private meter: Meter,
29
+ public readonly tracer: Tracer,
35
30
  private logger = createLogger('prover-node:publisher:metrics'),
36
31
  ) {
37
- const meter = client.getMeter(name);
38
- this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
32
+ this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
39
33
  description: 'Duration of execution of an epoch by the prover',
40
34
  unit: 'ms',
41
35
  valueType: ValueType.INT,
42
36
  });
43
- this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
37
+ this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
44
38
  description: 'Duration of proving job',
45
39
  unit: 's',
46
40
  valueType: ValueType.DOUBLE,
47
41
  });
48
- this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
42
+ this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
49
43
  description: 'Number of blocks in a proven epoch',
50
44
  valueType: ValueType.INT,
51
45
  });
52
- this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
46
+ this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
53
47
  description: 'Number of transactions in a proven epoch',
54
48
  valueType: ValueType.INT,
55
49
  });
50
+ }
51
+
52
+ public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
53
+ this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
54
+ this.provingJobDuration.record(totalTimeMs / 1000);
55
+ this.provingJobBlocks.record(Math.floor(numBlocks));
56
+ this.provingJobTransactions.record(Math.floor(numTxs));
57
+ }
58
+ }
59
+
60
+ export class ProverNodeRewardsMetrics {
61
+ private rewards: ObservableGauge;
62
+ private accumulatedRewards: UpDownCounter;
63
+ private prevEpoch = -1n;
64
+ private proofSubmissionEpochs = 0;
65
+
66
+ constructor(
67
+ private meter: Meter,
68
+ private coinbase: EthAddress,
69
+ private rollup: RollupContract,
70
+ private logger = createLogger('prover-node:publisher:metrics'),
71
+ ) {
72
+ this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
73
+ valueType: ValueType.DOUBLE,
74
+ description: 'The rewards earned',
75
+ });
76
+
77
+ this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
78
+ valueType: ValueType.DOUBLE,
79
+ description: 'The rewards earned (total)',
80
+ });
81
+ }
82
+
83
+ public async start() {
84
+ const proofSubmissionEpochs = await this.rollup.getProofSubmissionEpochs();
85
+ this.proofSubmissionEpochs = Number(proofSubmissionEpochs);
86
+ this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
87
+ }
88
+
89
+ public stop() {
90
+ this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
91
+ }
92
+
93
+ private observe = async (observer: BatchObservableResult): Promise<void> => {
94
+ const epoch = await this.rollup.getCurrentEpochNumber();
95
+
96
+ if (epoch > this.proofSubmissionEpochs) {
97
+ // look at the prev epoch so that we get an accurate value, after proof submission window has closed
98
+ // For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
99
+ // 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;
101
+ const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
56
102
 
57
- this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
103
+ const fmt = parseFloat(formatUnits(rewards, 18));
104
+
105
+ observer.observe(this.rewards, fmt, {
106
+ [Attributes.COINBASE]: this.coinbase.toString(),
107
+ });
108
+
109
+ // only accumulate once per epoch
110
+ if (closedEpoch > this.prevEpoch) {
111
+ this.prevEpoch = closedEpoch;
112
+ this.accumulatedRewards.add(fmt, {
113
+ [Attributes.COINBASE]: this.coinbase.toString(),
114
+ });
115
+ }
116
+ }
117
+ };
118
+ }
119
+
120
+ export class ProverNodePublisherMetrics {
121
+ gasPrice: Histogram;
122
+ txCount: UpDownCounter;
123
+ txDuration: Histogram;
124
+ txGas: Histogram;
125
+ txCalldataSize: Histogram;
126
+ txCalldataGas: Histogram;
127
+ txBlobDataGasUsed: Histogram;
128
+ txBlobDataGasCost: Histogram;
129
+ txTotalFee: Histogram;
130
+
131
+ private senderBalance: Gauge;
132
+ private meter: Meter;
133
+
134
+ constructor(
135
+ public readonly client: TelemetryClient,
136
+ name = 'ProverNode',
137
+ private logger = createLogger('prover-node:publisher:metrics'),
138
+ ) {
139
+ this.meter = client.getMeter(name);
140
+
141
+ this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
58
142
  description: 'The gas price used for transactions',
59
143
  unit: 'gwei',
60
144
  valueType: ValueType.DOUBLE,
61
145
  });
62
146
 
63
- this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
147
+ this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
64
148
  description: 'The number of transactions processed',
65
149
  });
66
150
 
67
- this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
151
+ this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
68
152
  description: 'The duration of transaction processing',
69
153
  unit: 'ms',
70
154
  valueType: ValueType.INT,
71
155
  });
72
156
 
73
- this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
157
+ this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
74
158
  description: 'The gas consumed by transactions',
75
159
  unit: 'gas',
76
160
  valueType: ValueType.INT,
77
161
  });
78
162
 
79
- this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
163
+ this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
80
164
  description: 'The size of the calldata in transactions',
81
165
  unit: 'By',
82
166
  valueType: ValueType.INT,
83
167
  });
84
168
 
85
- this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
169
+ this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
86
170
  description: 'The gas consumed by the calldata in transactions',
87
171
  unit: 'gas',
88
172
  valueType: ValueType.INT,
89
173
  });
90
174
 
91
- this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
175
+ this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
92
176
  description: 'The amount of blob gas used in transactions',
93
177
  unit: 'gas',
94
178
  valueType: ValueType.INT,
95
179
  });
96
180
 
97
- this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
181
+ this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
98
182
  description: 'The gas cost of blobs in transactions',
99
183
  unit: 'gwei',
100
184
  valueType: ValueType.INT,
101
185
  });
102
186
 
103
- this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
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
+ });
197
+
198
+ this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
104
199
  unit: 'eth',
105
200
  description: 'The balance of the sender address',
106
201
  valueType: ValueType.DOUBLE,
@@ -118,13 +213,6 @@ export class ProverNodeMetrics {
118
213
  this.recordTx(durationMs, stats);
119
214
  }
120
215
 
121
- public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
122
- this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
123
- this.provingJobDuration.record(totalTimeMs / 1000);
124
- this.provingJobBlocks.record(Math.floor(numBlocks));
125
- this.provingJobTransactions.record(Math.floor(numTxs));
126
- }
127
-
128
216
  public recordSenderBalance(wei: bigint, senderAddress: string) {
129
217
  const eth = parseFloat(formatEther(wei, 'wei'));
130
218
  this.senderBalance.record(eth, {
@@ -157,7 +245,17 @@ export class ProverNodeMetrics {
157
245
 
158
246
  try {
159
247
  this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
160
- } catch (e) {
248
+ } catch {
249
+ // ignore
250
+ }
251
+
252
+ const executionFee = stats.gasUsed * stats.gasPrice;
253
+ const blobFee = stats.blobGasUsed * stats.blobDataGas;
254
+ const totalFee = executionFee + blobFee;
255
+
256
+ try {
257
+ this.txTotalFee.record(parseFloat(formatEther(totalFee)));
258
+ } catch {
161
259
  // ignore
162
260
  }
163
261
  }
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
2
  import { RunningPromise } from '@aztec/foundation/running-promise';
3
+ import { sleep } from '@aztec/foundation/sleep';
3
4
  import type { L2BlockSource } from '@aztec/stdlib/block';
4
5
  import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
5
6
  import {
@@ -11,7 +12,7 @@ import {
11
12
  } from '@aztec/telemetry-client';
12
13
 
13
14
  export interface EpochMonitorHandler {
14
- handleEpochReadyToProve(epochNumber: bigint): Promise<void>;
15
+ handleEpochReadyToProve(epochNumber: bigint): Promise<boolean>;
15
16
  }
16
17
 
17
18
  /**
@@ -36,16 +37,19 @@ export class EpochMonitor implements Traceable {
36
37
  constructor(
37
38
  private readonly l2BlockSource: L2BlockSource,
38
39
  private readonly l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
39
- private options: { pollingIntervalMs: number },
40
+ private options: { pollingIntervalMs: number; provingDelayMs?: number },
40
41
  telemetry: TelemetryClient = getTelemetryClient(),
41
42
  ) {
42
43
  this.tracer = telemetry.getTracer('EpochMonitor');
43
44
  this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.options.pollingIntervalMs);
45
+ if (this.options.provingDelayMs) {
46
+ this.log.warn(`Prover node epoch monitor running with delay of ${this.options.provingDelayMs}ms`);
47
+ }
44
48
  }
45
49
 
46
50
  public static async create(
47
51
  l2BlockSource: L2BlockSource,
48
- options: { pollingIntervalMs: number },
52
+ options: { pollingIntervalMs: number; provingDelayMs?: number },
49
53
  telemetry: TelemetryClient = getTelemetryClient(),
50
54
  ): Promise<EpochMonitor> {
51
55
  const l1Constants = await l2BlockSource.getL1Constants();
@@ -71,6 +75,7 @@ export class EpochMonitor implements Traceable {
71
75
  @trackSpan('EpochMonitor.work')
72
76
  public async work() {
73
77
  const { epochToProve, blockNumber, slotNumber } = await this.getEpochNumberToProve();
78
+ this.log.debug(`Epoch to prove: ${epochToProve}`, { blockNumber, slotNumber });
74
79
  if (epochToProve === undefined) {
75
80
  this.log.trace(`Next block to prove ${blockNumber} not yet mined`, { blockNumber });
76
81
  return;
@@ -86,9 +91,15 @@ export class EpochMonitor implements Traceable {
86
91
  return;
87
92
  }
88
93
 
94
+ if (this.options.provingDelayMs) {
95
+ this.log.debug(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
96
+ await sleep(this.options.provingDelayMs);
97
+ }
98
+
89
99
  this.log.debug(`Epoch ${epochToProve} is ready to be proven`);
90
- await this.handler?.handleEpochReadyToProve(epochToProve);
91
- this.latestEpochNumber = epochToProve;
100
+ if (await this.handler?.handleEpochReadyToProve(epochToProve)) {
101
+ this.latestEpochNumber = epochToProve;
102
+ }
92
103
  }
93
104
 
94
105
  private async getEpochNumberToProve() {
@@ -1,34 +1,31 @@
1
- import { AGGREGATION_OBJECT_LENGTH, AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
2
- import type { L1TxUtils, RollupContract } from '@aztec/ethereum';
1
+ import type { BatchedBlob } from '@aztec/blob-lib';
2
+ import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
3
+ import type { L1TxUtils, RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum';
3
4
  import { makeTuple } from '@aztec/foundation/array';
4
- import { areArraysEqual, times } from '@aztec/foundation/collection';
5
+ import { areArraysEqual } from '@aztec/foundation/collection';
5
6
  import { EthAddress } from '@aztec/foundation/eth-address';
6
7
  import { Fr } from '@aztec/foundation/fields';
7
8
  import { createLogger } from '@aztec/foundation/log';
8
- import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
9
- import { InterruptibleSleep } from '@aztec/foundation/sleep';
9
+ import type { Tuple } from '@aztec/foundation/serialize';
10
10
  import { Timer } from '@aztec/foundation/timer';
11
11
  import { RollupAbi } from '@aztec/l1-artifacts';
12
12
  import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
13
+ import { CommitteeAttestation, CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
13
14
  import type { Proof } from '@aztec/stdlib/proofs';
14
15
  import type { FeeRecipient, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
15
16
  import type { L1PublishProofStats } from '@aztec/stdlib/stats';
16
17
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
17
18
 
19
+ import { inspect } from 'util';
18
20
  import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
19
21
 
20
- import { ProverNodeMetrics } from './metrics.js';
22
+ import { ProverNodePublisherMetrics } from './metrics.js';
21
23
 
22
- /**
23
- * Stats for a sent transaction.
24
- */
25
24
  /** Arguments to the submitEpochProof method of the rollup contract */
26
25
  export type L1SubmitEpochProofArgs = {
27
26
  epochSize: number;
28
27
  previousArchive: Fr;
29
28
  endArchive: Fr;
30
- previousBlockHash: Fr;
31
- endBlockHash: Fr;
32
29
  endTimestamp: Fr;
33
30
  outHash: Fr;
34
31
  proverId: Fr;
@@ -37,10 +34,8 @@ export type L1SubmitEpochProofArgs = {
37
34
  };
38
35
 
39
36
  export class ProverNodePublisher {
40
- private interruptibleSleep = new InterruptibleSleep();
41
- private sleepTimeMs: number;
42
37
  private interrupted = false;
43
- private metrics: ProverNodeMetrics;
38
+ private metrics: ProverNodePublisherMetrics;
44
39
 
45
40
  protected log = createLogger('prover-node:l1-tx-publisher');
46
41
 
@@ -56,16 +51,18 @@ export class ProverNodePublisher {
56
51
  telemetry?: TelemetryClient;
57
52
  },
58
53
  ) {
59
- this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
60
-
61
54
  const telemetry = deps.telemetry ?? getTelemetryClient();
62
55
 
63
- this.metrics = new ProverNodeMetrics(telemetry, 'ProverNode');
56
+ this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
64
57
 
65
58
  this.rollupContract = deps.rollupContract;
66
59
  this.l1TxUtils = deps.l1TxUtils;
67
60
  }
68
61
 
62
+ public getRollupContract() {
63
+ return this.rollupContract;
64
+ }
65
+
69
66
  /**
70
67
  * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
71
68
  * Be warned, the call may return false even if the tx subsequently gets successfully mined.
@@ -74,16 +71,17 @@ export class ProverNodePublisher {
74
71
  */
75
72
  public interrupt() {
76
73
  this.interrupted = true;
77
- this.interruptibleSleep.interrupt();
74
+ this.l1TxUtils.interrupt();
78
75
  }
79
76
 
80
77
  /** Restarts the publisher after calling `interrupt`. */
81
78
  public restart() {
82
79
  this.interrupted = false;
80
+ this.l1TxUtils.restart();
83
81
  }
84
82
 
85
83
  public getSenderAddress() {
86
- return EthAddress.fromString(this.l1TxUtils.getSenderAddress());
84
+ return this.l1TxUtils.getSenderAddress();
87
85
  }
88
86
 
89
87
  public async submitEpochProof(args: {
@@ -92,12 +90,14 @@ export class ProverNodePublisher {
92
90
  toBlock: number;
93
91
  publicInputs: RootRollupPublicInputs;
94
92
  proof: Proof;
93
+ batchedBlobInputs: BatchedBlob;
94
+ attestations: ViemCommitteeAttestation[];
95
95
  }): Promise<boolean> {
96
96
  const { epochNumber, fromBlock, toBlock } = args;
97
97
  const ctx = { epochNumber, fromBlock, toBlock };
98
+
98
99
  if (!this.interrupted) {
99
100
  const timer = new Timer();
100
-
101
101
  // Validate epoch proof range and hashes are correct before submitting
102
102
  await this.validateEpochProofSubmission(args);
103
103
 
@@ -107,13 +107,16 @@ export class ProverNodePublisher {
107
107
  }
108
108
 
109
109
  try {
110
- this.metrics.recordSenderBalance(await this.l1TxUtils.getSenderBalance(), this.l1TxUtils.getSenderAddress());
110
+ this.metrics.recordSenderBalance(
111
+ await this.l1TxUtils.getSenderBalance(),
112
+ this.l1TxUtils.getSenderAddress().toString(),
113
+ );
111
114
  } catch (err) {
112
115
  this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
113
116
  }
114
117
 
115
118
  // Tx was mined successfully
116
- if (txReceipt.status) {
119
+ if (txReceipt.status === 'success') {
117
120
  const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
118
121
  const stats: L1PublishProofStats = {
119
122
  gasPrice: txReceipt.effectiveGasPrice,
@@ -132,11 +135,10 @@ export class ProverNodePublisher {
132
135
  }
133
136
 
134
137
  this.metrics.recordFailedTx();
135
- this.log.error(`Rollup.submitEpochProof tx status failed: ${txReceipt.transactionHash}`, ctx);
136
- await this.sleepOrInterrupted();
138
+ this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
137
139
  }
138
140
 
139
- this.log.verbose('L2 block data syncing interrupted while processing blocks.', ctx);
141
+ this.log.verbose('L2 block data syncing interrupted', ctx);
140
142
  return false;
141
143
  }
142
144
 
@@ -145,49 +147,51 @@ export class ProverNodePublisher {
145
147
  toBlock: number;
146
148
  publicInputs: RootRollupPublicInputs;
147
149
  proof: Proof;
150
+ batchedBlobInputs: BatchedBlob;
151
+ attestations: ViemCommitteeAttestation[];
148
152
  }) {
149
- const { fromBlock, toBlock, publicInputs, proof } = args;
153
+ const { fromBlock, toBlock, publicInputs, batchedBlobInputs } = args;
150
154
 
151
155
  // Check that the block numbers match the expected epoch to be proven
152
156
  const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
153
- if (proven !== BigInt(fromBlock) - 1n) {
157
+ // Don't publish if proven is beyond our toBlock, pointless to do so
158
+ if (proven > BigInt(toBlock)) {
154
159
  throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
155
160
  }
161
+ // toBlock can't be greater than pending
156
162
  if (toBlock > pending) {
157
163
  throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
158
164
  }
159
165
 
160
- // Check the block hash and archive for the immediate block before the epoch
161
- const blockLog = await this.rollupContract.getBlock(proven);
162
- if (publicInputs.previousArchive.root.toString() !== blockLog.archive) {
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) {
163
169
  throw new Error(
164
- `Previous archive root mismatch: ${publicInputs.previousArchive.root.toString()} !== ${blockLog.archive}`,
170
+ `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${blockLog.archive}`,
165
171
  );
166
172
  }
167
- // TODO: Remove zero check once we inject the proper zero blockhash
168
- if (blockLog.blockHash !== Fr.ZERO.toString() && publicInputs.previousBlockHash.toString() !== blockLog.blockHash) {
173
+
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) {
169
177
  throw new Error(
170
- `Previous block hash mismatch: ${publicInputs.previousBlockHash.toString()} !== ${blockLog.blockHash}`,
178
+ `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endBlockLog.archive}`,
171
179
  );
172
180
  }
173
181
 
174
- // Check the block hash and archive for the last block in the epoch
175
- const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
176
- if (publicInputs.endArchive.root.toString() !== endBlockLog.archive) {
182
+ // Check the batched blob inputs from the root rollup against the batched blob computed in ts
183
+ const finalBlobAccumulator = batchedBlobInputs.toFinalBlobAccumulator();
184
+ if (!publicInputs.blobPublicInputs.equals(finalBlobAccumulator)) {
177
185
  throw new Error(
178
- `End archive root mismatch: ${publicInputs.endArchive.root.toString()} !== ${endBlockLog.archive}`,
186
+ `Batched blob mismatch: ${inspect(publicInputs.blobPublicInputs)} !== ${inspect(finalBlobAccumulator)}`,
179
187
  );
180
188
  }
181
- if (publicInputs.endBlockHash.toString() !== endBlockLog.blockHash) {
182
- throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockLog.blockHash}`);
183
- }
184
189
 
185
190
  // Compare the public inputs computed by the contract with the ones injected
186
- const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(this.getSubmitEpochProofArgs(args));
187
- const aggregationObject = proof.isEmpty()
188
- ? times(AGGREGATION_OBJECT_LENGTH, Fr.zero)
189
- : proof.extractAggregationObject();
190
- const argsPublicInputs = [...publicInputs.toFields(), ...aggregationObject];
191
+ const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(
192
+ this.getEpochProofPublicInputsArgs(args),
193
+ );
194
+ const argsPublicInputs = [...publicInputs.toFields()];
191
195
 
192
196
  if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b) => a.equals(b))) {
193
197
  const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
@@ -202,34 +206,23 @@ export class ProverNodePublisher {
202
206
  toBlock: number;
203
207
  publicInputs: RootRollupPublicInputs;
204
208
  proof: Proof;
209
+ batchedBlobInputs: BatchedBlob;
210
+ attestations: ViemCommitteeAttestation[];
205
211
  }): Promise<TransactionReceipt | undefined> {
206
- const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
207
- const argsArray = this.getSubmitEpochProofArgs(args);
208
-
209
- const txArgs = [
210
- {
211
- start: argsArray[0],
212
- end: argsArray[1],
213
- args: argsArray[2],
214
- fees: argsArray[3],
215
- blobPublicInputs: argsArray[4],
216
- aggregationObject: argsArray[5],
217
- proof: proofHex,
218
- },
219
- ] as const;
212
+ const txArgs = [this.getSubmitEpochProofArgs(args)] as const;
220
213
 
221
- this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
214
+ this.log.info(`Submitting epoch proof to L1 rollup contract`, {
215
+ proofSize: args.proof.withoutPublicInputs().length,
216
+ fromBlock: args.fromBlock,
217
+ toBlock: args.toBlock,
218
+ });
222
219
  const data = encodeFunctionData({
223
220
  abi: RollupAbi,
224
221
  functionName: 'submitEpochRootProof',
225
222
  args: txArgs,
226
223
  });
227
224
  try {
228
- const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
229
- to: this.rollupContract.address,
230
- data,
231
- });
232
-
225
+ const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
233
226
  return receipt;
234
227
  } catch (err) {
235
228
  this.log.error(`Rollup submit epoch proof failed`, err);
@@ -249,38 +242,52 @@ export class ProverNodePublisher {
249
242
  }
250
243
  }
251
244
 
252
- private getSubmitEpochProofArgs(args: {
245
+ private getEpochProofPublicInputsArgs(args: {
253
246
  fromBlock: number;
254
247
  toBlock: number;
255
248
  publicInputs: RootRollupPublicInputs;
256
- proof: Proof;
249
+ batchedBlobInputs: BatchedBlob;
250
+ attestations: ViemCommitteeAttestation[];
257
251
  }) {
252
+ // Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
258
253
  return [
259
- BigInt(args.fromBlock),
260
- BigInt(args.toBlock),
254
+ BigInt(args.fromBlock) /*_start*/,
255
+ BigInt(args.toBlock) /*_end*/,
261
256
  {
262
- previousArchive: args.publicInputs.previousArchive.root.toString(),
263
- endArchive: args.publicInputs.endArchive.root.toString(),
264
- previousBlockHash: args.publicInputs.previousBlockHash.toString(),
265
- endBlockHash: args.publicInputs.endBlockHash.toString(),
266
- endTimestamp: args.publicInputs.endTimestamp.toBigInt(),
267
- outHash: args.publicInputs.outHash.toString(),
268
- proverId: EthAddress.fromField(args.publicInputs.proverId).toString(),
269
- },
257
+ previousArchive: args.publicInputs.previousArchiveRoot.toString(),
258
+ endArchive: args.publicInputs.endArchiveRoot.toString(),
259
+ proverId: EthAddress.fromField(args.publicInputs.constants.proverId).toString(),
260
+ } /*_args*/,
270
261
  makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
271
262
  i % 2 === 0
272
263
  ? args.publicInputs.fees[i / 2].recipient.toField().toString()
273
264
  : args.publicInputs.fees[(i - 1) / 2].value.toString(),
274
- ),
275
- `0x${args.publicInputs.blobPublicInputs
276
- .filter((_, i) => i < args.toBlock - args.fromBlock + 1)
277
- .map(b => b.toString())
278
- .join(``)}`,
279
- `0x${serializeToBuffer(args.proof.extractAggregationObject()).toString('hex')}`,
265
+ ) /*_fees*/,
266
+ args.batchedBlobInputs.getEthBlobEvaluationInputs() /*_blobPublicInputs*/,
280
267
  ] as const;
281
268
  }
282
269
 
283
- protected async sleepOrInterrupted() {
284
- await this.interruptibleSleep.sleep(this.sleepTimeMs);
270
+ private getSubmitEpochProofArgs(args: {
271
+ fromBlock: number;
272
+ toBlock: number;
273
+ publicInputs: RootRollupPublicInputs;
274
+ proof: Proof;
275
+ batchedBlobInputs: BatchedBlob;
276
+ attestations: ViemCommitteeAttestation[];
277
+ }) {
278
+ // Returns arguments for EpochProofLib.sol -> submitEpochRootProof()
279
+ const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
280
+ const argsArray = this.getEpochProofPublicInputsArgs(args);
281
+ return {
282
+ start: argsArray[0],
283
+ end: argsArray[1],
284
+ args: argsArray[2],
285
+ fees: argsArray[3],
286
+ attestations: new CommitteeAttestationsAndSigners(
287
+ args.attestations.map(a => CommitteeAttestation.fromViem(a)),
288
+ ).getPackedAttestations(),
289
+ blobInputs: argsArray[4],
290
+ proof: proofHex,
291
+ };
285
292
  }
286
293
  }