@aztec-labs/prover-node 6.0.0-nightly.20260829
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/README.md +573 -0
- package/dest/actions/download-epoch-proving-job.d.ts +18 -0
- package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/download-epoch-proving-job.js +37 -0
- package/dest/actions/index.d.ts +3 -0
- package/dest/actions/index.d.ts.map +1 -0
- package/dest/actions/index.js +2 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts +23 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/rerun-epoch-proving-job.js +264 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
- package/dest/actions/upload-epoch-proof-failure.js +78 -0
- package/dest/bin/run-failed-epoch.d.ts +2 -0
- package/dest/bin/run-failed-epoch.d.ts.map +1 -0
- package/dest/bin/run-failed-epoch.js +68 -0
- package/dest/checkpoint-store.d.ts +95 -0
- package/dest/checkpoint-store.d.ts.map +1 -0
- package/dest/checkpoint-store.js +178 -0
- package/dest/config.d.ts +31 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +143 -0
- package/dest/factory.d.ts +36 -0
- package/dest/factory.d.ts.map +1 -0
- package/dest/factory.js +105 -0
- package/dest/index.d.ts +8 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +7 -0
- package/dest/job/checkpoint-prover.d.ts +169 -0
- package/dest/job/checkpoint-prover.d.ts.map +1 -0
- package/dest/job/checkpoint-prover.js +413 -0
- package/dest/job/epoch-proving-job-data.d.ts +20 -0
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
- package/dest/job/epoch-proving-job-data.js +61 -0
- package/dest/job/epoch-session.d.ts +160 -0
- package/dest/job/epoch-session.d.ts.map +1 -0
- package/dest/job/epoch-session.js +744 -0
- package/dest/job/top-tree-job.d.ts +82 -0
- package/dest/job/top-tree-job.d.ts.map +1 -0
- package/dest/job/top-tree-job.js +152 -0
- package/dest/metrics.d.ts +88 -0
- package/dest/metrics.d.ts.map +1 -0
- package/dest/metrics.js +264 -0
- package/dest/monitors/epoch-monitor.d.ts +43 -0
- package/dest/monitors/epoch-monitor.d.ts.map +1 -0
- package/dest/monitors/epoch-monitor.js +111 -0
- package/dest/monitors/index.d.ts +2 -0
- package/dest/monitors/index.d.ts.map +1 -0
- package/dest/monitors/index.js +1 -0
- package/dest/proof-publishing-service.d.ts +161 -0
- package/dest/proof-publishing-service.d.ts.map +1 -0
- package/dest/proof-publishing-service.js +335 -0
- package/dest/prover-node-publisher.d.ts +71 -0
- package/dest/prover-node-publisher.d.ts.map +1 -0
- package/dest/prover-node-publisher.js +346 -0
- package/dest/prover-node.d.ts +185 -0
- package/dest/prover-node.d.ts.map +1 -0
- package/dest/prover-node.js +1056 -0
- package/dest/prover-publisher-factory.d.ts +27 -0
- package/dest/prover-publisher-factory.d.ts.map +1 -0
- package/dest/prover-publisher-factory.js +29 -0
- package/dest/session-manager.d.ts +158 -0
- package/dest/session-manager.d.ts.map +1 -0
- package/dest/session-manager.js +493 -0
- package/dest/test/index.d.ts +13 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +3 -0
- package/package.json +102 -0
- package/src/actions/download-epoch-proving-job.ts +43 -0
- package/src/actions/index.ts +2 -0
- package/src/actions/rerun-epoch-proving-job.ts +224 -0
- package/src/actions/upload-epoch-proof-failure.ts +87 -0
- package/src/bin/run-failed-epoch.ts +78 -0
- package/src/checkpoint-store.ts +212 -0
- package/src/config.ts +208 -0
- package/src/factory.ts +191 -0
- package/src/index.ts +7 -0
- package/src/job/checkpoint-prover.ts +551 -0
- package/src/job/epoch-proving-job-data.ts +94 -0
- package/src/job/epoch-session.ts +466 -0
- package/src/job/top-tree-job.ts +227 -0
- package/src/metrics.ts +323 -0
- package/src/monitors/epoch-monitor.ts +110 -0
- package/src/monitors/index.ts +1 -0
- package/src/proof-publishing-service.ts +427 -0
- package/src/prover-node-publisher.ts +473 -0
- package/src/prover-node.ts +830 -0
- package/src/prover-publisher-factory.ts +48 -0
- package/src/session-manager.ts +593 -0
- package/src/test/index.ts +14 -0
package/src/metrics.ts
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { RollupContract } from '@aztec-labs/ethereum/contracts';
|
|
2
|
+
import type { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
3
|
+
import { createLogger } from '@aztec-labs/foundation/log';
|
|
4
|
+
import type { L1PublishProofStats, L1PublishStats } from '@aztec-labs/stdlib/stats';
|
|
5
|
+
import {
|
|
6
|
+
Attributes,
|
|
7
|
+
type BatchObservableResult,
|
|
8
|
+
type Gauge,
|
|
9
|
+
type Histogram,
|
|
10
|
+
type Meter,
|
|
11
|
+
Metrics,
|
|
12
|
+
type ObservableGauge,
|
|
13
|
+
type TelemetryClient,
|
|
14
|
+
type Tracer,
|
|
15
|
+
type UpDownCounter,
|
|
16
|
+
createUpDownCounterWithDefault,
|
|
17
|
+
} from '@aztec-labs/telemetry-client';
|
|
18
|
+
import { formatEther, formatUnits } from 'viem';
|
|
19
|
+
|
|
20
|
+
import type { CheckpointStore } from './checkpoint-store.js';
|
|
21
|
+
import type { SessionManager } from './session-manager.js';
|
|
22
|
+
|
|
23
|
+
export class ProverNodeJobMetrics {
|
|
24
|
+
provingJobDuration: Histogram;
|
|
25
|
+
provingJobCheckpoints: Gauge;
|
|
26
|
+
provingJobBlocks: Gauge;
|
|
27
|
+
provingJobTransactions: Gauge;
|
|
28
|
+
|
|
29
|
+
private blobProcessingDuration: Gauge;
|
|
30
|
+
private blockProcessingDuration: Histogram;
|
|
31
|
+
private checkpointProcessingDuration: Histogram;
|
|
32
|
+
private checkpointProvingDuration: Histogram;
|
|
33
|
+
private checkpointBlocks: Histogram;
|
|
34
|
+
private checkpointTransactions: Histogram;
|
|
35
|
+
|
|
36
|
+
/** Observable gauges for live state. Registered via `observeState(...)` once the
|
|
37
|
+
* CheckpointStore and SessionManager are available. */
|
|
38
|
+
private activeCheckpoints: ObservableGauge | undefined;
|
|
39
|
+
private activeEpochSessions: ObservableGauge | undefined;
|
|
40
|
+
private stateObserver: ((observer: BatchObservableResult) => void) | undefined;
|
|
41
|
+
private stateObservedMetrics: ObservableGauge[] = [];
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
private meter: Meter,
|
|
45
|
+
public readonly tracer: Tracer,
|
|
46
|
+
private logger = createLogger('prover-node:publisher:metrics'),
|
|
47
|
+
) {
|
|
48
|
+
this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION);
|
|
49
|
+
this.provingJobCheckpoints = this.meter.createGauge(Metrics.PROVER_NODE_JOB_CHECKPOINTS);
|
|
50
|
+
this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS);
|
|
51
|
+
this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS);
|
|
52
|
+
|
|
53
|
+
this.blobProcessingDuration = this.meter.createGauge(Metrics.PROVER_NODE_BLOB_PROCESSING_LAST_DURATION);
|
|
54
|
+
this.blockProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_BLOCK_PROCESSING_DURATION);
|
|
55
|
+
this.checkpointProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_PROCESSING_DURATION);
|
|
56
|
+
this.checkpointProvingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_PROVING_DURATION);
|
|
57
|
+
this.checkpointBlocks = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_BLOCKS);
|
|
58
|
+
this.checkpointTransactions = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_TRANSACTIONS);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public recordProvingJob(totalTimeMs: number, numCheckpoints: number, numBlocks: number, numTxs: number) {
|
|
62
|
+
this.provingJobDuration.record(totalTimeMs / 1000);
|
|
63
|
+
this.provingJobCheckpoints.record(Math.floor(numCheckpoints));
|
|
64
|
+
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
65
|
+
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public recordBlobProcessing(durationMs: number) {
|
|
69
|
+
this.blobProcessingDuration.record(Math.ceil(durationMs));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public recordBlockProcessing(durationMs: number) {
|
|
73
|
+
this.blockProcessingDuration.record(Math.ceil(durationMs));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public recordCheckpointProcessing(durationMs: number, numBlocks: number, numTxs: number) {
|
|
77
|
+
this.checkpointProcessingDuration.record(Math.ceil(durationMs));
|
|
78
|
+
this.checkpointBlocks.record(Math.floor(numBlocks));
|
|
79
|
+
this.checkpointTransactions.record(Math.floor(numTxs));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public recordCheckpointProving(durationMs: number) {
|
|
83
|
+
this.checkpointProvingDuration.record(Math.ceil(durationMs));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Registers observable gauges for the prover-node's live state: how many canonical
|
|
88
|
+
* checkpoint provers are in the store, and how many epoch sessions are live (broken
|
|
89
|
+
* down by kind). Idempotent — repeated calls re-arm with the latest references.
|
|
90
|
+
*
|
|
91
|
+
* Call this once the `SessionManager` has been constructed (i.e. inside `ProverNode.start()`).
|
|
92
|
+
*/
|
|
93
|
+
public observeState(checkpointStore: CheckpointStore, sessionManager: SessionManager): void {
|
|
94
|
+
this.stopObservingState();
|
|
95
|
+
this.activeCheckpoints = this.meter.createObservableGauge(Metrics.PROVER_NODE_ACTIVE_CHECKPOINTS);
|
|
96
|
+
this.activeEpochSessions = this.meter.createObservableGauge(Metrics.PROVER_NODE_ACTIVE_EPOCH_SESSIONS);
|
|
97
|
+
this.stateObserver = (observer: BatchObservableResult) => {
|
|
98
|
+
observer.observe(this.activeCheckpoints!, checkpointStore.listAll().length);
|
|
99
|
+
let full = 0;
|
|
100
|
+
let partial = 0;
|
|
101
|
+
for (const session of sessionManager.allSessions()) {
|
|
102
|
+
if (session.isTerminal()) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (session.getKind() === 'full') {
|
|
106
|
+
full++;
|
|
107
|
+
} else {
|
|
108
|
+
partial++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
observer.observe(this.activeEpochSessions!, full, { [Attributes.EPOCH_SESSION_KIND]: 'full' });
|
|
112
|
+
observer.observe(this.activeEpochSessions!, partial, { [Attributes.EPOCH_SESSION_KIND]: 'partial' });
|
|
113
|
+
};
|
|
114
|
+
this.stateObservedMetrics = [this.activeCheckpoints, this.activeEpochSessions];
|
|
115
|
+
this.meter.addBatchObservableCallback(this.stateObserver, this.stateObservedMetrics);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Tears down the observable callback registered by `observeState`. Idempotent. */
|
|
119
|
+
public stopObservingState(): void {
|
|
120
|
+
if (this.stateObserver) {
|
|
121
|
+
this.meter.removeBatchObservableCallback(this.stateObserver, this.stateObservedMetrics);
|
|
122
|
+
this.stateObserver = undefined;
|
|
123
|
+
this.stateObservedMetrics = [];
|
|
124
|
+
this.activeCheckpoints = undefined;
|
|
125
|
+
this.activeEpochSessions = undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class ProverNodeRewardsMetrics {
|
|
131
|
+
private rewards: ObservableGauge;
|
|
132
|
+
private accumulatedRewards: UpDownCounter;
|
|
133
|
+
private prevEpoch = -1n;
|
|
134
|
+
private proofSubmissionEpochs = 0;
|
|
135
|
+
|
|
136
|
+
constructor(
|
|
137
|
+
private meter: Meter,
|
|
138
|
+
private coinbase: EthAddress,
|
|
139
|
+
private rollup: RollupContract,
|
|
140
|
+
private logger = createLogger('prover-node:publisher:metrics'),
|
|
141
|
+
) {
|
|
142
|
+
this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH);
|
|
143
|
+
|
|
144
|
+
this.accumulatedRewards = createUpDownCounterWithDefault(this.meter, Metrics.PROVER_NODE_REWARDS_TOTAL);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async start() {
|
|
148
|
+
const proofSubmissionEpochs = await this.rollup.getProofSubmissionEpochs();
|
|
149
|
+
this.proofSubmissionEpochs = Number(proofSubmissionEpochs);
|
|
150
|
+
this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public stop() {
|
|
154
|
+
this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private observe = async (observer: BatchObservableResult): Promise<void> => {
|
|
158
|
+
const epoch = await this.rollup.getCurrentEpochNumber();
|
|
159
|
+
|
|
160
|
+
if (epoch > this.proofSubmissionEpochs) {
|
|
161
|
+
// look at the prev epoch so that we get an accurate value, after proof submission window has closed
|
|
162
|
+
// For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
|
|
163
|
+
// Similarly, if the proof submission window is 0, and we are in epoch 1, we should be looking at epoch 0.
|
|
164
|
+
const closedEpoch = BigInt(epoch) - BigInt(this.proofSubmissionEpochs) - 1n;
|
|
165
|
+
const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
|
|
166
|
+
|
|
167
|
+
const fmt = parseFloat(formatUnits(rewards, 18));
|
|
168
|
+
|
|
169
|
+
observer.observe(this.rewards, fmt, {
|
|
170
|
+
[Attributes.COINBASE]: this.coinbase.toString(),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// only accumulate once per epoch
|
|
174
|
+
if (closedEpoch > this.prevEpoch) {
|
|
175
|
+
this.prevEpoch = closedEpoch;
|
|
176
|
+
this.accumulatedRewards.add(fmt, {
|
|
177
|
+
[Attributes.COINBASE]: this.coinbase.toString(),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type EstimatedSubmitProofStats = {
|
|
185
|
+
gasLimit: bigint;
|
|
186
|
+
baseFeePerGas: bigint;
|
|
187
|
+
maxPriorityFeePerGas: bigint;
|
|
188
|
+
estimatedTotalFee: bigint;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export class ProverNodePublisherMetrics {
|
|
192
|
+
gasPrice: Histogram;
|
|
193
|
+
txCount: UpDownCounter;
|
|
194
|
+
txDuration: Histogram;
|
|
195
|
+
txGas: Histogram;
|
|
196
|
+
txCalldataSize: Histogram;
|
|
197
|
+
txCalldataGas: Histogram;
|
|
198
|
+
txBlobDataGasUsed: Histogram;
|
|
199
|
+
txBlobDataGasCost: Histogram;
|
|
200
|
+
txTotalFee: Histogram;
|
|
201
|
+
|
|
202
|
+
private txGasEstimated: Histogram;
|
|
203
|
+
private gasPriceEstimated: Histogram;
|
|
204
|
+
private txTotalFeeEstimated: Histogram;
|
|
205
|
+
|
|
206
|
+
private senderBalance: Gauge;
|
|
207
|
+
private meter: Meter;
|
|
208
|
+
|
|
209
|
+
constructor(
|
|
210
|
+
public readonly client: TelemetryClient,
|
|
211
|
+
name = 'ProverNode',
|
|
212
|
+
private logger = createLogger('prover-node:publisher:metrics'),
|
|
213
|
+
) {
|
|
214
|
+
this.meter = client.getMeter(name);
|
|
215
|
+
|
|
216
|
+
this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE);
|
|
217
|
+
|
|
218
|
+
this.txCount = createUpDownCounterWithDefault(this.meter, Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
219
|
+
[Attributes.L1_TX_TYPE]: ['submitProof'],
|
|
220
|
+
[Attributes.OK]: [true, false],
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION);
|
|
224
|
+
|
|
225
|
+
this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS);
|
|
226
|
+
|
|
227
|
+
this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE);
|
|
228
|
+
|
|
229
|
+
this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS);
|
|
230
|
+
|
|
231
|
+
this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED);
|
|
232
|
+
|
|
233
|
+
this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST);
|
|
234
|
+
|
|
235
|
+
this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE);
|
|
236
|
+
|
|
237
|
+
this.txGasEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS);
|
|
238
|
+
|
|
239
|
+
this.gasPriceEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS_PRICE);
|
|
240
|
+
|
|
241
|
+
this.txTotalFeeEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_TOTAL_FEE);
|
|
242
|
+
|
|
243
|
+
this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
recordFailedTx() {
|
|
247
|
+
this.txCount.add(1, {
|
|
248
|
+
[Attributes.L1_TX_TYPE]: 'submitProof',
|
|
249
|
+
[Attributes.OK]: false,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
recordSubmitProof(durationMs: number, stats: L1PublishProofStats) {
|
|
254
|
+
this.recordTx(durationMs, stats);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public recordEstimatedSubmitProof(stats: EstimatedSubmitProofStats) {
|
|
258
|
+
const attributes = { [Attributes.L1_TX_TYPE]: 'submitProof' } as const;
|
|
259
|
+
|
|
260
|
+
this.txGasEstimated.record(Number(stats.gasLimit), attributes);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
this.gasPriceEstimated.record(
|
|
264
|
+
parseInt(formatEther(stats.baseFeePerGas + stats.maxPriorityFeePerGas, 'gwei'), 10),
|
|
265
|
+
);
|
|
266
|
+
} catch {
|
|
267
|
+
// ignore
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
this.txTotalFeeEstimated.record(parseFloat(formatEther(stats.estimatedTotalFee)));
|
|
272
|
+
} catch {
|
|
273
|
+
// ignore
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public recordSenderBalance(wei: bigint, senderAddress: string) {
|
|
278
|
+
const eth = parseFloat(formatEther(wei, 'wei'));
|
|
279
|
+
this.senderBalance.record(eth, {
|
|
280
|
+
[Attributes.SENDER_ADDRESS]: senderAddress,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private recordTx(durationMs: number, stats: L1PublishStats) {
|
|
285
|
+
const attributes = {
|
|
286
|
+
[Attributes.L1_TX_TYPE]: 'submitProof',
|
|
287
|
+
[Attributes.L1_SENDER]: stats.sender,
|
|
288
|
+
} as const;
|
|
289
|
+
|
|
290
|
+
this.txCount.add(1, {
|
|
291
|
+
...attributes,
|
|
292
|
+
[Attributes.OK]: true,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
this.txDuration.record(Math.ceil(durationMs), attributes);
|
|
296
|
+
this.txGas.record(
|
|
297
|
+
// safe to downcast - total block limit is 30M gas which fits in a JS number
|
|
298
|
+
Number(stats.gasUsed),
|
|
299
|
+
attributes,
|
|
300
|
+
);
|
|
301
|
+
this.txCalldataGas.record(stats.calldataGas, attributes);
|
|
302
|
+
this.txCalldataSize.record(stats.calldataSize, attributes);
|
|
303
|
+
|
|
304
|
+
this.txBlobDataGasCost.record(Number(stats.blobDataGas), attributes);
|
|
305
|
+
this.txBlobDataGasUsed.record(Number(stats.blobGasUsed), attributes);
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
|
|
309
|
+
} catch {
|
|
310
|
+
// ignore
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const executionFee = stats.gasUsed * stats.gasPrice;
|
|
314
|
+
const blobFee = stats.blobGasUsed * stats.blobDataGas;
|
|
315
|
+
const totalFee = executionFee + blobFee;
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
this.txTotalFee.record(parseFloat(formatEther(totalFee)));
|
|
319
|
+
} catch {
|
|
320
|
+
// ignore
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BlockNumber, EpochNumber } from '@aztec-labs/foundation/branded-types';
|
|
2
|
+
import { createLogger } from '@aztec-labs/foundation/log';
|
|
3
|
+
import { RunningPromise } from '@aztec-labs/foundation/running-promise';
|
|
4
|
+
import { sleep } from '@aztec-labs/foundation/sleep';
|
|
5
|
+
import type { L2BlockSource } from '@aztec-labs/stdlib/block';
|
|
6
|
+
import { type L1RollupConstants, getEpochAtSlot } from '@aztec-labs/stdlib/epoch-helpers';
|
|
7
|
+
import { type TelemetryClient, type Traceable, type Tracer, getTelemetryClient } from '@aztec-labs/telemetry-client';
|
|
8
|
+
|
|
9
|
+
export interface EpochMonitorHandler {
|
|
10
|
+
handleEpochReadyToProve(epochNumber: EpochNumber): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fires an event when a new epoch ready to prove is detected.
|
|
15
|
+
*
|
|
16
|
+
* We define an epoch as ready to prove when:
|
|
17
|
+
* - The epoch is complete
|
|
18
|
+
* - Its blocks have not been reorg'd out due to a missing L2 proof
|
|
19
|
+
* - Its first block is the immediate successor of the last proven block
|
|
20
|
+
*
|
|
21
|
+
* This class periodically hits the L2BlockSource.
|
|
22
|
+
* On start it will trigger the event for the last epoch ready to prove.
|
|
23
|
+
*/
|
|
24
|
+
export class EpochMonitor implements Traceable {
|
|
25
|
+
private runningPromise: RunningPromise;
|
|
26
|
+
private log = createLogger('prover-node:epoch-monitor');
|
|
27
|
+
public readonly tracer: Tracer;
|
|
28
|
+
|
|
29
|
+
private handler: EpochMonitorHandler | undefined;
|
|
30
|
+
private latestEpochNumber: EpochNumber | undefined;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
private readonly l2BlockSource: L2BlockSource,
|
|
34
|
+
private readonly l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
35
|
+
private options: { pollingIntervalMs: number; provingDelayMs?: number },
|
|
36
|
+
telemetry: TelemetryClient = getTelemetryClient(),
|
|
37
|
+
) {
|
|
38
|
+
this.tracer = telemetry.getTracer('EpochMonitor');
|
|
39
|
+
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.options.pollingIntervalMs);
|
|
40
|
+
if (this.options.provingDelayMs) {
|
|
41
|
+
this.log.warn(`Prover node epoch monitor running with delay of ${this.options.provingDelayMs}ms`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public static async create(
|
|
46
|
+
l2BlockSource: L2BlockSource,
|
|
47
|
+
options: { pollingIntervalMs: number; provingDelayMs?: number },
|
|
48
|
+
telemetry: TelemetryClient = getTelemetryClient(),
|
|
49
|
+
): Promise<EpochMonitor> {
|
|
50
|
+
const l1Constants = await l2BlockSource.getL1Constants();
|
|
51
|
+
return new EpochMonitor(l2BlockSource, l1Constants, options, telemetry);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public start(handler: EpochMonitorHandler) {
|
|
55
|
+
this.handler = handler;
|
|
56
|
+
this.runningPromise.start();
|
|
57
|
+
this.log.info('Started EpochMonitor', this.options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Exposed for testing */
|
|
61
|
+
public setHandler(handler: EpochMonitorHandler) {
|
|
62
|
+
this.handler = handler;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async stop() {
|
|
66
|
+
await this.runningPromise.stop();
|
|
67
|
+
this.log.info('Stopped EpochMonitor');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public async work() {
|
|
71
|
+
const { epochToProve, blockNumber, slotNumber } = await this.getEpochNumberToProve();
|
|
72
|
+
if (epochToProve === undefined) {
|
|
73
|
+
this.log.trace(`Next block to prove ${blockNumber} not yet mined`, { epochToProve, blockNumber, slotNumber });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (this.latestEpochNumber !== undefined && epochToProve <= this.latestEpochNumber) {
|
|
77
|
+
this.log.trace(`Epoch ${epochToProve} already processed`, { epochToProve, blockNumber, slotNumber });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const isCompleted = await this.l2BlockSource.isEpochComplete(epochToProve);
|
|
82
|
+
if (!isCompleted) {
|
|
83
|
+
this.log.trace(`Epoch ${epochToProve} is not complete`, { epochToProve, blockNumber, slotNumber });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.options.provingDelayMs) {
|
|
88
|
+
this.log.warn(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
|
|
89
|
+
await sleep(this.options.provingDelayMs);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.log.verbose(`Epoch ${epochToProve} is ready to be proven`);
|
|
93
|
+
if (await this.handler?.handleEpochReadyToProve(epochToProve)) {
|
|
94
|
+
this.latestEpochNumber = epochToProve;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async getEpochNumberToProve() {
|
|
99
|
+
const lastBlockProven = (await this.l2BlockSource.getBlockNumber({ tag: 'proven' })) ?? BlockNumber.ZERO;
|
|
100
|
+
const firstBlockToProve = BlockNumber(lastBlockProven + 1);
|
|
101
|
+
const firstBlockHeaderToProve = (await this.l2BlockSource.getBlockData({ number: firstBlockToProve }))?.header;
|
|
102
|
+
if (!firstBlockHeaderToProve) {
|
|
103
|
+
return { epochToProve: undefined, blockNumber: firstBlockToProve };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const firstSlotOfEpochToProve = firstBlockHeaderToProve.getSlot();
|
|
107
|
+
const epochToProve = getEpochAtSlot(firstSlotOfEpochToProve, this.l1Constants);
|
|
108
|
+
return { epochToProve, blockNumber: firstBlockToProve, slotNumber: firstSlotOfEpochToProve };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './epoch-monitor.js';
|