@aztec/prover-node 0.0.0-test.1 → 0.0.1-fake-ceab37513c

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 +94 -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 +119 -46
  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 +58 -59
  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 +158 -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 +177 -46
  60. package/src/metrics.ts +135 -37
  61. package/src/monitors/epoch-monitor.ts +16 -5
  62. package/src/prover-node-publisher.ts +90 -85
  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, FinalBlobAccumulatorPublicInputs } 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,13 @@ 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
  if (!this.interrupted) {
99
99
  const timer = new Timer();
100
-
101
100
  // Validate epoch proof range and hashes are correct before submitting
102
101
  await this.validateEpochProofSubmission(args);
103
102
 
@@ -107,13 +106,16 @@ export class ProverNodePublisher {
107
106
  }
108
107
 
109
108
  try {
110
- this.metrics.recordSenderBalance(await this.l1TxUtils.getSenderBalance(), this.l1TxUtils.getSenderAddress());
109
+ this.metrics.recordSenderBalance(
110
+ await this.l1TxUtils.getSenderBalance(),
111
+ this.l1TxUtils.getSenderAddress().toString(),
112
+ );
111
113
  } catch (err) {
112
114
  this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
113
115
  }
114
116
 
115
117
  // Tx was mined successfully
116
- if (txReceipt.status) {
118
+ if (txReceipt.status === 'success') {
117
119
  const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
118
120
  const stats: L1PublishProofStats = {
119
121
  gasPrice: txReceipt.effectiveGasPrice,
@@ -132,11 +134,10 @@ export class ProverNodePublisher {
132
134
  }
133
135
 
134
136
  this.metrics.recordFailedTx();
135
- this.log.error(`Rollup.submitEpochProof tx status failed: ${txReceipt.transactionHash}`, ctx);
136
- await this.sleepOrInterrupted();
137
+ this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
137
138
  }
138
139
 
139
- this.log.verbose('L2 block data syncing interrupted while processing blocks.', ctx);
140
+ this.log.verbose('L2 block data syncing interrupted', ctx);
140
141
  return false;
141
142
  }
142
143
 
@@ -145,49 +146,50 @@ export class ProverNodePublisher {
145
146
  toBlock: number;
146
147
  publicInputs: RootRollupPublicInputs;
147
148
  proof: Proof;
149
+ batchedBlobInputs: BatchedBlob;
150
+ attestations: ViemCommitteeAttestation[];
148
151
  }) {
149
- const { fromBlock, toBlock, publicInputs, proof } = args;
152
+ const { fromBlock, toBlock, publicInputs, batchedBlobInputs } = args;
150
153
 
151
154
  // Check that the block numbers match the expected epoch to be proven
152
155
  const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
153
- if (proven !== BigInt(fromBlock) - 1n) {
156
+ // Don't publish if proven is beyond our toBlock, pointless to do so
157
+ if (proven > BigInt(toBlock)) {
154
158
  throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
155
159
  }
160
+ // toBlock can't be greater than pending
156
161
  if (toBlock > pending) {
157
162
  throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
158
163
  }
159
164
 
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) {
165
+ // Check the archive for the immediate block before the epoch
166
+ const blockLog = await this.rollupContract.getBlock(BigInt(fromBlock - 1));
167
+ if (publicInputs.previousArchiveRoot.toString() !== blockLog.archive) {
163
168
  throw new Error(
164
- `Previous archive root mismatch: ${publicInputs.previousArchive.root.toString()} !== ${blockLog.archive}`,
169
+ `Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${blockLog.archive}`,
165
170
  );
166
171
  }
167
- // TODO: Remove zero check once we inject the proper zero blockhash
168
- if (blockLog.blockHash !== Fr.ZERO.toString() && publicInputs.previousBlockHash.toString() !== blockLog.blockHash) {
172
+
173
+ // Check the archive for the last block in the epoch
174
+ const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
175
+ if (publicInputs.endArchiveRoot.toString() !== endBlockLog.archive) {
169
176
  throw new Error(
170
- `Previous block hash mismatch: ${publicInputs.previousBlockHash.toString()} !== ${blockLog.blockHash}`,
177
+ `End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endBlockLog.archive}`,
171
178
  );
172
179
  }
173
180
 
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) {
181
+ // Check the batched blob inputs from the root rollup against the batched blob computed in ts
182
+ if (!publicInputs.blobPublicInputs.equals(FinalBlobAccumulatorPublicInputs.fromBatchedBlob(batchedBlobInputs))) {
177
183
  throw new Error(
178
- `End archive root mismatch: ${publicInputs.endArchive.root.toString()} !== ${endBlockLog.archive}`,
184
+ `Batched blob mismatch: ${inspect(publicInputs.blobPublicInputs)} !== ${inspect(FinalBlobAccumulatorPublicInputs.fromBatchedBlob(batchedBlobInputs))}`,
179
185
  );
180
186
  }
181
- if (publicInputs.endBlockHash.toString() !== endBlockLog.blockHash) {
182
- throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockLog.blockHash}`);
183
- }
184
187
 
185
188
  // 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];
189
+ const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(
190
+ this.getEpochProofPublicInputsArgs(args),
191
+ );
192
+ const argsPublicInputs = [...publicInputs.toFields()];
191
193
 
192
194
  if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b) => a.equals(b))) {
193
195
  const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
@@ -202,34 +204,23 @@ export class ProverNodePublisher {
202
204
  toBlock: number;
203
205
  publicInputs: RootRollupPublicInputs;
204
206
  proof: Proof;
207
+ batchedBlobInputs: BatchedBlob;
208
+ attestations: ViemCommitteeAttestation[];
205
209
  }): 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;
210
+ const txArgs = [this.getSubmitEpochProofArgs(args)] as const;
220
211
 
221
- this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
212
+ this.log.info(`Submitting epoch proof to L1 rollup contract`, {
213
+ proofSize: args.proof.withoutPublicInputs().length,
214
+ fromBlock: args.fromBlock,
215
+ toBlock: args.toBlock,
216
+ });
222
217
  const data = encodeFunctionData({
223
218
  abi: RollupAbi,
224
219
  functionName: 'submitEpochRootProof',
225
220
  args: txArgs,
226
221
  });
227
222
  try {
228
- const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
229
- to: this.rollupContract.address,
230
- data,
231
- });
232
-
223
+ const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
233
224
  return receipt;
234
225
  } catch (err) {
235
226
  this.log.error(`Rollup submit epoch proof failed`, err);
@@ -249,38 +240,52 @@ export class ProverNodePublisher {
249
240
  }
250
241
  }
251
242
 
252
- private getSubmitEpochProofArgs(args: {
243
+ private getEpochProofPublicInputsArgs(args: {
253
244
  fromBlock: number;
254
245
  toBlock: number;
255
246
  publicInputs: RootRollupPublicInputs;
256
- proof: Proof;
247
+ batchedBlobInputs: BatchedBlob;
248
+ attestations: ViemCommitteeAttestation[];
257
249
  }) {
250
+ // Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
258
251
  return [
259
- BigInt(args.fromBlock),
260
- BigInt(args.toBlock),
252
+ BigInt(args.fromBlock) /*_start*/,
253
+ BigInt(args.toBlock) /*_end*/,
261
254
  {
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(),
255
+ previousArchive: args.publicInputs.previousArchiveRoot.toString(),
256
+ endArchive: args.publicInputs.endArchiveRoot.toString(),
268
257
  proverId: EthAddress.fromField(args.publicInputs.proverId).toString(),
269
- },
258
+ } /*_args*/,
270
259
  makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
271
260
  i % 2 === 0
272
261
  ? args.publicInputs.fees[i / 2].recipient.toField().toString()
273
262
  : 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')}`,
263
+ ) /*_fees*/,
264
+ args.batchedBlobInputs.getEthBlobEvaluationInputs() /*_blobPublicInputs*/,
280
265
  ] as const;
281
266
  }
282
267
 
283
- protected async sleepOrInterrupted() {
284
- await this.interruptibleSleep.sleep(this.sleepTimeMs);
268
+ private getSubmitEpochProofArgs(args: {
269
+ fromBlock: number;
270
+ toBlock: number;
271
+ publicInputs: RootRollupPublicInputs;
272
+ proof: Proof;
273
+ batchedBlobInputs: BatchedBlob;
274
+ attestations: ViemCommitteeAttestation[];
275
+ }) {
276
+ // Returns arguments for EpochProofLib.sol -> submitEpochRootProof()
277
+ const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
278
+ const argsArray = this.getEpochProofPublicInputsArgs(args);
279
+ return {
280
+ start: argsArray[0],
281
+ end: argsArray[1],
282
+ args: argsArray[2],
283
+ fees: argsArray[3],
284
+ attestations: new CommitteeAttestationsAndSigners(
285
+ args.attestations.map(a => CommitteeAttestation.fromViem(a)),
286
+ ).getPackedAttestations(),
287
+ blobInputs: argsArray[4],
288
+ proof: proofHex,
289
+ };
285
290
  }
286
291
  }