@aztec/prover-node 0.0.1-commit.03f7ef2 → 0.0.1-commit.04d373f

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 (49) hide show
  1. package/dest/actions/download-epoch-proving-job.d.ts +1 -1
  2. package/dest/actions/download-epoch-proving-job.js +1 -1
  3. package/dest/actions/rerun-epoch-proving-job.d.ts +5 -3
  4. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  5. package/dest/actions/rerun-epoch-proving-job.js +9 -7
  6. package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
  7. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
  8. package/dest/bin/run-failed-epoch.js +6 -5
  9. package/dest/config.d.ts +5 -8
  10. package/dest/config.d.ts.map +1 -1
  11. package/dest/config.js +16 -19
  12. package/dest/factory.d.ts +19 -15
  13. package/dest/factory.d.ts.map +1 -1
  14. package/dest/factory.js +45 -64
  15. package/dest/index.d.ts +2 -1
  16. package/dest/index.d.ts.map +1 -1
  17. package/dest/index.js +1 -0
  18. package/dest/job/epoch-proving-job.d.ts +7 -2
  19. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  20. package/dest/job/epoch-proving-job.js +600 -52
  21. package/dest/metrics.d.ts +21 -1
  22. package/dest/metrics.d.ts.map +1 -1
  23. package/dest/metrics.js +73 -100
  24. package/dest/monitors/epoch-monitor.d.ts +1 -1
  25. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  26. package/dest/monitors/epoch-monitor.js +12 -19
  27. package/dest/prover-node-publisher.d.ts +25 -5
  28. package/dest/prover-node-publisher.d.ts.map +1 -1
  29. package/dest/prover-node-publisher.js +210 -14
  30. package/dest/prover-node.d.ts +20 -11
  31. package/dest/prover-node.d.ts.map +1 -1
  32. package/dest/prover-node.js +429 -46
  33. package/dest/prover-publisher-factory.d.ts +7 -5
  34. package/dest/prover-publisher-factory.d.ts.map +1 -1
  35. package/dest/prover-publisher-factory.js +7 -5
  36. package/package.json +24 -23
  37. package/src/actions/download-epoch-proving-job.ts +1 -1
  38. package/src/actions/rerun-epoch-proving-job.ts +20 -7
  39. package/src/actions/upload-epoch-proof-failure.ts +1 -1
  40. package/src/bin/run-failed-epoch.ts +5 -3
  41. package/src/config.ts +23 -31
  42. package/src/factory.ts +77 -104
  43. package/src/index.ts +1 -0
  44. package/src/job/epoch-proving-job.ts +151 -47
  45. package/src/metrics.ts +91 -83
  46. package/src/monitors/epoch-monitor.ts +6 -14
  47. package/src/prover-node-publisher.ts +245 -19
  48. package/src/prover-node.ts +31 -31
  49. package/src/prover-publisher-factory.ts +16 -10
@@ -1,17 +1,16 @@
1
- import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
1
  import { asyncPool } from '@aztec/foundation/async-pool';
3
2
  import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
4
- import { padArrayEnd } from '@aztec/foundation/collection';
5
3
  import { Fr } from '@aztec/foundation/curves/bn254';
6
- import { createLogger } from '@aztec/foundation/log';
4
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
7
5
  import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
8
6
  import { Timer } from '@aztec/foundation/timer';
7
+ import { AVM_MAX_CONCURRENT_SIMULATIONS } from '@aztec/native';
9
8
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
9
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
10
  import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
12
11
  import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
13
12
  import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
14
- import type { L2BlockNew, L2BlockSource } from '@aztec/stdlib/block';
13
+ import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
15
14
  import type { Checkpoint } from '@aztec/stdlib/checkpoint';
16
15
  import {
17
16
  type EpochProver,
@@ -19,8 +18,8 @@ import {
19
18
  EpochProvingJobTerminalState,
20
19
  type ForkMerkleTreeOperations,
21
20
  } from '@aztec/stdlib/interfaces/server';
21
+ import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging';
22
22
  import { CheckpointConstantData } from '@aztec/stdlib/rollup';
23
- import { MerkleTreeId } from '@aztec/stdlib/trees';
24
23
  import type { ProcessedTx, Tx } from '@aztec/stdlib/tx';
25
24
  import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
26
25
 
@@ -43,10 +42,11 @@ export type EpochProvingJobOptions = {
43
42
  */
44
43
  export class EpochProvingJob implements Traceable {
45
44
  private state: EpochProvingJobState = 'initialized';
46
- private log = createLogger('prover-node:epoch-proving-job');
45
+ private log: Logger;
47
46
  private uuid: string;
48
47
 
49
48
  private runPromise: Promise<void> | undefined;
49
+ private abortController = new AbortController();
50
50
  private epochCheckPromise: RunningPromise | undefined;
51
51
  private deadlineTimeoutHandler: NodeJS.Timeout | undefined;
52
52
 
@@ -57,14 +57,19 @@ export class EpochProvingJob implements Traceable {
57
57
  private dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>,
58
58
  private prover: EpochProver,
59
59
  private publicProcessorFactory: PublicProcessorFactory,
60
- private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
60
+ private publisher: Pick<ProverNodePublisher, 'submitEpochProof' | 'analyzeEpochProofSubmission'>,
61
61
  private l2BlockSource: L2BlockSource | undefined,
62
62
  private metrics: ProverNodeJobMetrics,
63
63
  private deadline: Date | undefined,
64
64
  private config: EpochProvingJobOptions,
65
+ bindings?: LoggerBindings,
65
66
  ) {
66
67
  validateEpochProvingJobData(data);
67
68
  this.uuid = crypto.randomUUID();
69
+ this.log = createLogger('prover-node:epoch-proving-job', {
70
+ ...bindings,
71
+ instanceId: `epoch-${data.epochNumber}`,
72
+ });
68
73
  this.tracer = metrics.tracer;
69
74
  }
70
75
 
@@ -143,19 +148,32 @@ export class EpochProvingJob implements Traceable {
143
148
  this.runPromise = promise;
144
149
 
145
150
  try {
151
+ const blobTimer = new Timer();
146
152
  const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
147
153
  const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
154
+ this.metrics.recordBlobProcessing(blobTimer.ms());
148
155
 
149
156
  this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
157
+ const chonkTimer = new Timer();
150
158
  await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
159
+ this.metrics.recordChonkVerifier(chonkTimer.ms());
151
160
 
152
161
  // Everything in the epoch should have the same chainId and version.
153
162
  const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
154
163
 
155
164
  const previousBlockHeaders = this.gatherPreviousBlockHeaders();
156
165
 
157
- await asyncPool(this.config.parallelBlockLimit ?? 32, this.checkpoints, async checkpoint => {
166
+ const allCheckpointsTimer = new Timer();
167
+
168
+ const parallelism = this.config.parallelBlockLimit
169
+ ? this.config.parallelBlockLimit
170
+ : AVM_MAX_CONCURRENT_SIMULATIONS > 0
171
+ ? AVM_MAX_CONCURRENT_SIMULATIONS
172
+ : this.checkpoints.length;
173
+
174
+ await this.processCheckpoints(parallelism, async checkpoint => {
158
175
  this.checkState();
176
+ const checkpointTimer = new Timer();
159
177
 
160
178
  const checkpointIndex = checkpoint.number - fromCheckpoint;
161
179
  const checkpointConstants = CheckpointConstantData.from({
@@ -172,11 +190,12 @@ export class EpochProvingJob implements Traceable {
172
190
  const previousHeader = previousBlockHeaders[checkpointIndex];
173
191
  const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
174
192
 
175
- this.log.verbose(`Starting processing checkpoint ${checkpoint.number}`, {
193
+ this.log.debug(`Starting processing checkpoint ${checkpoint.number}`, {
176
194
  number: checkpoint.number,
177
195
  checkpointHash: checkpoint.hash().toString(),
178
- lastArchive: checkpoint.header.lastArchiveRoot,
179
- previousHeader: previousHeader.hash(),
196
+ headerHash: checkpoint.header.hash().toString(),
197
+ numL1ToL2Messages: l1ToL2Messages.length,
198
+ previousBlockNumber: previousHeader.globalVariables.blockNumber,
180
199
  uuid: this.uuid,
181
200
  });
182
201
 
@@ -188,7 +207,9 @@ export class EpochProvingJob implements Traceable {
188
207
  previousHeader,
189
208
  );
190
209
 
191
- for (const block of checkpoint.blocks) {
210
+ for (let blockIndex = 0; blockIndex < checkpoint.blocks.length; blockIndex++) {
211
+ const blockTimer = new Timer();
212
+ const block = checkpoint.blocks[blockIndex];
192
213
  const globalVariables = block.header.globalVariables;
193
214
  const txs = this.getTxs(block);
194
215
 
@@ -206,20 +227,28 @@ export class EpochProvingJob implements Traceable {
206
227
  // Start block proving
207
228
  await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
208
229
 
209
- // Process public fns
210
- const db = await this.createFork(BlockNumber(block.number - 1), l1ToL2Messages);
211
- const config = PublicSimulatorConfig.from({
212
- proverId: this.prover.getProverId().toField(),
213
- skipFeeEnforcement: false,
214
- collectDebugLogs: false,
215
- collectHints: true,
216
- collectPublicInputs: true,
217
- collectStatistics: false,
218
- });
219
- const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
220
- const processed = await this.processTxs(publicProcessor, txs);
221
- await this.prover.addTxs(processed);
222
- await db.close();
230
+ // Process public fns. L1 to L2 messages are only inserted for the first block of a checkpoint,
231
+ // as the fork for subsequent blocks already includes them from the previous block's synced state.
232
+ {
233
+ await using db = await this.createFork(
234
+ BlockNumber(block.number - 1),
235
+ blockIndex === 0 ? l1ToL2Messages : undefined,
236
+ );
237
+ this.checkState();
238
+ const config = PublicSimulatorConfig.from({
239
+ proverId: this.prover.getProverId().toField(),
240
+ skipFeeEnforcement: false,
241
+ collectDebugLogs: false,
242
+ collectHints: true,
243
+ collectPublicInputs: true,
244
+ collectStatistics: false,
245
+ });
246
+ const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
247
+ const processed = await this.processTxs(publicProcessor, txs);
248
+ this.checkState();
249
+ await this.prover.addTxs(processed);
250
+ }
251
+ this.checkState();
223
252
  this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
224
253
  blockNumber: block.number,
225
254
  blockHash: (await block.hash()).toString(),
@@ -229,8 +258,11 @@ export class EpochProvingJob implements Traceable {
229
258
  // Mark block as completed to pad it
230
259
  const expectedBlockHeader = block.header;
231
260
  await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
261
+ this.metrics.recordBlockProcessing(blockTimer.ms());
232
262
  }
263
+ this.metrics.recordCheckpointProcessing(checkpointTimer.ms());
233
264
  });
265
+ this.metrics.recordAllCheckpointsProcessing(allCheckpointsTimer.ms());
234
266
 
235
267
  const executionTime = timer.ms();
236
268
 
@@ -242,8 +274,21 @@ export class EpochProvingJob implements Traceable {
242
274
 
243
275
  if (this.config.skipSubmitProof) {
244
276
  this.log.info(
245
- `Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
277
+ `Proof publishing is disabled. Analyzing estimated L1 fees for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
246
278
  );
279
+ try {
280
+ await this.publisher.analyzeEpochProofSubmission({
281
+ fromCheckpoint,
282
+ toCheckpoint,
283
+ epochNumber,
284
+ publicInputs,
285
+ proof,
286
+ batchedBlobInputs,
287
+ attestations,
288
+ });
289
+ } catch (err) {
290
+ this.log.warn(`Failed to analyze estimated L1 fees for epoch ${epochNumber}`, err);
291
+ }
247
292
  this.state = 'completed';
248
293
  this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
249
294
  return;
@@ -290,25 +335,63 @@ export class EpochProvingJob implements Traceable {
290
335
  }
291
336
 
292
337
  /**
293
- * Create a new db fork for tx processing, inserting all L1 to L2.
338
+ * Create a new db fork for tx processing, optionally inserting L1 to L2 messages.
339
+ * L1 to L2 messages should only be inserted for the first block in a checkpoint,
340
+ * as subsequent blocks' synced state already includes them.
294
341
  * REFACTOR: The prover already spawns a db fork of its own for each block, so we may be able to do away with just one fork.
295
342
  */
296
- private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) {
297
- const db = await this.dbProvider.fork(blockNumber);
298
- const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
299
- l1ToL2Messages,
300
- Fr.ZERO,
301
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
302
- 'Too many L1 to L2 messages',
303
- );
304
- this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
305
- blockNumber,
306
- l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
307
- });
308
- await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
343
+ private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[] | undefined) {
344
+ this.log.verbose(`Creating fork at ${blockNumber}`, { blockNumber });
345
+ // temporary stack to control fork lifetime
346
+ await using cleanup = new AsyncDisposableStack();
347
+ const db = cleanup.use(await this.dbProvider.fork(blockNumber));
348
+
349
+ if (l1ToL2Messages !== undefined) {
350
+ this.log.verbose(`Inserting ${l1ToL2Messages.length} L1 to L2 messages in fork`, {
351
+ blockNumber,
352
+ l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
353
+ });
354
+ await appendL1ToL2MessagesToTree(db, l1ToL2Messages);
355
+ }
356
+
357
+ // everything run succesfully so we can release this stack and give control of the fork's lifetime to the caller
358
+ cleanup.move();
309
359
  return db;
310
360
  }
311
361
 
362
+ private async processCheckpoints(
363
+ parallelism: number,
364
+ processCheckpoint: (checkpoint: Checkpoint) => Promise<void>,
365
+ ): Promise<void> {
366
+ let hasError = false;
367
+ let firstError: unknown;
368
+
369
+ await asyncPool(Math.max(parallelism, 1), this.checkpoints, async checkpoint => {
370
+ if (hasError || this.abortController.signal.aborted) {
371
+ return;
372
+ }
373
+
374
+ try {
375
+ this.checkState();
376
+ await processCheckpoint(checkpoint);
377
+ } catch (err) {
378
+ if (!hasError) {
379
+ hasError = true;
380
+ firstError = err;
381
+ this.failProcessing();
382
+ }
383
+ }
384
+ });
385
+
386
+ if (hasError) {
387
+ throw firstError;
388
+ }
389
+
390
+ if (this.abortController.signal.aborted) {
391
+ this.checkState();
392
+ }
393
+ }
394
+
312
395
  private progressState(state: EpochProvingJobState) {
313
396
  this.checkState();
314
397
  this.state = state;
@@ -322,12 +405,24 @@ export class EpochProvingJob implements Traceable {
322
405
 
323
406
  public async stop(state: EpochProvingJobTerminalState = 'stopped') {
324
407
  this.state = state;
325
- this.prover.cancel();
408
+ this.interruptProcessing();
326
409
  if (this.runPromise) {
327
410
  await this.runPromise;
328
411
  }
329
412
  }
330
413
 
414
+ private failProcessing() {
415
+ if (!EpochProvingJobTerminalState.includes(this.state)) {
416
+ this.state = 'failed';
417
+ }
418
+ this.interruptProcessing();
419
+ }
420
+
421
+ private interruptProcessing() {
422
+ this.abortController.abort();
423
+ this.prover.cancel();
424
+ }
425
+
331
426
  private scheduleDeadlineStop() {
332
427
  const deadline = this.deadline;
333
428
  if (deadline) {
@@ -362,11 +457,16 @@ export class EpochProvingJob implements Traceable {
362
457
  const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
363
458
  this.epochCheckPromise = new RunningPromise(
364
459
  async () => {
365
- const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
366
- const blockHashes = await Promise.all(blocks.map(block => block.hash()));
460
+ const blockHeaders = (
461
+ await l2BlockSource.getBlocksData({ epoch: this.epochNumber, onlyCheckpointed: true })
462
+ ).map(d => d.header);
463
+ const blockHashes = await Promise.all(blockHeaders.map(header => header.hash()));
367
464
  const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
368
465
  const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
369
- if (blocks.length !== thisBlocks.length || !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))) {
466
+ if (
467
+ blockHeaders.length !== thisBlocks.length ||
468
+ !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))
469
+ ) {
370
470
  this.log.warn('Epoch blocks changed underfoot', {
371
471
  uuid: this.uuid,
372
472
  epochNumber: this.epochNumber,
@@ -388,7 +488,7 @@ export class EpochProvingJob implements Traceable {
388
488
  return [this.data.previousBlockHeader, ...lastBlocks.map(block => block.header).slice(0, -1)];
389
489
  }
390
490
 
391
- private getTxs(block: L2BlockNew): Tx[] {
491
+ private getTxs(block: L2Block): Tx[] {
392
492
  return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
393
493
  }
394
494
 
@@ -398,7 +498,11 @@ export class EpochProvingJob implements Traceable {
398
498
 
399
499
  private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
400
500
  const { deadline } = this;
401
- const [processedTxs, failedTxs] = await publicProcessor.process(txs, { deadline });
501
+ const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
502
+ deadline,
503
+ signal: this.abortController.signal,
504
+ });
505
+ this.checkState();
402
506
 
403
507
  if (failedTxs.length) {
404
508
  const failedTxHashes = await Promise.all(failedTxs.map(({ tx }) => tx.getTxHash()));
package/src/metrics.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  type TelemetryClient,
14
14
  type Tracer,
15
15
  type UpDownCounter,
16
- ValueType,
16
+ createUpDownCounterWithDefault,
17
17
  } from '@aztec/telemetry-client';
18
18
 
19
19
  import { formatEther, formatUnits } from 'viem';
@@ -25,33 +25,30 @@ export class ProverNodeJobMetrics {
25
25
  provingJobBlocks: Gauge;
26
26
  provingJobTransactions: Gauge;
27
27
 
28
+ private blobProcessingDuration: Gauge;
29
+ private chonkVerifierDuration: Gauge;
30
+ private blockProcessingDuration: Histogram;
31
+ private checkpointProcessingDuration: Histogram;
32
+ private allCheckpointsProcessingDuration: Gauge;
33
+
28
34
  constructor(
29
35
  private meter: Meter,
30
36
  public readonly tracer: Tracer,
31
37
  private logger = createLogger('prover-node:publisher:metrics'),
32
38
  ) {
33
- this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
34
- description: 'Duration of execution of an epoch by the prover',
35
- unit: 'ms',
36
- valueType: ValueType.INT,
37
- });
38
- this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
39
- description: 'Duration of proving job',
40
- unit: 's',
41
- valueType: ValueType.DOUBLE,
42
- });
43
- this.provingJobCheckpoints = this.meter.createGauge(Metrics.PROVER_NODE_JOB_CHECKPOINTS, {
44
- description: 'Number of checkpoints in a proven epoch',
45
- valueType: ValueType.INT,
46
- });
47
- this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
48
- description: 'Number of blocks in a proven epoch',
49
- valueType: ValueType.INT,
50
- });
51
- this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
52
- description: 'Number of transactions in a proven epoch',
53
- valueType: ValueType.INT,
54
- });
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
+ );
55
52
  }
56
53
 
57
54
  public recordProvingJob(
@@ -67,6 +64,26 @@ export class ProverNodeJobMetrics {
67
64
  this.provingJobBlocks.record(Math.floor(numBlocks));
68
65
  this.provingJobTransactions.record(Math.floor(numTxs));
69
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
+ }
70
87
  }
71
88
 
72
89
  export class ProverNodeRewardsMetrics {
@@ -81,15 +98,9 @@ export class ProverNodeRewardsMetrics {
81
98
  private rollup: RollupContract,
82
99
  private logger = createLogger('prover-node:publisher:metrics'),
83
100
  ) {
84
- this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
85
- valueType: ValueType.DOUBLE,
86
- description: 'The rewards earned',
87
- });
101
+ this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH);
88
102
 
89
- this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
90
- valueType: ValueType.DOUBLE,
91
- description: 'The rewards earned (total)',
92
- });
103
+ this.accumulatedRewards = createUpDownCounterWithDefault(this.meter, Metrics.PROVER_NODE_REWARDS_TOTAL);
93
104
  }
94
105
 
95
106
  public async start() {
@@ -129,6 +140,13 @@ export class ProverNodeRewardsMetrics {
129
140
  };
130
141
  }
131
142
 
143
+ export type EstimatedSubmitProofStats = {
144
+ gasLimit: bigint;
145
+ baseFeePerGas: bigint;
146
+ maxPriorityFeePerGas: bigint;
147
+ estimatedTotalFee: bigint;
148
+ };
149
+
132
150
  export class ProverNodePublisherMetrics {
133
151
  gasPrice: Histogram;
134
152
  txCount: UpDownCounter;
@@ -140,6 +158,10 @@ export class ProverNodePublisherMetrics {
140
158
  txBlobDataGasCost: Histogram;
141
159
  txTotalFee: Histogram;
142
160
 
161
+ private txGasEstimated: Histogram;
162
+ private gasPriceEstimated: Histogram;
163
+ private txTotalFeeEstimated: Histogram;
164
+
143
165
  private senderBalance: Gauge;
144
166
  private meter: Meter;
145
167
 
@@ -150,68 +172,34 @@ export class ProverNodePublisherMetrics {
150
172
  ) {
151
173
  this.meter = client.getMeter(name);
152
174
 
153
- this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
154
- description: 'The gas price used for transactions',
155
- unit: 'gwei',
156
- valueType: ValueType.DOUBLE,
157
- });
175
+ this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE);
158
176
 
159
- this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
160
- description: 'The number of transactions processed',
177
+ this.txCount = createUpDownCounterWithDefault(this.meter, Metrics.L1_PUBLISHER_TX_COUNT, {
178
+ [Attributes.L1_TX_TYPE]: ['submitProof'],
179
+ [Attributes.OK]: [true, false],
161
180
  });
162
181
 
163
- this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
164
- description: 'The duration of transaction processing',
165
- unit: 'ms',
166
- valueType: ValueType.INT,
167
- });
182
+ this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION);
168
183
 
169
- this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
170
- description: 'The gas consumed by transactions',
171
- unit: 'gas',
172
- valueType: ValueType.INT,
173
- });
184
+ this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS);
174
185
 
175
- this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
176
- description: 'The size of the calldata in transactions',
177
- unit: 'By',
178
- valueType: ValueType.INT,
179
- });
186
+ this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE);
180
187
 
181
- this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
182
- description: 'The gas consumed by the calldata in transactions',
183
- unit: 'gas',
184
- valueType: ValueType.INT,
185
- });
188
+ this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS);
186
189
 
187
- this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
188
- description: 'The amount of blob gas used in transactions',
189
- unit: 'gas',
190
- valueType: ValueType.INT,
191
- });
190
+ this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED);
192
191
 
193
- this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
194
- description: 'The gas cost of blobs in transactions',
195
- unit: 'gwei',
196
- valueType: ValueType.INT,
197
- });
192
+ this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST);
198
193
 
199
- this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
200
- description: 'How much L1 tx costs',
201
- unit: 'gwei',
202
- valueType: ValueType.DOUBLE,
203
- advice: {
204
- explicitBucketBoundaries: [
205
- 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,
206
- ],
207
- },
208
- });
194
+ this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE);
209
195
 
210
- this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
211
- unit: 'eth',
212
- description: 'The balance of the sender address',
213
- valueType: ValueType.DOUBLE,
214
- });
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);
215
203
  }
216
204
 
217
205
  recordFailedTx() {
@@ -225,6 +213,26 @@ export class ProverNodePublisherMetrics {
225
213
  this.recordTx(durationMs, stats);
226
214
  }
227
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
+
228
236
  public recordSenderBalance(wei: bigint, senderAddress: string) {
229
237
  const eth = parseFloat(formatEther(wei, 'wei'));
230
238
  this.senderBalance.record(eth, {
@@ -4,13 +4,7 @@ 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,20 +85,20 @@ export class EpochMonitor implements Traceable {
93
85
  }
94
86
 
95
87
  if (this.options.provingDelayMs) {
96
- this.log.debug(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
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.debug(`Epoch ${epochToProve} is ready to be proven`);
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
  }
104
96
  }
105
97
 
106
98
  private async getEpochNumberToProve() {
107
- const lastBlockProven = await this.l2BlockSource.getProvenBlockNumber();
99
+ const lastBlockProven = (await this.l2BlockSource.getBlockNumber({ tag: 'proven' })) ?? BlockNumber.ZERO;
108
100
  const firstBlockToProve = BlockNumber(lastBlockProven + 1);
109
- const firstBlockHeaderToProve = await this.l2BlockSource.getBlockHeader(firstBlockToProve);
101
+ const firstBlockHeaderToProve = (await this.l2BlockSource.getBlockData({ number: firstBlockToProve }))?.header;
110
102
  if (!firstBlockHeaderToProve) {
111
103
  return { epochToProve: undefined, blockNumber: firstBlockToProve };
112
104
  }