@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.
Files changed (90) hide show
  1. package/README.md +573 -0
  2. package/dest/actions/download-epoch-proving-job.d.ts +18 -0
  3. package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
  4. package/dest/actions/download-epoch-proving-job.js +37 -0
  5. package/dest/actions/index.d.ts +3 -0
  6. package/dest/actions/index.d.ts.map +1 -0
  7. package/dest/actions/index.js +2 -0
  8. package/dest/actions/rerun-epoch-proving-job.d.ts +23 -0
  9. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
  10. package/dest/actions/rerun-epoch-proving-job.js +264 -0
  11. package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
  12. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
  13. package/dest/actions/upload-epoch-proof-failure.js +78 -0
  14. package/dest/bin/run-failed-epoch.d.ts +2 -0
  15. package/dest/bin/run-failed-epoch.d.ts.map +1 -0
  16. package/dest/bin/run-failed-epoch.js +68 -0
  17. package/dest/checkpoint-store.d.ts +95 -0
  18. package/dest/checkpoint-store.d.ts.map +1 -0
  19. package/dest/checkpoint-store.js +178 -0
  20. package/dest/config.d.ts +31 -0
  21. package/dest/config.d.ts.map +1 -0
  22. package/dest/config.js +143 -0
  23. package/dest/factory.d.ts +36 -0
  24. package/dest/factory.d.ts.map +1 -0
  25. package/dest/factory.js +105 -0
  26. package/dest/index.d.ts +8 -0
  27. package/dest/index.d.ts.map +1 -0
  28. package/dest/index.js +7 -0
  29. package/dest/job/checkpoint-prover.d.ts +169 -0
  30. package/dest/job/checkpoint-prover.d.ts.map +1 -0
  31. package/dest/job/checkpoint-prover.js +413 -0
  32. package/dest/job/epoch-proving-job-data.d.ts +20 -0
  33. package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
  34. package/dest/job/epoch-proving-job-data.js +61 -0
  35. package/dest/job/epoch-session.d.ts +160 -0
  36. package/dest/job/epoch-session.d.ts.map +1 -0
  37. package/dest/job/epoch-session.js +744 -0
  38. package/dest/job/top-tree-job.d.ts +82 -0
  39. package/dest/job/top-tree-job.d.ts.map +1 -0
  40. package/dest/job/top-tree-job.js +152 -0
  41. package/dest/metrics.d.ts +88 -0
  42. package/dest/metrics.d.ts.map +1 -0
  43. package/dest/metrics.js +264 -0
  44. package/dest/monitors/epoch-monitor.d.ts +43 -0
  45. package/dest/monitors/epoch-monitor.d.ts.map +1 -0
  46. package/dest/monitors/epoch-monitor.js +111 -0
  47. package/dest/monitors/index.d.ts +2 -0
  48. package/dest/monitors/index.d.ts.map +1 -0
  49. package/dest/monitors/index.js +1 -0
  50. package/dest/proof-publishing-service.d.ts +161 -0
  51. package/dest/proof-publishing-service.d.ts.map +1 -0
  52. package/dest/proof-publishing-service.js +335 -0
  53. package/dest/prover-node-publisher.d.ts +71 -0
  54. package/dest/prover-node-publisher.d.ts.map +1 -0
  55. package/dest/prover-node-publisher.js +346 -0
  56. package/dest/prover-node.d.ts +185 -0
  57. package/dest/prover-node.d.ts.map +1 -0
  58. package/dest/prover-node.js +1056 -0
  59. package/dest/prover-publisher-factory.d.ts +27 -0
  60. package/dest/prover-publisher-factory.d.ts.map +1 -0
  61. package/dest/prover-publisher-factory.js +29 -0
  62. package/dest/session-manager.d.ts +158 -0
  63. package/dest/session-manager.d.ts.map +1 -0
  64. package/dest/session-manager.js +493 -0
  65. package/dest/test/index.d.ts +13 -0
  66. package/dest/test/index.d.ts.map +1 -0
  67. package/dest/test/index.js +3 -0
  68. package/package.json +102 -0
  69. package/src/actions/download-epoch-proving-job.ts +43 -0
  70. package/src/actions/index.ts +2 -0
  71. package/src/actions/rerun-epoch-proving-job.ts +224 -0
  72. package/src/actions/upload-epoch-proof-failure.ts +87 -0
  73. package/src/bin/run-failed-epoch.ts +78 -0
  74. package/src/checkpoint-store.ts +212 -0
  75. package/src/config.ts +208 -0
  76. package/src/factory.ts +191 -0
  77. package/src/index.ts +7 -0
  78. package/src/job/checkpoint-prover.ts +551 -0
  79. package/src/job/epoch-proving-job-data.ts +94 -0
  80. package/src/job/epoch-session.ts +466 -0
  81. package/src/job/top-tree-job.ts +227 -0
  82. package/src/metrics.ts +323 -0
  83. package/src/monitors/epoch-monitor.ts +110 -0
  84. package/src/monitors/index.ts +1 -0
  85. package/src/proof-publishing-service.ts +427 -0
  86. package/src/prover-node-publisher.ts +473 -0
  87. package/src/prover-node.ts +830 -0
  88. package/src/prover-publisher-factory.ts +48 -0
  89. package/src/session-manager.ts +593 -0
  90. 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';