@aztec/prover-node 0.0.0-test.1 → 0.0.1-fake-ceab37513c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/actions/download-epoch-proving-job.d.ts +18 -0
- package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/download-epoch-proving-job.js +37 -0
- package/dest/actions/index.d.ts +3 -0
- package/dest/actions/index.d.ts.map +1 -0
- package/dest/actions/index.js +2 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts +11 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/rerun-epoch-proving-job.js +40 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
- package/dest/actions/upload-epoch-proof-failure.js +78 -0
- package/dest/bin/run-failed-epoch.d.ts +2 -0
- package/dest/bin/run-failed-epoch.d.ts.map +1 -0
- package/dest/bin/run-failed-epoch.js +67 -0
- package/dest/config.d.ts +12 -9
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +81 -14
- package/dest/factory.d.ts +12 -8
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +94 -31
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/job/epoch-proving-job-data.d.ts +16 -0
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
- package/dest/job/epoch-proving-job-data.js +52 -0
- package/dest/job/epoch-proving-job.d.ts +30 -15
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +119 -46
- package/dest/metrics.d.ts +28 -4
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +141 -35
- package/dest/monitors/epoch-monitor.d.ts +3 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +15 -2
- package/dest/prover-node-publisher.d.ts +7 -10
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +58 -59
- package/dest/prover-node.d.ts +43 -39
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +171 -100
- package/dest/prover-publisher-factory.d.ts +21 -0
- package/dest/prover-publisher-factory.d.ts.map +1 -0
- package/dest/prover-publisher-factory.js +26 -0
- package/dest/test/index.d.ts +4 -2
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -3
- package/package.json +36 -31
- package/src/actions/download-epoch-proving-job.ts +44 -0
- package/src/actions/index.ts +2 -0
- package/src/actions/rerun-epoch-proving-job.ts +61 -0
- package/src/actions/upload-epoch-proof-failure.ts +88 -0
- package/src/bin/run-failed-epoch.ts +77 -0
- package/src/config.ts +108 -24
- package/src/factory.ts +158 -43
- package/src/index.ts +1 -1
- package/src/job/epoch-proving-job-data.ts +76 -0
- package/src/job/epoch-proving-job.ts +177 -46
- package/src/metrics.ts +135 -37
- package/src/monitors/epoch-monitor.ts +16 -5
- package/src/prover-node-publisher.ts +90 -85
- package/src/prover-node.ts +203 -126
- package/src/prover-publisher-factory.ts +37 -0
- package/src/test/index.ts +7 -4
- package/dest/http.d.ts +0 -8
- package/dest/http.d.ts.map +0 -1
- package/dest/http.js +0 -9
- package/dest/prover-coordination/config.d.ts +0 -7
- package/dest/prover-coordination/config.d.ts.map +0 -1
- package/dest/prover-coordination/config.js +0 -11
- package/dest/prover-coordination/factory.d.ts +0 -22
- package/dest/prover-coordination/factory.d.ts.map +0 -1
- package/dest/prover-coordination/factory.js +0 -42
- package/dest/prover-coordination/index.d.ts +0 -3
- package/dest/prover-coordination/index.d.ts.map +0 -1
- package/dest/prover-coordination/index.js +0 -2
- package/src/http.ts +0 -13
- package/src/prover-coordination/config.ts +0 -17
- package/src/prover-coordination/factory.ts +0 -72
- package/src/prover-coordination/index.ts +0 -2
package/src/metrics.ts
CHANGED
|
@@ -1,106 +1,201 @@
|
|
|
1
|
+
import type { RollupContract } from '@aztec/ethereum';
|
|
2
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
1
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
4
|
import type { L1PublishProofStats, L1PublishStats } from '@aztec/stdlib/stats';
|
|
3
5
|
import {
|
|
4
6
|
Attributes,
|
|
7
|
+
type BatchObservableResult,
|
|
5
8
|
type Gauge,
|
|
6
9
|
type Histogram,
|
|
10
|
+
type Meter,
|
|
7
11
|
Metrics,
|
|
12
|
+
type ObservableGauge,
|
|
8
13
|
type TelemetryClient,
|
|
14
|
+
type Tracer,
|
|
9
15
|
type UpDownCounter,
|
|
10
16
|
ValueType,
|
|
11
17
|
} from '@aztec/telemetry-client';
|
|
12
18
|
|
|
13
|
-
import { formatEther } from 'viem';
|
|
19
|
+
import { formatEther, formatUnits } from 'viem';
|
|
14
20
|
|
|
15
|
-
export class
|
|
21
|
+
export class ProverNodeJobMetrics {
|
|
16
22
|
proverEpochExecutionDuration: Histogram;
|
|
17
23
|
provingJobDuration: Histogram;
|
|
18
24
|
provingJobBlocks: Gauge;
|
|
19
25
|
provingJobTransactions: Gauge;
|
|
20
26
|
|
|
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
|
-
|
|
30
|
-
private senderBalance: Gauge;
|
|
31
|
-
|
|
32
27
|
constructor(
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
private meter: Meter,
|
|
29
|
+
public readonly tracer: Tracer,
|
|
35
30
|
private logger = createLogger('prover-node:publisher:metrics'),
|
|
36
31
|
) {
|
|
37
|
-
|
|
38
|
-
this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
32
|
+
this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
39
33
|
description: 'Duration of execution of an epoch by the prover',
|
|
40
34
|
unit: 'ms',
|
|
41
35
|
valueType: ValueType.INT,
|
|
42
36
|
});
|
|
43
|
-
this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
37
|
+
this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
44
38
|
description: 'Duration of proving job',
|
|
45
39
|
unit: 's',
|
|
46
40
|
valueType: ValueType.DOUBLE,
|
|
47
41
|
});
|
|
48
|
-
this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
|
|
42
|
+
this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
|
|
49
43
|
description: 'Number of blocks in a proven epoch',
|
|
50
44
|
valueType: ValueType.INT,
|
|
51
45
|
});
|
|
52
|
-
this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
|
|
46
|
+
this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
|
|
53
47
|
description: 'Number of transactions in a proven epoch',
|
|
54
48
|
valueType: ValueType.INT,
|
|
55
49
|
});
|
|
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 proofSubmissionEpochs = 0;
|
|
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
|
+
const proofSubmissionEpochs = await this.rollup.getProofSubmissionEpochs();
|
|
85
|
+
this.proofSubmissionEpochs = Number(proofSubmissionEpochs);
|
|
86
|
+
this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public stop() {
|
|
90
|
+
this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private observe = async (observer: BatchObservableResult): Promise<void> => {
|
|
94
|
+
const epoch = await this.rollup.getCurrentEpochNumber();
|
|
95
|
+
|
|
96
|
+
if (epoch > this.proofSubmissionEpochs) {
|
|
97
|
+
// look at the prev epoch so that we get an accurate value, after proof submission window has closed
|
|
98
|
+
// For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
|
|
99
|
+
// Similarly, if the proof submission window is 0, and we are in epoch 1, we should be looking at epoch 0.
|
|
100
|
+
const closedEpoch = epoch - BigInt(this.proofSubmissionEpochs) - 1n;
|
|
101
|
+
const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
|
|
56
102
|
|
|
57
|
-
|
|
103
|
+
const fmt = parseFloat(formatUnits(rewards, 18));
|
|
104
|
+
|
|
105
|
+
observer.observe(this.rewards, fmt, {
|
|
106
|
+
[Attributes.COINBASE]: this.coinbase.toString(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// only accumulate once per epoch
|
|
110
|
+
if (closedEpoch > this.prevEpoch) {
|
|
111
|
+
this.prevEpoch = closedEpoch;
|
|
112
|
+
this.accumulatedRewards.add(fmt, {
|
|
113
|
+
[Attributes.COINBASE]: this.coinbase.toString(),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class ProverNodePublisherMetrics {
|
|
121
|
+
gasPrice: Histogram;
|
|
122
|
+
txCount: UpDownCounter;
|
|
123
|
+
txDuration: Histogram;
|
|
124
|
+
txGas: Histogram;
|
|
125
|
+
txCalldataSize: Histogram;
|
|
126
|
+
txCalldataGas: Histogram;
|
|
127
|
+
txBlobDataGasUsed: Histogram;
|
|
128
|
+
txBlobDataGasCost: Histogram;
|
|
129
|
+
txTotalFee: Histogram;
|
|
130
|
+
|
|
131
|
+
private senderBalance: Gauge;
|
|
132
|
+
private meter: Meter;
|
|
133
|
+
|
|
134
|
+
constructor(
|
|
135
|
+
public readonly client: TelemetryClient,
|
|
136
|
+
name = 'ProverNode',
|
|
137
|
+
private logger = createLogger('prover-node:publisher:metrics'),
|
|
138
|
+
) {
|
|
139
|
+
this.meter = client.getMeter(name);
|
|
140
|
+
|
|
141
|
+
this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
|
|
58
142
|
description: 'The gas price used for transactions',
|
|
59
143
|
unit: 'gwei',
|
|
60
144
|
valueType: ValueType.DOUBLE,
|
|
61
145
|
});
|
|
62
146
|
|
|
63
|
-
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
147
|
+
this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
64
148
|
description: 'The number of transactions processed',
|
|
65
149
|
});
|
|
66
150
|
|
|
67
|
-
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
|
|
151
|
+
this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
|
|
68
152
|
description: 'The duration of transaction processing',
|
|
69
153
|
unit: 'ms',
|
|
70
154
|
valueType: ValueType.INT,
|
|
71
155
|
});
|
|
72
156
|
|
|
73
|
-
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
|
|
157
|
+
this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
|
|
74
158
|
description: 'The gas consumed by transactions',
|
|
75
159
|
unit: 'gas',
|
|
76
160
|
valueType: ValueType.INT,
|
|
77
161
|
});
|
|
78
162
|
|
|
79
|
-
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
|
|
163
|
+
this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
|
|
80
164
|
description: 'The size of the calldata in transactions',
|
|
81
165
|
unit: 'By',
|
|
82
166
|
valueType: ValueType.INT,
|
|
83
167
|
});
|
|
84
168
|
|
|
85
|
-
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
|
|
169
|
+
this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
|
|
86
170
|
description: 'The gas consumed by the calldata in transactions',
|
|
87
171
|
unit: 'gas',
|
|
88
172
|
valueType: ValueType.INT,
|
|
89
173
|
});
|
|
90
174
|
|
|
91
|
-
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
|
|
175
|
+
this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
|
|
92
176
|
description: 'The amount of blob gas used in transactions',
|
|
93
177
|
unit: 'gas',
|
|
94
178
|
valueType: ValueType.INT,
|
|
95
179
|
});
|
|
96
180
|
|
|
97
|
-
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
|
|
181
|
+
this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
|
|
98
182
|
description: 'The gas cost of blobs in transactions',
|
|
99
183
|
unit: 'gwei',
|
|
100
184
|
valueType: ValueType.INT,
|
|
101
185
|
});
|
|
102
186
|
|
|
103
|
-
this.
|
|
187
|
+
this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
|
|
188
|
+
description: 'How much L1 tx costs',
|
|
189
|
+
unit: 'gwei',
|
|
190
|
+
valueType: ValueType.DOUBLE,
|
|
191
|
+
advice: {
|
|
192
|
+
explicitBucketBoundaries: [
|
|
193
|
+
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,
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
|
|
104
199
|
unit: 'eth',
|
|
105
200
|
description: 'The balance of the sender address',
|
|
106
201
|
valueType: ValueType.DOUBLE,
|
|
@@ -118,13 +213,6 @@ export class ProverNodeMetrics {
|
|
|
118
213
|
this.recordTx(durationMs, stats);
|
|
119
214
|
}
|
|
120
215
|
|
|
121
|
-
public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
|
|
122
|
-
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
|
|
123
|
-
this.provingJobDuration.record(totalTimeMs / 1000);
|
|
124
|
-
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
125
|
-
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
216
|
public recordSenderBalance(wei: bigint, senderAddress: string) {
|
|
129
217
|
const eth = parseFloat(formatEther(wei, 'wei'));
|
|
130
218
|
this.senderBalance.record(eth, {
|
|
@@ -157,7 +245,17 @@ export class ProverNodeMetrics {
|
|
|
157
245
|
|
|
158
246
|
try {
|
|
159
247
|
this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
|
|
160
|
-
} catch
|
|
248
|
+
} catch {
|
|
249
|
+
// ignore
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const executionFee = stats.gasUsed * stats.gasPrice;
|
|
253
|
+
const blobFee = stats.blobGasUsed * stats.blobDataGas;
|
|
254
|
+
const totalFee = executionFee + blobFee;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
this.txTotalFee.record(parseFloat(formatEther(totalFee)));
|
|
258
|
+
} catch {
|
|
161
259
|
// ignore
|
|
162
260
|
}
|
|
163
261
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
3
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
3
4
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
4
5
|
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
5
6
|
import {
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
} from '@aztec/telemetry-client';
|
|
12
13
|
|
|
13
14
|
export interface EpochMonitorHandler {
|
|
14
|
-
handleEpochReadyToProve(epochNumber: bigint): Promise<
|
|
15
|
+
handleEpochReadyToProve(epochNumber: bigint): Promise<boolean>;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -36,16 +37,19 @@ export class EpochMonitor implements Traceable {
|
|
|
36
37
|
constructor(
|
|
37
38
|
private readonly l2BlockSource: L2BlockSource,
|
|
38
39
|
private readonly l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
39
|
-
private options: { pollingIntervalMs: number },
|
|
40
|
+
private options: { pollingIntervalMs: number; provingDelayMs?: number },
|
|
40
41
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
41
42
|
) {
|
|
42
43
|
this.tracer = telemetry.getTracer('EpochMonitor');
|
|
43
44
|
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.options.pollingIntervalMs);
|
|
45
|
+
if (this.options.provingDelayMs) {
|
|
46
|
+
this.log.warn(`Prover node epoch monitor running with delay of ${this.options.provingDelayMs}ms`);
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
public static async create(
|
|
47
51
|
l2BlockSource: L2BlockSource,
|
|
48
|
-
options: { pollingIntervalMs: number },
|
|
52
|
+
options: { pollingIntervalMs: number; provingDelayMs?: number },
|
|
49
53
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
50
54
|
): Promise<EpochMonitor> {
|
|
51
55
|
const l1Constants = await l2BlockSource.getL1Constants();
|
|
@@ -71,6 +75,7 @@ export class EpochMonitor implements Traceable {
|
|
|
71
75
|
@trackSpan('EpochMonitor.work')
|
|
72
76
|
public async work() {
|
|
73
77
|
const { epochToProve, blockNumber, slotNumber } = await this.getEpochNumberToProve();
|
|
78
|
+
this.log.debug(`Epoch to prove: ${epochToProve}`, { blockNumber, slotNumber });
|
|
74
79
|
if (epochToProve === undefined) {
|
|
75
80
|
this.log.trace(`Next block to prove ${blockNumber} not yet mined`, { blockNumber });
|
|
76
81
|
return;
|
|
@@ -86,9 +91,15 @@ export class EpochMonitor implements Traceable {
|
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
93
|
|
|
94
|
+
if (this.options.provingDelayMs) {
|
|
95
|
+
this.log.debug(`Waiting ${this.options.provingDelayMs}ms before proving epoch ${epochToProve}`);
|
|
96
|
+
await sleep(this.options.provingDelayMs);
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
this.log.debug(`Epoch ${epochToProve} is ready to be proven`);
|
|
90
|
-
await this.handler?.handleEpochReadyToProve(epochToProve)
|
|
91
|
-
|
|
100
|
+
if (await this.handler?.handleEpochReadyToProve(epochToProve)) {
|
|
101
|
+
this.latestEpochNumber = epochToProve;
|
|
102
|
+
}
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
private async getEpochNumberToProve() {
|
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { type BatchedBlob, FinalBlobAccumulatorPublicInputs } from '@aztec/blob-lib';
|
|
2
|
+
import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
|
|
3
|
+
import type { L1TxUtils, RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum';
|
|
3
4
|
import { makeTuple } from '@aztec/foundation/array';
|
|
4
|
-
import { areArraysEqual
|
|
5
|
+
import { areArraysEqual } from '@aztec/foundation/collection';
|
|
5
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
7
|
import { Fr } from '@aztec/foundation/fields';
|
|
7
8
|
import { createLogger } from '@aztec/foundation/log';
|
|
8
|
-
import {
|
|
9
|
-
import { InterruptibleSleep } from '@aztec/foundation/sleep';
|
|
9
|
+
import type { Tuple } from '@aztec/foundation/serialize';
|
|
10
10
|
import { Timer } from '@aztec/foundation/timer';
|
|
11
11
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
12
12
|
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
|
|
13
|
+
import { CommitteeAttestation, CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
13
14
|
import type { Proof } from '@aztec/stdlib/proofs';
|
|
14
15
|
import type { FeeRecipient, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
|
|
15
16
|
import type { L1PublishProofStats } from '@aztec/stdlib/stats';
|
|
16
17
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
17
18
|
|
|
19
|
+
import { inspect } from 'util';
|
|
18
20
|
import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
|
|
19
21
|
|
|
20
|
-
import {
|
|
22
|
+
import { ProverNodePublisherMetrics } from './metrics.js';
|
|
21
23
|
|
|
22
|
-
/**
|
|
23
|
-
* Stats for a sent transaction.
|
|
24
|
-
*/
|
|
25
24
|
/** Arguments to the submitEpochProof method of the rollup contract */
|
|
26
25
|
export type L1SubmitEpochProofArgs = {
|
|
27
26
|
epochSize: number;
|
|
28
27
|
previousArchive: Fr;
|
|
29
28
|
endArchive: Fr;
|
|
30
|
-
previousBlockHash: Fr;
|
|
31
|
-
endBlockHash: Fr;
|
|
32
29
|
endTimestamp: Fr;
|
|
33
30
|
outHash: Fr;
|
|
34
31
|
proverId: Fr;
|
|
@@ -37,10 +34,8 @@ export type L1SubmitEpochProofArgs = {
|
|
|
37
34
|
};
|
|
38
35
|
|
|
39
36
|
export class ProverNodePublisher {
|
|
40
|
-
private interruptibleSleep = new InterruptibleSleep();
|
|
41
|
-
private sleepTimeMs: number;
|
|
42
37
|
private interrupted = false;
|
|
43
|
-
private metrics:
|
|
38
|
+
private metrics: ProverNodePublisherMetrics;
|
|
44
39
|
|
|
45
40
|
protected log = createLogger('prover-node:l1-tx-publisher');
|
|
46
41
|
|
|
@@ -56,16 +51,18 @@ export class ProverNodePublisher {
|
|
|
56
51
|
telemetry?: TelemetryClient;
|
|
57
52
|
},
|
|
58
53
|
) {
|
|
59
|
-
this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
|
|
60
|
-
|
|
61
54
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
62
55
|
|
|
63
|
-
this.metrics = new
|
|
56
|
+
this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
|
|
64
57
|
|
|
65
58
|
this.rollupContract = deps.rollupContract;
|
|
66
59
|
this.l1TxUtils = deps.l1TxUtils;
|
|
67
60
|
}
|
|
68
61
|
|
|
62
|
+
public getRollupContract() {
|
|
63
|
+
return this.rollupContract;
|
|
64
|
+
}
|
|
65
|
+
|
|
69
66
|
/**
|
|
70
67
|
* Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
|
|
71
68
|
* Be warned, the call may return false even if the tx subsequently gets successfully mined.
|
|
@@ -74,16 +71,17 @@ export class ProverNodePublisher {
|
|
|
74
71
|
*/
|
|
75
72
|
public interrupt() {
|
|
76
73
|
this.interrupted = true;
|
|
77
|
-
this.
|
|
74
|
+
this.l1TxUtils.interrupt();
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
/** Restarts the publisher after calling `interrupt`. */
|
|
81
78
|
public restart() {
|
|
82
79
|
this.interrupted = false;
|
|
80
|
+
this.l1TxUtils.restart();
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
public getSenderAddress() {
|
|
86
|
-
return
|
|
84
|
+
return this.l1TxUtils.getSenderAddress();
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
public async submitEpochProof(args: {
|
|
@@ -92,12 +90,13 @@ export class ProverNodePublisher {
|
|
|
92
90
|
toBlock: number;
|
|
93
91
|
publicInputs: RootRollupPublicInputs;
|
|
94
92
|
proof: Proof;
|
|
93
|
+
batchedBlobInputs: BatchedBlob;
|
|
94
|
+
attestations: ViemCommitteeAttestation[];
|
|
95
95
|
}): Promise<boolean> {
|
|
96
96
|
const { epochNumber, fromBlock, toBlock } = args;
|
|
97
97
|
const ctx = { epochNumber, fromBlock, toBlock };
|
|
98
98
|
if (!this.interrupted) {
|
|
99
99
|
const timer = new Timer();
|
|
100
|
-
|
|
101
100
|
// Validate epoch proof range and hashes are correct before submitting
|
|
102
101
|
await this.validateEpochProofSubmission(args);
|
|
103
102
|
|
|
@@ -107,13 +106,16 @@ export class ProverNodePublisher {
|
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
try {
|
|
110
|
-
this.metrics.recordSenderBalance(
|
|
109
|
+
this.metrics.recordSenderBalance(
|
|
110
|
+
await this.l1TxUtils.getSenderBalance(),
|
|
111
|
+
this.l1TxUtils.getSenderAddress().toString(),
|
|
112
|
+
);
|
|
111
113
|
} catch (err) {
|
|
112
114
|
this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
// Tx was mined successfully
|
|
116
|
-
if (txReceipt.status) {
|
|
118
|
+
if (txReceipt.status === 'success') {
|
|
117
119
|
const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
|
|
118
120
|
const stats: L1PublishProofStats = {
|
|
119
121
|
gasPrice: txReceipt.effectiveGasPrice,
|
|
@@ -132,11 +134,10 @@ export class ProverNodePublisher {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
this.metrics.recordFailedTx();
|
|
135
|
-
this.log.error(`Rollup.submitEpochProof tx status failed
|
|
136
|
-
await this.sleepOrInterrupted();
|
|
137
|
+
this.log.error(`Rollup.submitEpochProof tx status failed ${txReceipt.transactionHash}`, undefined, ctx);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
this.log.verbose('L2 block data syncing interrupted
|
|
140
|
+
this.log.verbose('L2 block data syncing interrupted', ctx);
|
|
140
141
|
return false;
|
|
141
142
|
}
|
|
142
143
|
|
|
@@ -145,49 +146,50 @@ export class ProverNodePublisher {
|
|
|
145
146
|
toBlock: number;
|
|
146
147
|
publicInputs: RootRollupPublicInputs;
|
|
147
148
|
proof: Proof;
|
|
149
|
+
batchedBlobInputs: BatchedBlob;
|
|
150
|
+
attestations: ViemCommitteeAttestation[];
|
|
148
151
|
}) {
|
|
149
|
-
const { fromBlock, toBlock, publicInputs,
|
|
152
|
+
const { fromBlock, toBlock, publicInputs, batchedBlobInputs } = args;
|
|
150
153
|
|
|
151
154
|
// Check that the block numbers match the expected epoch to be proven
|
|
152
155
|
const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
|
|
153
|
-
if
|
|
156
|
+
// Don't publish if proven is beyond our toBlock, pointless to do so
|
|
157
|
+
if (proven > BigInt(toBlock)) {
|
|
154
158
|
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
|
|
155
159
|
}
|
|
160
|
+
// toBlock can't be greater than pending
|
|
156
161
|
if (toBlock > pending) {
|
|
157
162
|
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
// Check the
|
|
161
|
-
const blockLog = await this.rollupContract.getBlock(
|
|
162
|
-
if (publicInputs.
|
|
165
|
+
// Check the archive for the immediate block before the epoch
|
|
166
|
+
const blockLog = await this.rollupContract.getBlock(BigInt(fromBlock - 1));
|
|
167
|
+
if (publicInputs.previousArchiveRoot.toString() !== blockLog.archive) {
|
|
163
168
|
throw new Error(
|
|
164
|
-
`Previous archive root mismatch: ${publicInputs.
|
|
169
|
+
`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${blockLog.archive}`,
|
|
165
170
|
);
|
|
166
171
|
}
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
|
|
173
|
+
// Check the archive for the last block in the epoch
|
|
174
|
+
const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
|
|
175
|
+
if (publicInputs.endArchiveRoot.toString() !== endBlockLog.archive) {
|
|
169
176
|
throw new Error(
|
|
170
|
-
`
|
|
177
|
+
`End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endBlockLog.archive}`,
|
|
171
178
|
);
|
|
172
179
|
}
|
|
173
180
|
|
|
174
|
-
// Check the
|
|
175
|
-
|
|
176
|
-
if (publicInputs.endArchive.root.toString() !== endBlockLog.archive) {
|
|
181
|
+
// Check the batched blob inputs from the root rollup against the batched blob computed in ts
|
|
182
|
+
if (!publicInputs.blobPublicInputs.equals(FinalBlobAccumulatorPublicInputs.fromBatchedBlob(batchedBlobInputs))) {
|
|
177
183
|
throw new Error(
|
|
178
|
-
`
|
|
184
|
+
`Batched blob mismatch: ${inspect(publicInputs.blobPublicInputs)} !== ${inspect(FinalBlobAccumulatorPublicInputs.fromBatchedBlob(batchedBlobInputs))}`,
|
|
179
185
|
);
|
|
180
186
|
}
|
|
181
|
-
if (publicInputs.endBlockHash.toString() !== endBlockLog.blockHash) {
|
|
182
|
-
throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockLog.blockHash}`);
|
|
183
|
-
}
|
|
184
187
|
|
|
185
188
|
// Compare the public inputs computed by the contract with the ones injected
|
|
186
|
-
const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const argsPublicInputs = [...publicInputs.toFields(), ...aggregationObject];
|
|
189
|
+
const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(
|
|
190
|
+
this.getEpochProofPublicInputsArgs(args),
|
|
191
|
+
);
|
|
192
|
+
const argsPublicInputs = [...publicInputs.toFields()];
|
|
191
193
|
|
|
192
194
|
if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b) => a.equals(b))) {
|
|
193
195
|
const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
|
|
@@ -202,34 +204,23 @@ export class ProverNodePublisher {
|
|
|
202
204
|
toBlock: number;
|
|
203
205
|
publicInputs: RootRollupPublicInputs;
|
|
204
206
|
proof: Proof;
|
|
207
|
+
batchedBlobInputs: BatchedBlob;
|
|
208
|
+
attestations: ViemCommitteeAttestation[];
|
|
205
209
|
}): Promise<TransactionReceipt | undefined> {
|
|
206
|
-
const
|
|
207
|
-
const argsArray = this.getSubmitEpochProofArgs(args);
|
|
208
|
-
|
|
209
|
-
const txArgs = [
|
|
210
|
-
{
|
|
211
|
-
start: argsArray[0],
|
|
212
|
-
end: argsArray[1],
|
|
213
|
-
args: argsArray[2],
|
|
214
|
-
fees: argsArray[3],
|
|
215
|
-
blobPublicInputs: argsArray[4],
|
|
216
|
-
aggregationObject: argsArray[5],
|
|
217
|
-
proof: proofHex,
|
|
218
|
-
},
|
|
219
|
-
] as const;
|
|
210
|
+
const txArgs = [this.getSubmitEpochProofArgs(args)] as const;
|
|
220
211
|
|
|
221
|
-
this.log.info(`
|
|
212
|
+
this.log.info(`Submitting epoch proof to L1 rollup contract`, {
|
|
213
|
+
proofSize: args.proof.withoutPublicInputs().length,
|
|
214
|
+
fromBlock: args.fromBlock,
|
|
215
|
+
toBlock: args.toBlock,
|
|
216
|
+
});
|
|
222
217
|
const data = encodeFunctionData({
|
|
223
218
|
abi: RollupAbi,
|
|
224
219
|
functionName: 'submitEpochRootProof',
|
|
225
220
|
args: txArgs,
|
|
226
221
|
});
|
|
227
222
|
try {
|
|
228
|
-
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
|
|
229
|
-
to: this.rollupContract.address,
|
|
230
|
-
data,
|
|
231
|
-
});
|
|
232
|
-
|
|
223
|
+
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
|
|
233
224
|
return receipt;
|
|
234
225
|
} catch (err) {
|
|
235
226
|
this.log.error(`Rollup submit epoch proof failed`, err);
|
|
@@ -249,38 +240,52 @@ export class ProverNodePublisher {
|
|
|
249
240
|
}
|
|
250
241
|
}
|
|
251
242
|
|
|
252
|
-
private
|
|
243
|
+
private getEpochProofPublicInputsArgs(args: {
|
|
253
244
|
fromBlock: number;
|
|
254
245
|
toBlock: number;
|
|
255
246
|
publicInputs: RootRollupPublicInputs;
|
|
256
|
-
|
|
247
|
+
batchedBlobInputs: BatchedBlob;
|
|
248
|
+
attestations: ViemCommitteeAttestation[];
|
|
257
249
|
}) {
|
|
250
|
+
// Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
|
|
258
251
|
return [
|
|
259
|
-
BigInt(args.fromBlock)
|
|
260
|
-
BigInt(args.toBlock)
|
|
252
|
+
BigInt(args.fromBlock) /*_start*/,
|
|
253
|
+
BigInt(args.toBlock) /*_end*/,
|
|
261
254
|
{
|
|
262
|
-
previousArchive: args.publicInputs.
|
|
263
|
-
endArchive: args.publicInputs.
|
|
264
|
-
previousBlockHash: args.publicInputs.previousBlockHash.toString(),
|
|
265
|
-
endBlockHash: args.publicInputs.endBlockHash.toString(),
|
|
266
|
-
endTimestamp: args.publicInputs.endTimestamp.toBigInt(),
|
|
267
|
-
outHash: args.publicInputs.outHash.toString(),
|
|
255
|
+
previousArchive: args.publicInputs.previousArchiveRoot.toString(),
|
|
256
|
+
endArchive: args.publicInputs.endArchiveRoot.toString(),
|
|
268
257
|
proverId: EthAddress.fromField(args.publicInputs.proverId).toString(),
|
|
269
|
-
}
|
|
258
|
+
} /*_args*/,
|
|
270
259
|
makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
|
|
271
260
|
i % 2 === 0
|
|
272
261
|
? args.publicInputs.fees[i / 2].recipient.toField().toString()
|
|
273
262
|
: args.publicInputs.fees[(i - 1) / 2].value.toString(),
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
.filter((_, i) => i < args.toBlock - args.fromBlock + 1)
|
|
277
|
-
.map(b => b.toString())
|
|
278
|
-
.join(``)}`,
|
|
279
|
-
`0x${serializeToBuffer(args.proof.extractAggregationObject()).toString('hex')}`,
|
|
263
|
+
) /*_fees*/,
|
|
264
|
+
args.batchedBlobInputs.getEthBlobEvaluationInputs() /*_blobPublicInputs*/,
|
|
280
265
|
] as const;
|
|
281
266
|
}
|
|
282
267
|
|
|
283
|
-
|
|
284
|
-
|
|
268
|
+
private getSubmitEpochProofArgs(args: {
|
|
269
|
+
fromBlock: number;
|
|
270
|
+
toBlock: number;
|
|
271
|
+
publicInputs: RootRollupPublicInputs;
|
|
272
|
+
proof: Proof;
|
|
273
|
+
batchedBlobInputs: BatchedBlob;
|
|
274
|
+
attestations: ViemCommitteeAttestation[];
|
|
275
|
+
}) {
|
|
276
|
+
// Returns arguments for EpochProofLib.sol -> submitEpochRootProof()
|
|
277
|
+
const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
|
|
278
|
+
const argsArray = this.getEpochProofPublicInputsArgs(args);
|
|
279
|
+
return {
|
|
280
|
+
start: argsArray[0],
|
|
281
|
+
end: argsArray[1],
|
|
282
|
+
args: argsArray[2],
|
|
283
|
+
fees: argsArray[3],
|
|
284
|
+
attestations: new CommitteeAttestationsAndSigners(
|
|
285
|
+
args.attestations.map(a => CommitteeAttestation.fromViem(a)),
|
|
286
|
+
).getPackedAttestations(),
|
|
287
|
+
blobInputs: argsArray[4],
|
|
288
|
+
proof: proofHex,
|
|
289
|
+
};
|
|
285
290
|
}
|
|
286
291
|
}
|