@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.
- 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 +95 -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 +149 -50
- 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 +59 -60
- 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 +161 -43
- package/src/index.ts +1 -1
- package/src/job/epoch-proving-job-data.ts +76 -0
- package/src/job/epoch-proving-job.ts +215 -50
- package/src/metrics.ts +135 -37
- package/src/monitors/epoch-monitor.ts +16 -5
- package/src/prover-node-publisher.ts +93 -86
- 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
|
@@ -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
|
|
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(
|
|
39
|
-
|
|
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.
|
|
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.
|
|
92
|
-
await
|
|
93
|
-
|
|
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 =
|
|
97
|
-
const l1ToL2Messages =
|
|
98
|
-
const previousHeader =
|
|
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(
|
|
155
|
+
await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
|
|
112
156
|
// Process public fns
|
|
113
|
-
const db = await this.
|
|
114
|
-
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables,
|
|
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
|
-
|
|
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.
|
|
129
|
-
this.log.info(`
|
|
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
|
|
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.
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
4
|
-
|
|
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
|
}
|
package/dest/metrics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"
|
|
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
|
|
5
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
}
|