@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.
- package/dest/actions/download-epoch-proving-job.d.ts +1 -1
- package/dest/actions/download-epoch-proving-job.js +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +5 -3
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +6 -5
- package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
- package/dest/bin/run-failed-epoch.js +5 -2
- package/dest/config.d.ts +8 -10
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +19 -21
- package/dest/factory.d.ts +20 -16
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +47 -62
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/job/epoch-proving-job-data.d.ts +7 -6
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
- package/dest/job/epoch-proving-job-data.js +24 -18
- package/dest/job/epoch-proving-job.d.ts +5 -4
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +524 -108
- package/dest/metrics.d.ts +24 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +76 -97
- package/dest/monitors/epoch-monitor.d.ts +1 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +8 -18
- package/dest/prover-node-publisher.d.ts +27 -10
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +97 -42
- package/dest/prover-node.d.ts +22 -12
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +442 -55
- package/dest/prover-publisher-factory.d.ts +10 -6
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +7 -5
- package/package.json +26 -25
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +12 -5
- package/src/actions/upload-epoch-proof-failure.ts +1 -1
- package/src/bin/run-failed-epoch.ts +5 -2
- package/src/config.ts +27 -33
- package/src/factory.ts +80 -104
- package/src/index.ts +1 -0
- package/src/job/epoch-proving-job-data.ts +28 -23
- package/src/job/epoch-proving-job.ts +165 -112
- package/src/metrics.ts +101 -81
- package/src/monitors/epoch-monitor.ts +6 -14
- package/src/prover-node-publisher.ts +139 -59
- package/src/prover-node.ts +54 -42
- 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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
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(
|
|
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
|
|
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
|
|
148
|
-
|
|
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.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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.
|
|
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.
|
|
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 {
|
|
3
|
-
import type {
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
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,
|
|
98
|
-
const ctx = { epochNumber,
|
|
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
|
|
143
|
+
this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
this.log.verbose('
|
|
146
|
+
this.log.verbose('Checkpoint data syncing interrupted', ctx);
|
|
143
147
|
return false;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
private async validateEpochProofSubmission(args: {
|
|
147
|
-
|
|
148
|
-
|
|
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 {
|
|
158
|
+
const { fromCheckpoint, toCheckpoint, publicInputs, batchedBlobInputs } = args;
|
|
155
159
|
|
|
156
|
-
// Check that the
|
|
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
|
|
159
|
-
if (proven >
|
|
160
|
-
throw new Error(
|
|
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
|
-
//
|
|
163
|
-
if (
|
|
164
|
-
throw new Error(
|
|
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
|
|
168
|
-
const
|
|
169
|
-
if (publicInputs.previousArchiveRoot.
|
|
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()} !== ${
|
|
179
|
+
`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive.toString()}`,
|
|
172
180
|
);
|
|
173
181
|
}
|
|
174
182
|
|
|
175
|
-
// Check the archive for the last
|
|
176
|
-
const
|
|
177
|
-
if (publicInputs.endArchiveRoot.
|
|
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()} !== ${
|
|
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
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
218
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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.
|
|
256
|
-
BigInt(args.
|
|
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(
|
|
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
|
-
|
|
273
|
-
|
|
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:
|
|
367
|
+
attestations: CommitteeAttestationsAndSigners.packAttestations(
|
|
288
368
|
args.attestations.map(a => CommitteeAttestation.fromViem(a)),
|
|
289
|
-
)
|
|
369
|
+
),
|
|
290
370
|
blobInputs: argsArray[4],
|
|
291
371
|
proof: proofHex,
|
|
292
372
|
};
|