@aztec/prover-node 0.0.1-commit.b655e406 → 0.0.1-commit.b9865e97
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 +4 -4
- package/dest/actions/download-epoch-proving-job.js +1 -1
- package/dest/actions/index.d.ts +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +5 -3
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +105 -25
- package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
- package/dest/bin/run-failed-epoch.d.ts +1 -1
- package/dest/bin/run-failed-epoch.js +6 -5
- package/dest/config.d.ts +8 -10
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +19 -21
- package/dest/factory.d.ts +20 -16
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +48 -63
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/job/epoch-proving-job-data.d.ts +8 -6
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -1
- package/dest/job/epoch-proving-job-data.js +25 -18
- package/dest/job/epoch-proving-job.d.ts +11 -13
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +669 -111
- package/dest/metrics.d.ts +24 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +77 -98
- package/dest/monitors/epoch-monitor.d.ts +3 -2
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +14 -20
- package/dest/monitors/index.d.ts +1 -1
- package/dest/prover-node-publisher.d.ts +33 -11
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +248 -46
- package/dest/prover-node.d.ts +30 -19
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +512 -81
- package/dest/prover-publisher-factory.d.ts +10 -6
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +7 -5
- package/dest/test/index.d.ts +1 -1
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +27 -25
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +20 -7
- package/src/actions/upload-epoch-proof-failure.ts +1 -1
- package/src/bin/run-failed-epoch.ts +5 -3
- package/src/config.ts +27 -33
- package/src/factory.ts +81 -103
- package/src/index.ts +1 -0
- package/src/job/epoch-proving-job-data.ts +31 -25
- package/src/job/epoch-proving-job.ts +228 -117
- package/src/metrics.ts +102 -82
- package/src/monitors/epoch-monitor.ts +10 -17
- package/src/prover-node-publisher.ts +302 -66
- package/src/prover-node.ts +135 -86
- package/src/prover-publisher-factory.ts +19 -11
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
1
|
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
3
|
-
import {
|
|
4
|
-
import { Fr } from '@aztec/foundation/
|
|
5
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
6
5
|
import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
7
6
|
import { Timer } from '@aztec/foundation/timer';
|
|
7
|
+
import { AVM_MAX_CONCURRENT_SIMULATIONS } from '@aztec/native';
|
|
8
8
|
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
9
9
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
10
10
|
import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
|
|
11
11
|
import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
|
|
12
|
+
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
12
13
|
import type { L2Block, L2BlockSource } from '@aztec/stdlib/block';
|
|
14
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
13
15
|
import {
|
|
14
16
|
type EpochProver,
|
|
15
17
|
type EpochProvingJobState,
|
|
16
18
|
EpochProvingJobTerminalState,
|
|
17
19
|
type ForkMerkleTreeOperations,
|
|
18
20
|
} from '@aztec/stdlib/interfaces/server';
|
|
21
|
+
import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging';
|
|
19
22
|
import { CheckpointConstantData } from '@aztec/stdlib/rollup';
|
|
20
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
21
23
|
import type { ProcessedTx, Tx } from '@aztec/stdlib/tx';
|
|
22
24
|
import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
23
25
|
|
|
@@ -40,10 +42,11 @@ export type EpochProvingJobOptions = {
|
|
|
40
42
|
*/
|
|
41
43
|
export class EpochProvingJob implements Traceable {
|
|
42
44
|
private state: EpochProvingJobState = 'initialized';
|
|
43
|
-
private log
|
|
45
|
+
private log: Logger;
|
|
44
46
|
private uuid: string;
|
|
45
47
|
|
|
46
48
|
private runPromise: Promise<void> | undefined;
|
|
49
|
+
private abortController = new AbortController();
|
|
47
50
|
private epochCheckPromise: RunningPromise | undefined;
|
|
48
51
|
private deadlineTimeoutHandler: NodeJS.Timeout | undefined;
|
|
49
52
|
|
|
@@ -54,14 +57,19 @@ export class EpochProvingJob implements Traceable {
|
|
|
54
57
|
private dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>,
|
|
55
58
|
private prover: EpochProver,
|
|
56
59
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
57
|
-
private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
|
|
60
|
+
private publisher: Pick<ProverNodePublisher, 'submitEpochProof' | 'analyzeEpochProofSubmission'>,
|
|
58
61
|
private l2BlockSource: L2BlockSource | undefined,
|
|
59
62
|
private metrics: ProverNodeJobMetrics,
|
|
60
63
|
private deadline: Date | undefined,
|
|
61
64
|
private config: EpochProvingJobOptions,
|
|
65
|
+
bindings?: LoggerBindings,
|
|
62
66
|
) {
|
|
63
67
|
validateEpochProvingJobData(data);
|
|
64
68
|
this.uuid = crypto.randomUUID();
|
|
69
|
+
this.log = createLogger('prover-node:epoch-proving-job', {
|
|
70
|
+
...bindings,
|
|
71
|
+
instanceId: `epoch-${data.epochNumber}`,
|
|
72
|
+
});
|
|
65
73
|
this.tracer = metrics.tracer;
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -73,7 +81,7 @@ export class EpochProvingJob implements Traceable {
|
|
|
73
81
|
return this.state;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
public getEpochNumber():
|
|
84
|
+
public getEpochNumber(): EpochNumber {
|
|
77
85
|
return this.data.epochNumber;
|
|
78
86
|
}
|
|
79
87
|
|
|
@@ -89,8 +97,8 @@ export class EpochProvingJob implements Traceable {
|
|
|
89
97
|
return this.data.epochNumber;
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
private get
|
|
93
|
-
return this.data.
|
|
100
|
+
private get checkpoints() {
|
|
101
|
+
return this.data.checkpoints;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
private get txs() {
|
|
@@ -105,7 +113,7 @@ export class EpochProvingJob implements Traceable {
|
|
|
105
113
|
* Proves the given epoch and submits the proof to L1.
|
|
106
114
|
*/
|
|
107
115
|
@trackSpan('EpochProvingJob.run', function () {
|
|
108
|
-
return { [Attributes.EPOCH_NUMBER]:
|
|
116
|
+
return { [Attributes.EPOCH_NUMBER]: this.data.epochNumber };
|
|
109
117
|
})
|
|
110
118
|
public async run() {
|
|
111
119
|
this.scheduleDeadlineStop();
|
|
@@ -114,14 +122,22 @@ export class EpochProvingJob implements Traceable {
|
|
|
114
122
|
}
|
|
115
123
|
|
|
116
124
|
const attestations = this.attestations.map(attestation => attestation.toViem());
|
|
117
|
-
const epochNumber =
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
125
|
+
const epochNumber = this.epochNumber;
|
|
126
|
+
const epochSizeCheckpoints = this.checkpoints.length;
|
|
127
|
+
const epochSizeBlocks = this.checkpoints.reduce((accum, checkpoint) => accum + checkpoint.blocks.length, 0);
|
|
128
|
+
const epochSizeTxs = this.checkpoints.reduce(
|
|
129
|
+
(accum, checkpoint) =>
|
|
130
|
+
accum + checkpoint.blocks.reduce((accumC, block) => accumC + block.body.txEffects.length, 0),
|
|
131
|
+
0,
|
|
132
|
+
);
|
|
133
|
+
const fromCheckpoint = this.checkpoints[0].number;
|
|
134
|
+
const toCheckpoint = this.checkpoints.at(-1)!.number;
|
|
135
|
+
const fromBlock = this.checkpoints[0].blocks[0].number;
|
|
136
|
+
const toBlock = this.checkpoints.at(-1)!.blocks.at(-1)!.number;
|
|
137
|
+
this.log.info(`Starting epoch ${epochNumber} proving job with checkpoints ${fromCheckpoint} to ${toCheckpoint}`, {
|
|
122
138
|
fromBlock,
|
|
123
139
|
toBlock,
|
|
124
|
-
|
|
140
|
+
epochSizeTxs,
|
|
125
141
|
epochNumber,
|
|
126
142
|
uuid: this.uuid,
|
|
127
143
|
});
|
|
@@ -132,84 +148,121 @@ export class EpochProvingJob implements Traceable {
|
|
|
132
148
|
this.runPromise = promise;
|
|
133
149
|
|
|
134
150
|
try {
|
|
135
|
-
const
|
|
151
|
+
const blobTimer = new Timer();
|
|
152
|
+
const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
|
|
136
153
|
const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
|
|
154
|
+
this.metrics.recordBlobProcessing(blobTimer.ms());
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const totalNumCheckpoints = epochSizeBlocks;
|
|
141
|
-
|
|
142
|
-
this.prover.startNewEpoch(epochNumber, totalNumCheckpoints, finalBlobBatchingChallenges);
|
|
156
|
+
this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
|
|
157
|
+
const chonkTimer = new Timer();
|
|
143
158
|
await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
|
|
159
|
+
this.metrics.recordChonkVerifier(chonkTimer.ms());
|
|
144
160
|
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
// Everything in the epoch should have the same chainId and version.
|
|
162
|
+
const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
|
|
147
163
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const previousHeader = this.getBlockHeader(block.number - 1)!;
|
|
152
|
-
|
|
153
|
-
this.log.verbose(`Starting processing block ${block.number}`, {
|
|
154
|
-
number: block.number,
|
|
155
|
-
blockHash: (await block.hash()).toString(),
|
|
156
|
-
lastArchive: block.header.lastArchive.root,
|
|
157
|
-
noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
|
|
158
|
-
nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
|
|
159
|
-
publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
|
|
160
|
-
previousHeader: previousHeader.hash(),
|
|
161
|
-
uuid: this.uuid,
|
|
162
|
-
...globalVariables,
|
|
163
|
-
});
|
|
164
|
+
const previousBlockHeaders = this.gatherPreviousBlockHeaders();
|
|
165
|
+
|
|
166
|
+
const allCheckpointsTimer = new Timer();
|
|
164
167
|
|
|
168
|
+
const parallelism = this.config.parallelBlockLimit
|
|
169
|
+
? this.config.parallelBlockLimit
|
|
170
|
+
: AVM_MAX_CONCURRENT_SIMULATIONS > 0
|
|
171
|
+
? AVM_MAX_CONCURRENT_SIMULATIONS
|
|
172
|
+
: this.checkpoints.length;
|
|
173
|
+
|
|
174
|
+
await this.processCheckpoints(parallelism, async checkpoint => {
|
|
175
|
+
this.checkState();
|
|
176
|
+
const checkpointTimer = new Timer();
|
|
177
|
+
|
|
178
|
+
const checkpointIndex = checkpoint.number - fromCheckpoint;
|
|
165
179
|
const checkpointConstants = CheckpointConstantData.from({
|
|
166
|
-
chainId
|
|
167
|
-
version
|
|
180
|
+
chainId,
|
|
181
|
+
version,
|
|
168
182
|
vkTreeRoot: getVKTreeRoot(),
|
|
169
183
|
protocolContractsHash: protocolContractsHash,
|
|
170
184
|
proverId: this.prover.getProverId().toField(),
|
|
171
|
-
slotNumber:
|
|
172
|
-
coinbase:
|
|
173
|
-
feeRecipient:
|
|
174
|
-
gasFees:
|
|
185
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
186
|
+
coinbase: checkpoint.header.coinbase,
|
|
187
|
+
feeRecipient: checkpoint.header.feeRecipient,
|
|
188
|
+
gasFees: checkpoint.header.gasFees,
|
|
189
|
+
});
|
|
190
|
+
const previousHeader = previousBlockHeaders[checkpointIndex];
|
|
191
|
+
const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
|
|
192
|
+
|
|
193
|
+
this.log.debug(`Starting processing checkpoint ${checkpoint.number}`, {
|
|
194
|
+
number: checkpoint.number,
|
|
195
|
+
checkpointHash: checkpoint.hash().toString(),
|
|
196
|
+
headerHash: checkpoint.header.hash().toString(),
|
|
197
|
+
numL1ToL2Messages: l1ToL2Messages.length,
|
|
198
|
+
previousBlockNumber: previousHeader.globalVariables.blockNumber,
|
|
199
|
+
uuid: this.uuid,
|
|
175
200
|
});
|
|
176
201
|
|
|
177
|
-
// TODO(#17027): Enable multiple blocks per checkpoint.
|
|
178
|
-
// Each checkpoint has only one block.
|
|
179
|
-
const totalNumBlocks = 1;
|
|
180
|
-
const checkpointIndex = block.number - fromBlock;
|
|
181
202
|
await this.prover.startNewCheckpoint(
|
|
182
203
|
checkpointIndex,
|
|
183
204
|
checkpointConstants,
|
|
184
205
|
l1ToL2Messages,
|
|
185
|
-
|
|
186
|
-
blobFieldsPerCheckpoint[checkpointIndex].length,
|
|
206
|
+
checkpoint.blocks.length,
|
|
187
207
|
previousHeader,
|
|
188
208
|
);
|
|
189
209
|
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
for (let blockIndex = 0; blockIndex < checkpoint.blocks.length; blockIndex++) {
|
|
211
|
+
const blockTimer = new Timer();
|
|
212
|
+
const block = checkpoint.blocks[blockIndex];
|
|
213
|
+
const globalVariables = block.header.globalVariables;
|
|
214
|
+
const txs = this.getTxs(block);
|
|
215
|
+
|
|
216
|
+
this.log.verbose(`Starting processing block ${block.number}`, {
|
|
217
|
+
number: block.number,
|
|
218
|
+
blockHash: (await block.hash()).toString(),
|
|
219
|
+
lastArchive: block.header.lastArchive.root,
|
|
220
|
+
noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
|
|
221
|
+
nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
|
|
222
|
+
publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
|
|
223
|
+
...globalVariables,
|
|
224
|
+
numTxs: txs.length,
|
|
225
|
+
});
|
|
192
226
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
227
|
+
// Start block proving
|
|
228
|
+
await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
|
|
229
|
+
|
|
230
|
+
// Process public fns. L1 to L2 messages are only inserted for the first block of a checkpoint,
|
|
231
|
+
// as the fork for subsequent blocks already includes them from the previous block's synced state.
|
|
232
|
+
{
|
|
233
|
+
await using db = await this.createFork(
|
|
234
|
+
BlockNumber(block.number - 1),
|
|
235
|
+
blockIndex === 0 ? l1ToL2Messages : undefined,
|
|
236
|
+
);
|
|
237
|
+
this.checkState();
|
|
238
|
+
const config = PublicSimulatorConfig.from({
|
|
239
|
+
proverId: this.prover.getProverId().toField(),
|
|
240
|
+
skipFeeEnforcement: false,
|
|
241
|
+
collectDebugLogs: false,
|
|
242
|
+
collectHints: true,
|
|
243
|
+
collectPublicInputs: true,
|
|
244
|
+
collectStatistics: false,
|
|
245
|
+
});
|
|
246
|
+
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
|
|
247
|
+
const processed = await this.processTxs(publicProcessor, txs);
|
|
248
|
+
this.checkState();
|
|
249
|
+
await this.prover.addTxs(processed);
|
|
250
|
+
}
|
|
251
|
+
this.checkState();
|
|
252
|
+
this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
|
|
253
|
+
blockNumber: block.number,
|
|
254
|
+
blockHash: (await block.hash()).toString(),
|
|
255
|
+
uuid: this.uuid,
|
|
256
|
+
});
|
|
208
257
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
258
|
+
// Mark block as completed to pad it
|
|
259
|
+
const expectedBlockHeader = block.header;
|
|
260
|
+
await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
|
|
261
|
+
this.metrics.recordBlockProcessing(blockTimer.ms());
|
|
262
|
+
}
|
|
263
|
+
this.metrics.recordCheckpointProcessing(checkpointTimer.ms());
|
|
212
264
|
});
|
|
265
|
+
this.metrics.recordAllCheckpointsProcessing(allCheckpointsTimer.ms());
|
|
213
266
|
|
|
214
267
|
const executionTime = timer.ms();
|
|
215
268
|
|
|
@@ -221,16 +274,29 @@ export class EpochProvingJob implements Traceable {
|
|
|
221
274
|
|
|
222
275
|
if (this.config.skipSubmitProof) {
|
|
223
276
|
this.log.info(
|
|
224
|
-
`Proof publishing is disabled.
|
|
277
|
+
`Proof publishing is disabled. Analyzing estimated L1 fees for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
|
|
225
278
|
);
|
|
279
|
+
try {
|
|
280
|
+
await this.publisher.analyzeEpochProofSubmission({
|
|
281
|
+
fromCheckpoint,
|
|
282
|
+
toCheckpoint,
|
|
283
|
+
epochNumber,
|
|
284
|
+
publicInputs,
|
|
285
|
+
proof,
|
|
286
|
+
batchedBlobInputs,
|
|
287
|
+
attestations,
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
this.log.warn(`Failed to analyze estimated L1 fees for epoch ${epochNumber}`, err);
|
|
291
|
+
}
|
|
226
292
|
this.state = 'completed';
|
|
227
|
-
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
|
|
293
|
+
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
|
|
228
294
|
return;
|
|
229
295
|
}
|
|
230
296
|
|
|
231
297
|
const success = await this.publisher.submitEpochProof({
|
|
232
|
-
|
|
233
|
-
|
|
298
|
+
fromCheckpoint,
|
|
299
|
+
toCheckpoint,
|
|
234
300
|
epochNumber,
|
|
235
301
|
publicInputs,
|
|
236
302
|
proof,
|
|
@@ -241,12 +307,12 @@ export class EpochProvingJob implements Traceable {
|
|
|
241
307
|
throw new Error('Failed to submit epoch proof to L1');
|
|
242
308
|
}
|
|
243
309
|
|
|
244
|
-
this.log.info(`Submitted proof for epoch ${epochNumber} (
|
|
310
|
+
this.log.info(`Submitted proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`, {
|
|
245
311
|
epochNumber,
|
|
246
312
|
uuid: this.uuid,
|
|
247
313
|
});
|
|
248
314
|
this.state = 'completed';
|
|
249
|
-
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
|
|
315
|
+
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
|
|
250
316
|
} catch (err: any) {
|
|
251
317
|
if (err && err.name === 'HaltExecutionError') {
|
|
252
318
|
this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
|
|
@@ -269,25 +335,63 @@ export class EpochProvingJob implements Traceable {
|
|
|
269
335
|
}
|
|
270
336
|
|
|
271
337
|
/**
|
|
272
|
-
* Create a new db fork for tx processing, inserting
|
|
338
|
+
* Create a new db fork for tx processing, optionally inserting L1 to L2 messages.
|
|
339
|
+
* L1 to L2 messages should only be inserted for the first block in a checkpoint,
|
|
340
|
+
* as subsequent blocks' synced state already includes them.
|
|
273
341
|
* REFACTOR: The prover already spawns a db fork of its own for each block, so we may be able to do away with just one fork.
|
|
274
342
|
*/
|
|
275
|
-
private async createFork(blockNumber:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
343
|
+
private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[] | undefined) {
|
|
344
|
+
this.log.verbose(`Creating fork at ${blockNumber}`, { blockNumber });
|
|
345
|
+
// temporary stack to control fork lifetime
|
|
346
|
+
await using cleanup = new AsyncDisposableStack();
|
|
347
|
+
const db = cleanup.use(await this.dbProvider.fork(blockNumber));
|
|
348
|
+
|
|
349
|
+
if (l1ToL2Messages !== undefined) {
|
|
350
|
+
this.log.verbose(`Inserting ${l1ToL2Messages.length} L1 to L2 messages in fork`, {
|
|
351
|
+
blockNumber,
|
|
352
|
+
l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
|
|
353
|
+
});
|
|
354
|
+
await appendL1ToL2MessagesToTree(db, l1ToL2Messages);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// everything run succesfully so we can release this stack and give control of the fork's lifetime to the caller
|
|
358
|
+
cleanup.move();
|
|
288
359
|
return db;
|
|
289
360
|
}
|
|
290
361
|
|
|
362
|
+
private async processCheckpoints(
|
|
363
|
+
parallelism: number,
|
|
364
|
+
processCheckpoint: (checkpoint: Checkpoint) => Promise<void>,
|
|
365
|
+
): Promise<void> {
|
|
366
|
+
let hasError = false;
|
|
367
|
+
let firstError: unknown;
|
|
368
|
+
|
|
369
|
+
await asyncPool(Math.max(parallelism, 1), this.checkpoints, async checkpoint => {
|
|
370
|
+
if (hasError || this.abortController.signal.aborted) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
this.checkState();
|
|
376
|
+
await processCheckpoint(checkpoint);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (!hasError) {
|
|
379
|
+
hasError = true;
|
|
380
|
+
firstError = err;
|
|
381
|
+
this.failProcessing();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (hasError) {
|
|
387
|
+
throw firstError;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (this.abortController.signal.aborted) {
|
|
391
|
+
this.checkState();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
291
395
|
private progressState(state: EpochProvingJobState) {
|
|
292
396
|
this.checkState();
|
|
293
397
|
this.state = state;
|
|
@@ -301,12 +405,24 @@ export class EpochProvingJob implements Traceable {
|
|
|
301
405
|
|
|
302
406
|
public async stop(state: EpochProvingJobTerminalState = 'stopped') {
|
|
303
407
|
this.state = state;
|
|
304
|
-
this.
|
|
408
|
+
this.interruptProcessing();
|
|
305
409
|
if (this.runPromise) {
|
|
306
410
|
await this.runPromise;
|
|
307
411
|
}
|
|
308
412
|
}
|
|
309
413
|
|
|
414
|
+
private failProcessing() {
|
|
415
|
+
if (!EpochProvingJobTerminalState.includes(this.state)) {
|
|
416
|
+
this.state = 'failed';
|
|
417
|
+
}
|
|
418
|
+
this.interruptProcessing();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private interruptProcessing() {
|
|
422
|
+
this.abortController.abort();
|
|
423
|
+
this.prover.cancel();
|
|
424
|
+
}
|
|
425
|
+
|
|
310
426
|
private scheduleDeadlineStop() {
|
|
311
427
|
const deadline = this.deadline;
|
|
312
428
|
if (deadline) {
|
|
@@ -341,11 +457,14 @@ export class EpochProvingJob implements Traceable {
|
|
|
341
457
|
const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
|
|
342
458
|
this.epochCheckPromise = new RunningPromise(
|
|
343
459
|
async () => {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
460
|
+
const blockHeaders = (
|
|
461
|
+
await l2BlockSource.getBlocksData({ epoch: this.epochNumber, onlyCheckpointed: true })
|
|
462
|
+
).map(d => d.header);
|
|
463
|
+
const blockHashes = await Promise.all(blockHeaders.map(header => header.hash()));
|
|
464
|
+
const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
|
|
465
|
+
const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
|
|
347
466
|
if (
|
|
348
|
-
|
|
467
|
+
blockHeaders.length !== thisBlocks.length ||
|
|
349
468
|
!blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))
|
|
350
469
|
) {
|
|
351
470
|
this.log.warn('Epoch blocks changed underfoot', {
|
|
@@ -363,35 +482,27 @@ export class EpochProvingJob implements Traceable {
|
|
|
363
482
|
this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
|
|
364
483
|
}
|
|
365
484
|
|
|
366
|
-
/* Returns the header
|
|
367
|
-
private
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
return block.getBlockHeader();
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (blockNumber === Number(this.data.previousBlockHeader.getBlockNumber())) {
|
|
374
|
-
return this.data.previousBlockHeader;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
throw new Error(
|
|
378
|
-
`Block header not found for block number ${blockNumber} (got ${this.blocks
|
|
379
|
-
.map(b => b.number)
|
|
380
|
-
.join(', ')} and previous header ${this.data.previousBlockHeader.getBlockNumber()})`,
|
|
381
|
-
);
|
|
485
|
+
/* Returns the last block header in the previous checkpoint for all checkpoints in the epoch */
|
|
486
|
+
private gatherPreviousBlockHeaders() {
|
|
487
|
+
const lastBlocks = this.checkpoints.map(checkpoint => checkpoint.blocks.at(-1)!);
|
|
488
|
+
return [this.data.previousBlockHeader, ...lastBlocks.map(block => block.header).slice(0, -1)];
|
|
382
489
|
}
|
|
383
490
|
|
|
384
491
|
private getTxs(block: L2Block): Tx[] {
|
|
385
492
|
return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
|
|
386
493
|
}
|
|
387
494
|
|
|
388
|
-
private getL1ToL2Messages(
|
|
389
|
-
return this.data.l1ToL2Messages[
|
|
495
|
+
private getL1ToL2Messages(checkpoint: Checkpoint) {
|
|
496
|
+
return this.data.l1ToL2Messages[checkpoint.number];
|
|
390
497
|
}
|
|
391
498
|
|
|
392
499
|
private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
|
|
393
500
|
const { deadline } = this;
|
|
394
|
-
const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
|
|
501
|
+
const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
|
|
502
|
+
deadline,
|
|
503
|
+
signal: this.abortController.signal,
|
|
504
|
+
});
|
|
505
|
+
this.checkState();
|
|
395
506
|
|
|
396
507
|
if (failedTxs.length) {
|
|
397
508
|
const failedTxHashes = await Promise.all(failedTxs.map(({ tx }) => tx.getTxHash()));
|