Skip to content

Commit 2021779

Browse files
committed
feat: track rewards and slots (#13546)
This PR adds metrics to track rewards for sequencers/provers and slots for sequencers.
1 parent b78ab68 commit 2021779

File tree

19 files changed

+345
-79
lines changed

19 files changed

+345
-79
lines changed

yarn-project/aztec/src/cli/cmds/start_prover_node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,6 @@ export async function startProverNode(
119119

120120
signalHandlers.push(proverNode.stop.bind(proverNode));
121121

122-
proverNode.start();
122+
await proverNode.start();
123123
return { config: proverConfig };
124124
}

yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ export class FullProverTest {
309309
},
310310
{ prefilledPublicData },
311311
);
312-
this.proverNode.start();
312+
await this.proverNode.start();
313313

314314
this.logger.warn(`Proofs are now enabled`);
315315
return this;

yarn-project/end-to-end/src/fixtures/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ export async function createAndSyncProverNode(
822822
{ prefilledPublicData },
823823
);
824824
getLogger().info(`Created and synced prover node`, { publisherAddress: l1TxUtils.walletClient.account.address });
825-
proverNode.start();
825+
await proverNode.start();
826826
return proverNode;
827827
}
828828

yarn-project/prover-node/src/job/epoch-proving-job.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { getTelemetryClient } from '@aztec/telemetry-client';
1313

1414
import { type MockProxy, mock } from 'jest-mock-extended';
1515

16-
import { ProverNodeMetrics } from '../metrics.js';
16+
import { ProverNodeJobMetrics } from '../metrics.js';
1717
import type { ProverNodePublisher } from '../prover-node-publisher.js';
1818
import type { EpochProvingJobData } from './epoch-proving-job-data.js';
1919
import { EpochProvingJob } from './epoch-proving-job.js';
@@ -25,7 +25,7 @@ describe('epoch-proving-job', () => {
2525
let l2BlockSource: MockProxy<L2BlockSource>;
2626
let worldState: MockProxy<WorldStateSynchronizer>;
2727
let publicProcessorFactory: MockProxy<PublicProcessorFactory>;
28-
let metrics: ProverNodeMetrics;
28+
let metrics: ProverNodeJobMetrics;
2929

3030
// Created by a dependency
3131
let db: MockProxy<MerkleTreeWriteOperations>;
@@ -74,7 +74,10 @@ describe('epoch-proving-job', () => {
7474
publicProcessorFactory = mock<PublicProcessorFactory>();
7575
db = mock<MerkleTreeWriteOperations>();
7676
publicProcessor = mock<PublicProcessor>();
77-
metrics = new ProverNodeMetrics(getTelemetryClient());
77+
metrics = new ProverNodeJobMetrics(
78+
getTelemetryClient().getMeter('EpochProvingJob'),
79+
getTelemetryClient().getTracer('EpochProvingJob'),
80+
);
7881

7982
publicInputs = RootRollupPublicInputs.random();
8083
proof = Proof.empty();

yarn-project/prover-node/src/job/epoch-proving-job.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telem
1515

1616
import * as crypto from 'node:crypto';
1717

18-
import type { ProverNodeMetrics } from '../metrics.js';
18+
import type { ProverNodeJobMetrics } from '../metrics.js';
1919
import type { ProverNodePublisher } from '../prover-node-publisher.js';
2020
import { type EpochProvingJobData, validateEpochProvingJobData } from './epoch-proving-job-data.js';
2121

@@ -42,13 +42,13 @@ export class EpochProvingJob implements Traceable {
4242
private publicProcessorFactory: PublicProcessorFactory,
4343
private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
4444
private l2BlockSource: L2BlockSource | undefined,
45-
private metrics: ProverNodeMetrics,
45+
private metrics: ProverNodeJobMetrics,
4646
private deadline: Date | undefined,
4747
private config: { parallelBlockLimit: number } = { parallelBlockLimit: 32 },
4848
) {
4949
validateEpochProvingJobData(data);
5050
this.uuid = crypto.randomUUID();
51-
this.tracer = metrics.client.getTracer('EpochProvingJob');
51+
this.tracer = metrics.tracer;
5252
}
5353

5454
public getId(): string {

yarn-project/prover-node/src/metrics.ts

Lines changed: 111 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,187 @@
1+
import type { RollupContract } from '@aztec/ethereum';
2+
import type { EthAddress } from '@aztec/foundation/eth-address';
13
import { createLogger } from '@aztec/foundation/log';
24
import type { L1PublishProofStats, L1PublishStats } from '@aztec/stdlib/stats';
35
import {
46
Attributes,
7+
type BatchObservableResult,
58
type Gauge,
69
type Histogram,
10+
type Meter,
711
Metrics,
12+
type ObservableGauge,
813
type TelemetryClient,
14+
type Tracer,
915
type UpDownCounter,
1016
ValueType,
1117
} from '@aztec/telemetry-client';
1218

13-
import { formatEther } from 'viem';
19+
import { formatEther, formatUnits } from 'viem';
1420

15-
export class ProverNodeMetrics {
21+
export class ProverNodeJobMetrics {
1622
proverEpochExecutionDuration: Histogram;
1723
provingJobDuration: Histogram;
1824
provingJobBlocks: Gauge;
1925
provingJobTransactions: Gauge;
2026

21-
gasPrice: Histogram;
22-
txCount: UpDownCounter;
23-
txDuration: Histogram;
24-
txGas: Histogram;
25-
txCalldataSize: Histogram;
26-
txCalldataGas: Histogram;
27-
txBlobDataGasUsed: Histogram;
28-
txBlobDataGasCost: Histogram;
29-
txTotalFee: Histogram;
30-
31-
private senderBalance: Gauge;
32-
3327
constructor(
34-
public readonly client: TelemetryClient,
35-
name = 'ProverNode',
28+
private meter: Meter,
29+
public readonly tracer: Tracer,
3630
private logger = createLogger('prover-node:publisher:metrics'),
3731
) {
38-
const meter = client.getMeter(name);
39-
this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
32+
this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
4033
description: 'Duration of execution of an epoch by the prover',
4134
unit: 'ms',
4235
valueType: ValueType.INT,
4336
});
44-
this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
37+
this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
4538
description: 'Duration of proving job',
4639
unit: 's',
4740
valueType: ValueType.DOUBLE,
4841
});
49-
this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
42+
this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
5043
description: 'Number of blocks in a proven epoch',
5144
valueType: ValueType.INT,
5245
});
53-
this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
46+
this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
5447
description: 'Number of transactions in a proven epoch',
5548
valueType: ValueType.INT,
5649
});
50+
}
51+
52+
public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
53+
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
54+
this.provingJobDuration.record(totalTimeMs / 1000);
55+
this.provingJobBlocks.record(Math.floor(numBlocks));
56+
this.provingJobTransactions.record(Math.floor(numTxs));
57+
}
58+
}
59+
60+
export class ProverNodeRewardsMetrics {
61+
private rewards: ObservableGauge;
62+
private accumulatedRewards: UpDownCounter;
63+
private prevEpoch = -1n;
64+
private proofSubmissionWindow = 0n;
65+
66+
constructor(
67+
private meter: Meter,
68+
private coinbase: EthAddress,
69+
private rollup: RollupContract,
70+
private logger = createLogger('prover-node:publisher:metrics'),
71+
) {
72+
this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
73+
valueType: ValueType.DOUBLE,
74+
description: 'The rewards earned',
75+
});
76+
77+
this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
78+
valueType: ValueType.DOUBLE,
79+
description: 'The rewards earned (total)',
80+
});
81+
}
82+
83+
public async start() {
84+
this.proofSubmissionWindow = await this.rollup.getProofSubmissionWindow();
85+
this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
86+
}
87+
88+
public stop() {
89+
this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
90+
}
91+
92+
private observe = async (observer: BatchObservableResult): Promise<void> => {
93+
const slot = await this.rollup.getSlotNumber();
94+
95+
// look at the prev epoch so that we get an accurate value, after proof submission window has closed
96+
if (slot > this.proofSubmissionWindow) {
97+
const closedEpoch = await this.rollup.getEpochNumberForSlotNumber(slot - this.proofSubmissionWindow);
98+
const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
99+
100+
const fmt = parseFloat(formatUnits(rewards, 18));
101+
102+
observer.observe(this.rewards, fmt, {
103+
[Attributes.COINBASE]: this.coinbase.toString(),
104+
});
105+
106+
// only accumulate once per epoch
107+
if (closedEpoch > this.prevEpoch) {
108+
this.prevEpoch = closedEpoch;
109+
this.accumulatedRewards.add(fmt, {
110+
[Attributes.COINBASE]: this.coinbase.toString(),
111+
});
112+
}
113+
}
114+
};
115+
}
57116

58-
this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
117+
export class ProverNodePublisherMetrics {
118+
gasPrice: Histogram;
119+
txCount: UpDownCounter;
120+
txDuration: Histogram;
121+
txGas: Histogram;
122+
txCalldataSize: Histogram;
123+
txCalldataGas: Histogram;
124+
txBlobDataGasUsed: Histogram;
125+
txBlobDataGasCost: Histogram;
126+
txTotalFee: Histogram;
127+
128+
private senderBalance: Gauge;
129+
private meter: Meter;
130+
131+
constructor(
132+
public readonly client: TelemetryClient,
133+
name = 'ProverNode',
134+
private logger = createLogger('prover-node:publisher:metrics'),
135+
) {
136+
this.meter = client.getMeter(name);
137+
138+
this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
59139
description: 'The gas price used for transactions',
60140
unit: 'gwei',
61141
valueType: ValueType.DOUBLE,
62142
});
63143

64-
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
144+
this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
65145
description: 'The number of transactions processed',
66146
});
67147

68-
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
148+
this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
69149
description: 'The duration of transaction processing',
70150
unit: 'ms',
71151
valueType: ValueType.INT,
72152
});
73153

74-
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
154+
this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
75155
description: 'The gas consumed by transactions',
76156
unit: 'gas',
77157
valueType: ValueType.INT,
78158
});
79159

80-
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
160+
this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
81161
description: 'The size of the calldata in transactions',
82162
unit: 'By',
83163
valueType: ValueType.INT,
84164
});
85165

86-
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
166+
this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
87167
description: 'The gas consumed by the calldata in transactions',
88168
unit: 'gas',
89169
valueType: ValueType.INT,
90170
});
91171

92-
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
172+
this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
93173
description: 'The amount of blob gas used in transactions',
94174
unit: 'gas',
95175
valueType: ValueType.INT,
96176
});
97177

98-
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
178+
this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
99179
description: 'The gas cost of blobs in transactions',
100180
unit: 'gwei',
101181
valueType: ValueType.INT,
102182
});
103183

104-
this.txTotalFee = meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
184+
this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
105185
description: 'How much L1 tx costs',
106186
unit: 'gwei',
107187
valueType: ValueType.DOUBLE,
@@ -112,7 +192,7 @@ export class ProverNodeMetrics {
112192
},
113193
});
114194

115-
this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
195+
this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
116196
unit: 'eth',
117197
description: 'The balance of the sender address',
118198
valueType: ValueType.DOUBLE,
@@ -130,13 +210,6 @@ export class ProverNodeMetrics {
130210
this.recordTx(durationMs, stats);
131211
}
132212

133-
public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
134-
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
135-
this.provingJobDuration.record(totalTimeMs / 1000);
136-
this.provingJobBlocks.record(Math.floor(numBlocks));
137-
this.provingJobTransactions.record(Math.floor(numTxs));
138-
}
139-
140213
public recordSenderBalance(wei: bigint, senderAddress: string) {
141214
const eth = parseFloat(formatEther(wei, 'wei'));
142215
this.senderBalance.record(eth, {

yarn-project/prover-node/src/prover-node-publisher.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
1717

1818
import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
1919

20-
import { ProverNodeMetrics } from './metrics.js';
20+
import { ProverNodePublisherMetrics } from './metrics.js';
2121

2222
/**
2323
* Stats for a sent transaction.
@@ -40,7 +40,7 @@ export class ProverNodePublisher {
4040
private interruptibleSleep = new InterruptibleSleep();
4141
private sleepTimeMs: number;
4242
private interrupted = false;
43-
private metrics: ProverNodeMetrics;
43+
private metrics: ProverNodePublisherMetrics;
4444

4545
protected log = createLogger('prover-node:l1-tx-publisher');
4646

@@ -60,12 +60,16 @@ export class ProverNodePublisher {
6060

6161
const telemetry = deps.telemetry ?? getTelemetryClient();
6262

63-
this.metrics = new ProverNodeMetrics(telemetry, 'ProverNode');
63+
this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
6464

6565
this.rollupContract = deps.rollupContract;
6666
this.l1TxUtils = deps.l1TxUtils;
6767
}
6868

69+
public getRollupContract() {
70+
return this.rollupContract;
71+
}
72+
6973
/**
7074
* Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
7175
* Be warned, the call may return false even if the tx subsequently gets successfully mined.

yarn-project/prover-node/src/prover-node.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { timesParallel } from '@aztec/foundation/collection';
22
import { EthAddress } from '@aztec/foundation/eth-address';
3+
import { Fr } from '@aztec/foundation/fields';
34
import { promiseWithResolvers } from '@aztec/foundation/promise';
45
import { retryUntil } from '@aztec/foundation/retry';
56
import { sleep } from '@aztec/foundation/sleep';
@@ -70,7 +71,9 @@ describe('prover-node', () => {
7071
);
7172

7273
beforeEach(async () => {
73-
prover = mock<EpochProverManager>();
74+
prover = mock<EpochProverManager>({
75+
getProverId: () => Fr.random(),
76+
});
7477
publisher = mock<ProverNodePublisher>();
7578
l2BlockSource = mock<L2BlockSource>();
7679
l1ToL2MessageSource = mock<L1ToL2MessageSource>();

0 commit comments

Comments
 (0)