@aztec/prover-node 0.0.0-test.1 → 0.0.1-commit.b655e406

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dest/actions/download-epoch-proving-job.d.ts +18 -0
  2. package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
  3. package/dest/actions/download-epoch-proving-job.js +37 -0
  4. package/dest/actions/index.d.ts +3 -0
  5. package/dest/actions/index.d.ts.map +1 -0
  6. package/dest/actions/index.js +2 -0
  7. package/dest/actions/rerun-epoch-proving-job.d.ts +11 -0
  8. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
  9. package/dest/actions/rerun-epoch-proving-job.js +40 -0
  10. package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
  11. package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
  12. package/dest/actions/upload-epoch-proof-failure.js +78 -0
  13. package/dest/bin/run-failed-epoch.d.ts +2 -0
  14. package/dest/bin/run-failed-epoch.d.ts.map +1 -0
  15. package/dest/bin/run-failed-epoch.js +67 -0
  16. package/dest/config.d.ts +12 -9
  17. package/dest/config.d.ts.map +1 -1
  18. package/dest/config.js +81 -14
  19. package/dest/factory.d.ts +12 -8
  20. package/dest/factory.d.ts.map +1 -1
  21. package/dest/factory.js +95 -31
  22. package/dest/index.d.ts +1 -1
  23. package/dest/index.d.ts.map +1 -1
  24. package/dest/index.js +1 -1
  25. package/dest/job/epoch-proving-job-data.d.ts +16 -0
  26. package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
  27. package/dest/job/epoch-proving-job-data.js +52 -0
  28. package/dest/job/epoch-proving-job.d.ts +30 -15
  29. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  30. package/dest/job/epoch-proving-job.js +149 -50
  31. package/dest/metrics.d.ts +28 -4
  32. package/dest/metrics.d.ts.map +1 -1
  33. package/dest/metrics.js +141 -35
  34. package/dest/monitors/epoch-monitor.d.ts +3 -1
  35. package/dest/monitors/epoch-monitor.d.ts.map +1 -1
  36. package/dest/monitors/epoch-monitor.js +15 -2
  37. package/dest/prover-node-publisher.d.ts +7 -10
  38. package/dest/prover-node-publisher.d.ts.map +1 -1
  39. package/dest/prover-node-publisher.js +59 -60
  40. package/dest/prover-node.d.ts +43 -39
  41. package/dest/prover-node.d.ts.map +1 -1
  42. package/dest/prover-node.js +171 -100
  43. package/dest/prover-publisher-factory.d.ts +21 -0
  44. package/dest/prover-publisher-factory.d.ts.map +1 -0
  45. package/dest/prover-publisher-factory.js +26 -0
  46. package/dest/test/index.d.ts +4 -2
  47. package/dest/test/index.d.ts.map +1 -1
  48. package/dest/test/index.js +1 -3
  49. package/package.json +36 -31
  50. package/src/actions/download-epoch-proving-job.ts +44 -0
  51. package/src/actions/index.ts +2 -0
  52. package/src/actions/rerun-epoch-proving-job.ts +61 -0
  53. package/src/actions/upload-epoch-proof-failure.ts +88 -0
  54. package/src/bin/run-failed-epoch.ts +77 -0
  55. package/src/config.ts +108 -24
  56. package/src/factory.ts +161 -43
  57. package/src/index.ts +1 -1
  58. package/src/job/epoch-proving-job-data.ts +76 -0
  59. package/src/job/epoch-proving-job.ts +215 -50
  60. package/src/metrics.ts +135 -37
  61. package/src/monitors/epoch-monitor.ts +16 -5
  62. package/src/prover-node-publisher.ts +93 -86
  63. package/src/prover-node.ts +203 -126
  64. package/src/prover-publisher-factory.ts +37 -0
  65. package/src/test/index.ts +7 -4
  66. package/dest/http.d.ts +0 -8
  67. package/dest/http.d.ts.map +0 -1
  68. package/dest/http.js +0 -9
  69. package/dest/prover-coordination/config.d.ts +0 -7
  70. package/dest/prover-coordination/config.d.ts.map +0 -1
  71. package/dest/prover-coordination/config.js +0 -11
  72. package/dest/prover-coordination/factory.d.ts +0 -22
  73. package/dest/prover-coordination/factory.d.ts.map +0 -1
  74. package/dest/prover-coordination/factory.js +0 -42
  75. package/dest/prover-coordination/index.d.ts +0 -3
  76. package/dest/prover-coordination/index.d.ts.map +0 -1
  77. package/dest/prover-coordination/index.js +0 -2
  78. package/src/http.ts +0 -13
  79. package/src/prover-coordination/config.ts +0 -17
  80. package/src/prover-coordination/factory.ts +0 -72
  81. package/src/prover-coordination/index.ts +0 -2
@@ -4,57 +4,58 @@ function _ts_decorate(decorators, target, key, desc) {
4
4
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  }
7
+ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
7
8
  import { asyncPool } from '@aztec/foundation/async-pool';
9
+ import { padArrayEnd } from '@aztec/foundation/collection';
10
+ import { Fr } from '@aztec/foundation/fields';
8
11
  import { createLogger } from '@aztec/foundation/log';
9
- import { promiseWithResolvers } from '@aztec/foundation/promise';
12
+ import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
10
13
  import { Timer } from '@aztec/foundation/timer';
14
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
15
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
16
+ import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
11
17
  import { EpochProvingJobTerminalState } from '@aztec/stdlib/interfaces/server';
18
+ import { CheckpointConstantData } from '@aztec/stdlib/rollup';
19
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
12
20
  import { Attributes, trackSpan } from '@aztec/telemetry-client';
13
21
  import * as crypto from 'node:crypto';
22
+ import { validateEpochProvingJobData } from './epoch-proving-job-data.js';
14
23
  /**
15
- * Job that grabs a range of blocks from the unfinalised chain from L1, gets their txs given their hashes,
24
+ * Job that grabs a range of blocks from the unfinalized chain from L1, gets their txs given their hashes,
16
25
  * re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
17
26
  * world state as part of public call execution via the public processor.
18
27
  */ export class EpochProvingJob {
28
+ data;
19
29
  dbProvider;
20
- epochNumber;
21
- blocks;
22
- txs;
23
30
  prover;
24
31
  publicProcessorFactory;
25
32
  publisher;
26
33
  l2BlockSource;
27
- l1ToL2MessageSource;
28
34
  metrics;
29
35
  deadline;
30
36
  config;
31
- cleanUp;
32
37
  state;
33
38
  log;
34
39
  uuid;
35
40
  runPromise;
41
+ epochCheckPromise;
36
42
  deadlineTimeoutHandler;
37
43
  tracer;
38
- constructor(dbProvider, epochNumber, blocks, txs, prover, publicProcessorFactory, publisher, l2BlockSource, l1ToL2MessageSource, metrics, deadline, config = {
39
- parallelBlockLimit: 32
40
- }, cleanUp = ()=>Promise.resolve()){
44
+ constructor(data, dbProvider, prover, publicProcessorFactory, publisher, l2BlockSource, metrics, deadline, config){
45
+ this.data = data;
41
46
  this.dbProvider = dbProvider;
42
- this.epochNumber = epochNumber;
43
- this.blocks = blocks;
44
- this.txs = txs;
45
47
  this.prover = prover;
46
48
  this.publicProcessorFactory = publicProcessorFactory;
47
49
  this.publisher = publisher;
48
50
  this.l2BlockSource = l2BlockSource;
49
- this.l1ToL2MessageSource = l1ToL2MessageSource;
50
51
  this.metrics = metrics;
51
52
  this.deadline = deadline;
52
53
  this.config = config;
53
- this.cleanUp = cleanUp;
54
54
  this.state = 'initialized';
55
55
  this.log = createLogger('prover-node:epoch-proving-job');
56
+ validateEpochProvingJobData(data);
56
57
  this.uuid = crypto.randomUUID();
57
- this.tracer = metrics.client.getTracer('EpochProvingJob');
58
+ this.tracer = metrics.tracer;
58
59
  }
59
60
  getId() {
60
61
  return this.uuid;
@@ -63,12 +64,34 @@ import * as crypto from 'node:crypto';
63
64
  return this.state;
64
65
  }
65
66
  getEpochNumber() {
66
- return this.epochNumber;
67
+ return this.data.epochNumber;
68
+ }
69
+ getDeadline() {
70
+ return this.deadline;
71
+ }
72
+ getProvingData() {
73
+ return this.data;
74
+ }
75
+ get epochNumber() {
76
+ return this.data.epochNumber;
77
+ }
78
+ get blocks() {
79
+ return this.data.blocks;
80
+ }
81
+ get txs() {
82
+ return this.data.txs;
83
+ }
84
+ get attestations() {
85
+ return this.data.attestations;
67
86
  }
68
87
  /**
69
88
  * Proves the given epoch and submits the proof to L1.
70
89
  */ async run() {
71
90
  this.scheduleDeadlineStop();
91
+ if (!this.config.skipEpochCheck) {
92
+ await this.scheduleEpochCheck();
93
+ }
94
+ const attestations = this.attestations.map((attestation)=>attestation.toViem());
72
95
  const epochNumber = Number(this.epochNumber);
73
96
  const epochSizeBlocks = this.blocks.length;
74
97
  const epochSizeTxs = this.blocks.reduce((total, current)=>total + current.body.txEffects.length, 0);
@@ -88,14 +111,19 @@ import * as crypto from 'node:crypto';
88
111
  const { promise, resolve } = promiseWithResolvers();
89
112
  this.runPromise = promise;
90
113
  try {
91
- this.prover.startNewEpoch(epochNumber, fromBlock, epochSizeBlocks);
92
- await this.prover.startTubeCircuits(this.txs);
93
- await asyncPool(this.config.parallelBlockLimit, this.blocks, async (block)=>{
114
+ const blobFieldsPerCheckpoint = this.blocks.map((block)=>block.getCheckpointBlobFields());
115
+ const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
116
+ // TODO(#17027): Enable multiple blocks per checkpoint.
117
+ // Total number of checkpoints equals number of blocks because we currently build a checkpoint with only one block.
118
+ const totalNumCheckpoints = epochSizeBlocks;
119
+ this.prover.startNewEpoch(epochNumber, totalNumCheckpoints, finalBlobBatchingChallenges);
120
+ await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
121
+ await asyncPool(this.config.parallelBlockLimit ?? 32, this.blocks, async (block)=>{
94
122
  this.checkState();
95
123
  const globalVariables = block.header.globalVariables;
96
- const txs = await this.getTxs(block);
97
- const l1ToL2Messages = await this.getL1ToL2Messages(block);
98
- const previousHeader = await this.getBlockHeader(block.number - 1);
124
+ const txs = this.getTxs(block);
125
+ const l1ToL2Messages = this.getL1ToL2Messages(block);
126
+ const previousHeader = this.getBlockHeader(block.number - 1);
99
127
  this.log.verbose(`Starting processing block ${block.number}`, {
100
128
  number: block.number,
101
129
  blockHash: (await block.hash()).toString(),
@@ -107,11 +135,31 @@ import * as crypto from 'node:crypto';
107
135
  uuid: this.uuid,
108
136
  ...globalVariables
109
137
  });
138
+ const checkpointConstants = CheckpointConstantData.from({
139
+ chainId: globalVariables.chainId,
140
+ version: globalVariables.version,
141
+ vkTreeRoot: getVKTreeRoot(),
142
+ protocolContractsHash: protocolContractsHash,
143
+ proverId: this.prover.getProverId().toField(),
144
+ slotNumber: globalVariables.slotNumber,
145
+ coinbase: globalVariables.coinbase,
146
+ feeRecipient: globalVariables.feeRecipient,
147
+ gasFees: globalVariables.gasFees
148
+ });
149
+ // TODO(#17027): Enable multiple blocks per checkpoint.
150
+ // Each checkpoint has only one block.
151
+ const totalNumBlocks = 1;
152
+ const checkpointIndex = block.number - fromBlock;
153
+ await this.prover.startNewCheckpoint(checkpointIndex, checkpointConstants, l1ToL2Messages, totalNumBlocks, blobFieldsPerCheckpoint[checkpointIndex].length, previousHeader);
110
154
  // Start block proving
111
- await this.prover.startNewBlock(globalVariables, l1ToL2Messages, previousHeader);
155
+ await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
112
156
  // Process public fns
113
- const db = await this.dbProvider.fork(block.number - 1);
114
- const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, true);
157
+ const db = await this.createFork(block.number - 1, l1ToL2Messages);
158
+ const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, {
159
+ skipFeeEnforcement: true,
160
+ clientInitiatedSimulation: false,
161
+ proverId: this.prover.getProverId().toField()
162
+ });
115
163
  const processed = await this.processTxs(publicProcessor, txs);
116
164
  await this.prover.addTxs(processed);
117
165
  await db.close();
@@ -121,23 +169,32 @@ import * as crypto from 'node:crypto';
121
169
  uuid: this.uuid
122
170
  });
123
171
  // Mark block as completed to pad it
124
- await this.prover.setBlockCompleted(block.number, block.header);
172
+ const expectedBlockHeader = block.getBlockHeader();
173
+ await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
125
174
  });
126
175
  const executionTime = timer.ms();
127
176
  this.progressState('awaiting-prover');
128
- const { publicInputs, proof } = await this.prover.finaliseEpoch();
129
- this.log.info(`Finalised proof for epoch ${epochNumber}`, {
177
+ const { publicInputs, proof, batchedBlobInputs } = await this.prover.finalizeEpoch();
178
+ this.log.info(`Finalized proof for epoch ${epochNumber}`, {
130
179
  epochNumber,
131
180
  uuid: this.uuid,
132
181
  duration: timer.ms()
133
182
  });
134
183
  this.progressState('publishing-proof');
184
+ if (this.config.skipSubmitProof) {
185
+ this.log.info(`Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`);
186
+ this.state = 'completed';
187
+ this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
188
+ return;
189
+ }
135
190
  const success = await this.publisher.submitEpochProof({
136
191
  fromBlock,
137
192
  toBlock,
138
193
  epochNumber,
139
194
  publicInputs,
140
- proof
195
+ proof,
196
+ batchedBlobInputs,
197
+ attestations
141
198
  });
142
199
  if (!success) {
143
200
  throw new Error('Failed to submit epoch proof to L1');
@@ -152,7 +209,8 @@ import * as crypto from 'node:crypto';
152
209
  if (err && err.name === 'HaltExecutionError') {
153
210
  this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
154
211
  uuid: this.uuid,
155
- epochNumber
212
+ epochNumber,
213
+ details: err.message
156
214
  });
157
215
  return;
158
216
  }
@@ -160,27 +218,41 @@ import * as crypto from 'node:crypto';
160
218
  uuid: this.uuid,
161
219
  epochNumber
162
220
  });
163
- this.state = 'failed';
221
+ if (this.state === 'processing' || this.state === 'awaiting-prover' || this.state === 'publishing-proof') {
222
+ this.state = 'failed';
223
+ }
164
224
  } finally{
165
225
  clearTimeout(this.deadlineTimeoutHandler);
166
- await this.cleanUp(this);
226
+ await this.epochCheckPromise?.stop();
167
227
  await this.prover.stop();
168
228
  resolve();
169
229
  }
170
230
  }
231
+ /**
232
+ * Create a new db fork for tx processing, inserting all L1 to L2.
233
+ * 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.
234
+ */ async createFork(blockNumber, l1ToL2Messages) {
235
+ const db = await this.dbProvider.fork(blockNumber);
236
+ const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages');
237
+ this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
238
+ blockNumber,
239
+ l1ToL2Messages: l1ToL2MessagesPadded.map((m)=>m.toString())
240
+ });
241
+ await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
242
+ return db;
243
+ }
171
244
  progressState(state) {
172
245
  this.checkState();
173
246
  this.state = state;
174
247
  }
175
248
  checkState() {
176
- if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed') {
249
+ if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed' || this.state === 'reorg') {
177
250
  throw new HaltExecutionError(this.state);
178
251
  }
179
252
  }
180
253
  async stop(state = 'stopped') {
181
254
  this.state = state;
182
255
  this.prover.cancel();
183
- // TODO(palla/prover): Stop the publisher as well
184
256
  if (this.runPromise) {
185
257
  await this.runPromise;
186
258
  }
@@ -209,22 +281,47 @@ import * as crypto from 'node:crypto';
209
281
  }, timeout);
210
282
  }
211
283
  }
212
- /* Returns the header for the given block number, or the genesis block for block zero. */ async getBlockHeader(blockNumber) {
213
- if (blockNumber === 0) {
214
- return (await this.dbProvider.fork()).getInitialHeader();
284
+ /**
285
+ * Kicks off a running promise that queries the archiver for the set of L2 blocks of the current epoch.
286
+ * If those change, stops the proving job with a `rerun` state, so the node re-enqueues it.
287
+ */ async scheduleEpochCheck() {
288
+ const l2BlockSource = this.l2BlockSource;
289
+ if (!l2BlockSource) {
290
+ this.log.warn(`No L2 block source available, skipping epoch check`);
291
+ return;
215
292
  }
216
- return this.l2BlockSource.getBlockHeader(blockNumber);
293
+ const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
294
+ this.epochCheckPromise = new RunningPromise(async ()=>{
295
+ const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
296
+ const blockHashes = await Promise.all(blocks.map((block)=>block.hash()));
297
+ const thisBlockHashes = await Promise.all(this.blocks.map((block)=>block.hash()));
298
+ if (blocks.length !== this.blocks.length || !blockHashes.every((block, i)=>block.equals(thisBlockHashes[i]))) {
299
+ this.log.warn('Epoch blocks changed underfoot', {
300
+ uuid: this.uuid,
301
+ epochNumber: this.epochNumber,
302
+ oldBlockHashes: thisBlockHashes,
303
+ newBlockHashes: blockHashes
304
+ });
305
+ void this.stop('reorg');
306
+ }
307
+ }, this.log, intervalMs).start();
308
+ this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
217
309
  }
218
- async getTxs(block) {
219
- const txHashes = block.body.txEffects.map((tx)=>tx.txHash.toBigInt());
220
- const txsAndHashes = await Promise.all(this.txs.map(async (tx)=>({
221
- tx,
222
- hash: await tx.getTxHash()
223
- })));
224
- return txsAndHashes.filter((txAndHash)=>txHashes.includes(txAndHash.hash.toBigInt())).map((txAndHash)=>txAndHash.tx);
310
+ /* Returns the header for the given block number based on the epoch proving job data. */ getBlockHeader(blockNumber) {
311
+ const block = this.blocks.find((b)=>b.number === blockNumber);
312
+ if (block) {
313
+ return block.getBlockHeader();
314
+ }
315
+ if (blockNumber === Number(this.data.previousBlockHeader.getBlockNumber())) {
316
+ return this.data.previousBlockHeader;
317
+ }
318
+ throw new Error(`Block header not found for block number ${blockNumber} (got ${this.blocks.map((b)=>b.number).join(', ')} and previous header ${this.data.previousBlockHeader.getBlockNumber()})`);
319
+ }
320
+ getTxs(block) {
321
+ return block.body.txEffects.map((txEffect)=>this.txs.get(txEffect.txHash.toString()));
225
322
  }
226
323
  getL1ToL2Messages(block) {
227
- return this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(block.number));
324
+ return this.data.l1ToL2Messages[block.number];
228
325
  }
229
326
  async processTxs(publicProcessor, txs) {
230
327
  const { deadline } = this;
@@ -232,7 +329,8 @@ import * as crypto from 'node:crypto';
232
329
  deadline
233
330
  });
234
331
  if (failedTxs.length) {
235
- throw new Error(`Txs failed processing: ${failedTxs.map(({ tx, error })=>`${tx.getTxHash()} (${error})`).join(', ')}`);
332
+ const failedTxHashes = await Promise.all(failedTxs.map(({ tx })=>tx.getTxHash()));
333
+ throw new Error(`Txs failed processing: ${failedTxs.map(({ error }, index)=>`${failedTxHashes[index]} (${error})`).join(', ')}`);
236
334
  }
237
335
  if (processedTxs.length !== txs.length) {
238
336
  throw new Error(`Failed to process all txs: processed ${processedTxs.length} out of ${txs.length}`);
@@ -243,13 +341,14 @@ import * as crypto from 'node:crypto';
243
341
  _ts_decorate([
244
342
  trackSpan('EpochProvingJob.run', function() {
245
343
  return {
246
- [Attributes.EPOCH_NUMBER]: Number(this.epochNumber)
344
+ [Attributes.EPOCH_NUMBER]: Number(this.data.epochNumber)
247
345
  };
248
346
  })
249
347
  ], EpochProvingJob.prototype, "run", null);
250
348
  class HaltExecutionError extends Error {
349
+ state;
251
350
  constructor(state){
252
- super(`Halted execution due to state ${state}`);
351
+ super(`Halted execution due to state ${state}`), this.state = state;
253
352
  this.name = 'HaltExecutionError';
254
353
  }
255
354
  }
package/dest/metrics.d.ts CHANGED
@@ -1,12 +1,35 @@
1
+ import type { RollupContract } from '@aztec/ethereum';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
1
3
  import type { L1PublishProofStats } from '@aztec/stdlib/stats';
2
- import { type Gauge, type Histogram, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
3
- export declare class ProverNodeMetrics {
4
- readonly client: TelemetryClient;
4
+ import { type Gauge, type Histogram, type Meter, type TelemetryClient, type Tracer, type UpDownCounter } from '@aztec/telemetry-client';
5
+ export declare class ProverNodeJobMetrics {
6
+ private meter;
7
+ readonly tracer: Tracer;
5
8
  private logger;
6
9
  proverEpochExecutionDuration: Histogram;
7
10
  provingJobDuration: Histogram;
8
11
  provingJobBlocks: Gauge;
9
12
  provingJobTransactions: Gauge;
13
+ constructor(meter: Meter, tracer: Tracer, logger?: import("@aztec/foundation/log").Logger);
14
+ recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number): void;
15
+ }
16
+ export declare class ProverNodeRewardsMetrics {
17
+ private meter;
18
+ private coinbase;
19
+ private rollup;
20
+ private logger;
21
+ private rewards;
22
+ private accumulatedRewards;
23
+ private prevEpoch;
24
+ private proofSubmissionEpochs;
25
+ constructor(meter: Meter, coinbase: EthAddress, rollup: RollupContract, logger?: import("@aztec/foundation/log").Logger);
26
+ start(): Promise<void>;
27
+ stop(): void;
28
+ private observe;
29
+ }
30
+ export declare class ProverNodePublisherMetrics {
31
+ readonly client: TelemetryClient;
32
+ private logger;
10
33
  gasPrice: Histogram;
11
34
  txCount: UpDownCounter;
12
35
  txDuration: Histogram;
@@ -15,11 +38,12 @@ export declare class ProverNodeMetrics {
15
38
  txCalldataGas: Histogram;
16
39
  txBlobDataGasUsed: Histogram;
17
40
  txBlobDataGasCost: Histogram;
41
+ txTotalFee: Histogram;
18
42
  private senderBalance;
43
+ private meter;
19
44
  constructor(client: TelemetryClient, name?: string, logger?: import("@aztec/foundation/log").Logger);
20
45
  recordFailedTx(): void;
21
46
  recordSubmitProof(durationMs: number, stats: L1PublishProofStats): void;
22
- recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number): void;
23
47
  recordSenderBalance(wei: bigint, senderAddress: string): void;
24
48
  private recordTx;
25
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAEL,KAAK,KAAK,EACV,KAAK,SAAS,EAEd,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AAIjC,qBAAa,iBAAiB;aAkBV,MAAM,EAAE,eAAe;IAEvC,OAAO,CAAC,MAAM;IAnBhB,4BAA4B,EAAE,SAAS,CAAC;IACxC,kBAAkB,EAAE,SAAS,CAAC;IAC9B,gBAAgB,EAAE,KAAK,CAAC;IACxB,sBAAsB,EAAE,KAAK,CAAC;IAE9B,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC;IAC1B,aAAa,EAAE,SAAS,CAAC;IACzB,iBAAiB,EAAE,SAAS,CAAC;IAC7B,iBAAiB,EAAE,SAAS,CAAC;IAE7B,OAAO,CAAC,aAAa,CAAQ;gBAGX,MAAM,EAAE,eAAe,EACvC,IAAI,SAAe,EACX,MAAM,yCAAgD;IA2EhE,cAAc;IAOd,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;IAIzD,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAOhG,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAO7D,OAAO,CAAC,QAAQ;CA6BjB"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,KAAK,EAGV,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AAIjC,qBAAa,oBAAoB;IAO7B,OAAO,CAAC,KAAK;aACG,MAAM,EAAE,MAAM;IAC9B,OAAO,CAAC,MAAM;IARhB,4BAA4B,EAAE,SAAS,CAAC;IACxC,kBAAkB,EAAE,SAAS,CAAC;IAC9B,gBAAgB,EAAE,KAAK,CAAC;IACxB,sBAAsB,EAAE,KAAK,CAAC;gBAGpB,KAAK,EAAE,KAAK,EACJ,MAAM,EAAE,MAAM,EACtB,MAAM,yCAAgD;IAsBzD,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMxG;AAED,qBAAa,wBAAwB;IAOjC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IAThB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,qBAAqB,CAAK;gBAGxB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,UAAU,EACpB,MAAM,EAAE,cAAc,EACtB,MAAM,yCAAgD;IAanD,KAAK;IAMX,IAAI;IAIX,OAAO,CAAC,OAAO,CAwBb;CACH;AAED,qBAAa,0BAA0B;aAenB,MAAM,EAAE,eAAe;IAEvC,OAAO,CAAC,MAAM;IAhBhB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC;IAC1B,aAAa,EAAE,SAAS,CAAC;IACzB,iBAAiB,EAAE,SAAS,CAAC;IAC7B,iBAAiB,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE,SAAS,CAAC;IAEtB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,KAAK,CAAQ;gBAGH,MAAM,EAAE,eAAe,EACvC,IAAI,SAAe,EACX,MAAM,yCAAgD;IAoEhE,cAAc;IAOd,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;IAIzD,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAO7D,OAAO,CAAC,QAAQ;CAuCjB"}
package/dest/metrics.js CHANGED
@@ -1,83 +1,187 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
2
  import { Attributes, Metrics, ValueType } from '@aztec/telemetry-client';
3
- import { formatEther } from 'viem';
4
- export class ProverNodeMetrics {
5
- client;
3
+ import { formatEther, formatUnits } from 'viem';
4
+ export class ProverNodeJobMetrics {
5
+ meter;
6
+ tracer;
6
7
  logger;
7
8
  proverEpochExecutionDuration;
8
9
  provingJobDuration;
9
10
  provingJobBlocks;
10
11
  provingJobTransactions;
11
- gasPrice;
12
- txCount;
13
- txDuration;
14
- txGas;
15
- txCalldataSize;
16
- txCalldataGas;
17
- txBlobDataGasUsed;
18
- txBlobDataGasCost;
19
- senderBalance;
20
- constructor(client, name = 'ProverNode', logger = createLogger('prover-node:publisher:metrics')){
21
- this.client = client;
12
+ constructor(meter, tracer, logger = createLogger('prover-node:publisher:metrics')){
13
+ this.meter = meter;
14
+ this.tracer = tracer;
22
15
  this.logger = logger;
23
- const meter = client.getMeter(name);
24
- this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
16
+ this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
25
17
  description: 'Duration of execution of an epoch by the prover',
26
18
  unit: 'ms',
27
19
  valueType: ValueType.INT
28
20
  });
29
- this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
21
+ this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
30
22
  description: 'Duration of proving job',
31
23
  unit: 's',
32
24
  valueType: ValueType.DOUBLE
33
25
  });
34
- this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
26
+ this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
35
27
  description: 'Number of blocks in a proven epoch',
36
28
  valueType: ValueType.INT
37
29
  });
38
- this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
30
+ this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
39
31
  description: 'Number of transactions in a proven epoch',
40
32
  valueType: ValueType.INT
41
33
  });
42
- this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
34
+ }
35
+ recordProvingJob(executionTimeMs, totalTimeMs, numBlocks, numTxs) {
36
+ this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
37
+ this.provingJobDuration.record(totalTimeMs / 1000);
38
+ this.provingJobBlocks.record(Math.floor(numBlocks));
39
+ this.provingJobTransactions.record(Math.floor(numTxs));
40
+ }
41
+ }
42
+ export class ProverNodeRewardsMetrics {
43
+ meter;
44
+ coinbase;
45
+ rollup;
46
+ logger;
47
+ rewards;
48
+ accumulatedRewards;
49
+ prevEpoch;
50
+ proofSubmissionEpochs;
51
+ constructor(meter, coinbase, rollup, logger = createLogger('prover-node:publisher:metrics')){
52
+ this.meter = meter;
53
+ this.coinbase = coinbase;
54
+ this.rollup = rollup;
55
+ this.logger = logger;
56
+ this.prevEpoch = -1n;
57
+ this.proofSubmissionEpochs = 0;
58
+ this.observe = async (observer)=>{
59
+ const epoch = await this.rollup.getCurrentEpochNumber();
60
+ if (epoch > this.proofSubmissionEpochs) {
61
+ // look at the prev epoch so that we get an accurate value, after proof submission window has closed
62
+ // For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
63
+ // Similarly, if the proof submission window is 0, and we are in epoch 1, we should be looking at epoch 0.
64
+ const closedEpoch = epoch - BigInt(this.proofSubmissionEpochs) - 1n;
65
+ const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
66
+ const fmt = parseFloat(formatUnits(rewards, 18));
67
+ observer.observe(this.rewards, fmt, {
68
+ [Attributes.COINBASE]: this.coinbase.toString()
69
+ });
70
+ // only accumulate once per epoch
71
+ if (closedEpoch > this.prevEpoch) {
72
+ this.prevEpoch = closedEpoch;
73
+ this.accumulatedRewards.add(fmt, {
74
+ [Attributes.COINBASE]: this.coinbase.toString()
75
+ });
76
+ }
77
+ }
78
+ };
79
+ this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
80
+ valueType: ValueType.DOUBLE,
81
+ description: 'The rewards earned'
82
+ });
83
+ this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
84
+ valueType: ValueType.DOUBLE,
85
+ description: 'The rewards earned (total)'
86
+ });
87
+ }
88
+ async start() {
89
+ const proofSubmissionEpochs = await this.rollup.getProofSubmissionEpochs();
90
+ this.proofSubmissionEpochs = Number(proofSubmissionEpochs);
91
+ this.meter.addBatchObservableCallback(this.observe, [
92
+ this.rewards
93
+ ]);
94
+ }
95
+ stop() {
96
+ this.meter.removeBatchObservableCallback(this.observe, [
97
+ this.rewards
98
+ ]);
99
+ }
100
+ observe;
101
+ }
102
+ export class ProverNodePublisherMetrics {
103
+ client;
104
+ logger;
105
+ gasPrice;
106
+ txCount;
107
+ txDuration;
108
+ txGas;
109
+ txCalldataSize;
110
+ txCalldataGas;
111
+ txBlobDataGasUsed;
112
+ txBlobDataGasCost;
113
+ txTotalFee;
114
+ senderBalance;
115
+ meter;
116
+ constructor(client, name = 'ProverNode', logger = createLogger('prover-node:publisher:metrics')){
117
+ this.client = client;
118
+ this.logger = logger;
119
+ this.meter = client.getMeter(name);
120
+ this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
43
121
  description: 'The gas price used for transactions',
44
122
  unit: 'gwei',
45
123
  valueType: ValueType.DOUBLE
46
124
  });
47
- this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
125
+ this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
48
126
  description: 'The number of transactions processed'
49
127
  });
50
- this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
128
+ this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
51
129
  description: 'The duration of transaction processing',
52
130
  unit: 'ms',
53
131
  valueType: ValueType.INT
54
132
  });
55
- this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
133
+ this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
56
134
  description: 'The gas consumed by transactions',
57
135
  unit: 'gas',
58
136
  valueType: ValueType.INT
59
137
  });
60
- this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
138
+ this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
61
139
  description: 'The size of the calldata in transactions',
62
140
  unit: 'By',
63
141
  valueType: ValueType.INT
64
142
  });
65
- this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
143
+ this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
66
144
  description: 'The gas consumed by the calldata in transactions',
67
145
  unit: 'gas',
68
146
  valueType: ValueType.INT
69
147
  });
70
- this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
148
+ this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
71
149
  description: 'The amount of blob gas used in transactions',
72
150
  unit: 'gas',
73
151
  valueType: ValueType.INT
74
152
  });
75
- this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
153
+ this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
76
154
  description: 'The gas cost of blobs in transactions',
77
155
  unit: 'gwei',
78
156
  valueType: ValueType.INT
79
157
  });
80
- this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
158
+ this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
159
+ description: 'How much L1 tx costs',
160
+ unit: 'gwei',
161
+ valueType: ValueType.DOUBLE,
162
+ advice: {
163
+ explicitBucketBoundaries: [
164
+ 0.001,
165
+ 0.002,
166
+ 0.004,
167
+ 0.008,
168
+ 0.01,
169
+ 0.02,
170
+ 0.04,
171
+ 0.08,
172
+ 0.1,
173
+ 0.2,
174
+ 0.4,
175
+ 0.8,
176
+ 1,
177
+ 1.2,
178
+ 1.4,
179
+ 1.8,
180
+ 2
181
+ ]
182
+ }
183
+ });
184
+ this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
81
185
  unit: 'eth',
82
186
  description: 'The balance of the sender address',
83
187
  valueType: ValueType.DOUBLE
@@ -92,12 +196,6 @@ export class ProverNodeMetrics {
92
196
  recordSubmitProof(durationMs, stats) {
93
197
  this.recordTx(durationMs, stats);
94
198
  }
95
- recordProvingJob(executionTimeMs, totalTimeMs, numBlocks, numTxs) {
96
- this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
97
- this.provingJobDuration.record(totalTimeMs / 1000);
98
- this.provingJobBlocks.record(Math.floor(numBlocks));
99
- this.provingJobTransactions.record(Math.floor(numTxs));
100
- }
101
199
  recordSenderBalance(wei, senderAddress) {
102
200
  const eth = parseFloat(formatEther(wei, 'wei'));
103
201
  this.senderBalance.record(eth, {
@@ -122,7 +220,15 @@ export class ProverNodeMetrics {
122
220
  this.txBlobDataGasUsed.record(Number(stats.blobGasUsed), attributes);
123
221
  try {
124
222
  this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
125
- } catch (e) {
223
+ } catch {
224
+ // ignore
225
+ }
226
+ const executionFee = stats.gasUsed * stats.gasPrice;
227
+ const blobFee = stats.blobGasUsed * stats.blobDataGas;
228
+ const totalFee = executionFee + blobFee;
229
+ try {
230
+ this.txTotalFee.record(parseFloat(formatEther(totalFee)));
231
+ } catch {
126
232
  // ignore
127
233
  }
128
234
  }