@aztec/prover-node 0.0.0-test.1 → 0.0.1-commit.1142ef1
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 +15 -11
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +83 -15
- package/dest/factory.d.ts +14 -12
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +105 -32
- package/dest/index.d.ts +3 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/job/epoch-proving-job-data.d.ts +18 -0
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
- package/dest/job/epoch-proving-job-data.js +59 -0
- package/dest/job/epoch-proving-job.d.ts +26 -18
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +579 -96
- package/dest/metrics.d.ts +30 -5
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +103 -73
- package/dest/monitors/epoch-monitor.d.ts +5 -2
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +18 -13
- package/dest/monitors/index.d.ts +1 -1
- package/dest/prover-node-publisher.d.ts +14 -15
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +86 -81
- package/dest/prover-node.d.ts +49 -44
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +581 -129
- package/dest/prover-publisher-factory.d.ts +23 -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 +5 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -3
- package/package.json +39 -33
- 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 +111 -25
- package/src/factory.ts +176 -44
- package/src/index.ts +2 -1
- package/src/job/epoch-proving-job-data.ts +82 -0
- package/src/job/epoch-proving-job.ts +255 -83
- package/src/metrics.ts +116 -73
- package/src/monitors/epoch-monitor.ts +20 -15
- package/src/prover-node-publisher.ts +133 -115
- package/src/prover-node.ts +223 -142
- package/src/prover-publisher-factory.ts +39 -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
|
@@ -1,26 +1,43 @@
|
|
|
1
|
+
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
1
2
|
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
3
|
+
import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
4
|
+
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
5
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
7
|
+
import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
4
8
|
import { Timer } from '@aztec/foundation/timer';
|
|
9
|
+
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
10
|
+
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
11
|
+
import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
|
|
5
12
|
import type { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server';
|
|
6
|
-
import
|
|
13
|
+
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
14
|
+
import type { L2BlockNew, L2BlockSource } from '@aztec/stdlib/block';
|
|
15
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
7
16
|
import {
|
|
8
17
|
type EpochProver,
|
|
9
18
|
type EpochProvingJobState,
|
|
10
19
|
EpochProvingJobTerminalState,
|
|
11
20
|
type ForkMerkleTreeOperations,
|
|
12
21
|
} from '@aztec/stdlib/interfaces/server';
|
|
13
|
-
import
|
|
22
|
+
import { CheckpointConstantData } from '@aztec/stdlib/rollup';
|
|
23
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
14
24
|
import type { ProcessedTx, Tx } from '@aztec/stdlib/tx';
|
|
15
25
|
import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
16
26
|
|
|
17
27
|
import * as crypto from 'node:crypto';
|
|
18
28
|
|
|
19
|
-
import type {
|
|
29
|
+
import type { ProverNodeJobMetrics } from '../metrics.js';
|
|
20
30
|
import type { ProverNodePublisher } from '../prover-node-publisher.js';
|
|
31
|
+
import { type EpochProvingJobData, validateEpochProvingJobData } from './epoch-proving-job-data.js';
|
|
32
|
+
|
|
33
|
+
export type EpochProvingJobOptions = {
|
|
34
|
+
parallelBlockLimit?: number;
|
|
35
|
+
skipEpochCheck?: boolean;
|
|
36
|
+
skipSubmitProof?: boolean;
|
|
37
|
+
};
|
|
21
38
|
|
|
22
39
|
/**
|
|
23
|
-
* Job that grabs a range of blocks from the
|
|
40
|
+
* Job that grabs a range of blocks from the unfinalized chain from L1, gets their txs given their hashes,
|
|
24
41
|
* re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
|
|
25
42
|
* world state as part of public call execution via the public processor.
|
|
26
43
|
*/
|
|
@@ -30,27 +47,25 @@ export class EpochProvingJob implements Traceable {
|
|
|
30
47
|
private uuid: string;
|
|
31
48
|
|
|
32
49
|
private runPromise: Promise<void> | undefined;
|
|
50
|
+
private epochCheckPromise: RunningPromise | undefined;
|
|
33
51
|
private deadlineTimeoutHandler: NodeJS.Timeout | undefined;
|
|
34
52
|
|
|
35
53
|
public readonly tracer: Tracer;
|
|
36
54
|
|
|
37
55
|
constructor(
|
|
38
|
-
private
|
|
39
|
-
private
|
|
40
|
-
private blocks: L2Block[],
|
|
41
|
-
private txs: Tx[],
|
|
56
|
+
private data: EpochProvingJobData,
|
|
57
|
+
private dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>,
|
|
42
58
|
private prover: EpochProver,
|
|
43
59
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
44
|
-
private publisher: ProverNodePublisher,
|
|
45
|
-
private l2BlockSource: L2BlockSource,
|
|
46
|
-
private
|
|
47
|
-
private metrics: ProverNodeMetrics,
|
|
60
|
+
private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
|
|
61
|
+
private l2BlockSource: L2BlockSource | undefined,
|
|
62
|
+
private metrics: ProverNodeJobMetrics,
|
|
48
63
|
private deadline: Date | undefined,
|
|
49
|
-
private config:
|
|
50
|
-
private cleanUp: (job: EpochProvingJob) => Promise<void> = () => Promise.resolve(),
|
|
64
|
+
private config: EpochProvingJobOptions,
|
|
51
65
|
) {
|
|
66
|
+
validateEpochProvingJobData(data);
|
|
52
67
|
this.uuid = crypto.randomUUID();
|
|
53
|
-
this.tracer = metrics.
|
|
68
|
+
this.tracer = metrics.tracer;
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
public getId(): string {
|
|
@@ -61,27 +76,63 @@ export class EpochProvingJob implements Traceable {
|
|
|
61
76
|
return this.state;
|
|
62
77
|
}
|
|
63
78
|
|
|
64
|
-
public getEpochNumber():
|
|
65
|
-
return this.epochNumber;
|
|
79
|
+
public getEpochNumber(): EpochNumber {
|
|
80
|
+
return this.data.epochNumber;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public getDeadline(): Date | undefined {
|
|
84
|
+
return this.deadline;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public getProvingData(): EpochProvingJobData {
|
|
88
|
+
return this.data;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private get epochNumber() {
|
|
92
|
+
return this.data.epochNumber;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private get checkpoints() {
|
|
96
|
+
return this.data.checkpoints;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private get txs() {
|
|
100
|
+
return this.data.txs;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private get attestations() {
|
|
104
|
+
return this.data.attestations;
|
|
66
105
|
}
|
|
67
106
|
|
|
68
107
|
/**
|
|
69
108
|
* Proves the given epoch and submits the proof to L1.
|
|
70
109
|
*/
|
|
71
110
|
@trackSpan('EpochProvingJob.run', function () {
|
|
72
|
-
return { [Attributes.EPOCH_NUMBER]:
|
|
111
|
+
return { [Attributes.EPOCH_NUMBER]: this.data.epochNumber };
|
|
73
112
|
})
|
|
74
113
|
public async run() {
|
|
75
114
|
this.scheduleDeadlineStop();
|
|
115
|
+
if (!this.config.skipEpochCheck) {
|
|
116
|
+
await this.scheduleEpochCheck();
|
|
117
|
+
}
|
|
76
118
|
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
this.
|
|
119
|
+
const attestations = this.attestations.map(attestation => attestation.toViem());
|
|
120
|
+
const epochNumber = this.epochNumber;
|
|
121
|
+
const epochSizeCheckpoints = this.checkpoints.length;
|
|
122
|
+
const epochSizeBlocks = this.checkpoints.reduce((accum, checkpoint) => accum + checkpoint.blocks.length, 0);
|
|
123
|
+
const epochSizeTxs = this.checkpoints.reduce(
|
|
124
|
+
(accum, checkpoint) =>
|
|
125
|
+
accum + checkpoint.blocks.reduce((accumC, block) => accumC + block.body.txEffects.length, 0),
|
|
126
|
+
0,
|
|
127
|
+
);
|
|
128
|
+
const fromCheckpoint = this.checkpoints[0].number;
|
|
129
|
+
const toCheckpoint = this.checkpoints.at(-1)!.number;
|
|
130
|
+
const fromBlock = this.checkpoints[0].blocks[0].number;
|
|
131
|
+
const toBlock = this.checkpoints.at(-1)!.blocks.at(-1)!.number;
|
|
132
|
+
this.log.info(`Starting epoch ${epochNumber} proving job with checkpoints ${fromCheckpoint} to ${toCheckpoint}`, {
|
|
82
133
|
fromBlock,
|
|
83
134
|
toBlock,
|
|
84
|
-
|
|
135
|
+
epochSizeTxs,
|
|
85
136
|
epochNumber,
|
|
86
137
|
uuid: this.uuid,
|
|
87
138
|
});
|
|
@@ -92,96 +143,186 @@ export class EpochProvingJob implements Traceable {
|
|
|
92
143
|
this.runPromise = promise;
|
|
93
144
|
|
|
94
145
|
try {
|
|
95
|
-
this.
|
|
96
|
-
await
|
|
146
|
+
const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
|
|
147
|
+
const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
|
|
148
|
+
|
|
149
|
+
this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
|
|
150
|
+
await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
|
|
151
|
+
|
|
152
|
+
// Everything in the epoch should have the same chainId and version.
|
|
153
|
+
const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
|
|
97
154
|
|
|
98
|
-
|
|
155
|
+
const previousBlockHeaders = this.gatherPreviousBlockHeaders();
|
|
156
|
+
|
|
157
|
+
await asyncPool(this.config.parallelBlockLimit ?? 32, this.checkpoints, async checkpoint => {
|
|
99
158
|
this.checkState();
|
|
100
159
|
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
|
|
113
|
-
previousHeader: previousHeader.hash(),
|
|
114
|
-
uuid: this.uuid,
|
|
115
|
-
...globalVariables,
|
|
160
|
+
const checkpointIndex = checkpoint.number - fromCheckpoint;
|
|
161
|
+
const checkpointConstants = CheckpointConstantData.from({
|
|
162
|
+
chainId,
|
|
163
|
+
version,
|
|
164
|
+
vkTreeRoot: getVKTreeRoot(),
|
|
165
|
+
protocolContractsHash: protocolContractsHash,
|
|
166
|
+
proverId: this.prover.getProverId().toField(),
|
|
167
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
168
|
+
coinbase: checkpoint.header.coinbase,
|
|
169
|
+
feeRecipient: checkpoint.header.feeRecipient,
|
|
170
|
+
gasFees: checkpoint.header.gasFees,
|
|
116
171
|
});
|
|
172
|
+
const previousHeader = previousBlockHeaders[checkpointIndex];
|
|
173
|
+
const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
|
|
117
174
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, true);
|
|
124
|
-
const processed = await this.processTxs(publicProcessor, txs);
|
|
125
|
-
await this.prover.addTxs(processed);
|
|
126
|
-
await db.close();
|
|
127
|
-
this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
|
|
128
|
-
blockNumber: block.number,
|
|
129
|
-
blockHash: (await block.hash()).toString(),
|
|
175
|
+
this.log.verbose(`Starting processing checkpoint ${checkpoint.number}`, {
|
|
176
|
+
number: checkpoint.number,
|
|
177
|
+
checkpointHash: checkpoint.hash().toString(),
|
|
178
|
+
lastArchive: checkpoint.header.lastArchiveRoot,
|
|
179
|
+
previousHeader: previousHeader.hash(),
|
|
130
180
|
uuid: this.uuid,
|
|
131
181
|
});
|
|
132
182
|
|
|
133
|
-
|
|
134
|
-
|
|
183
|
+
await this.prover.startNewCheckpoint(
|
|
184
|
+
checkpointIndex,
|
|
185
|
+
checkpointConstants,
|
|
186
|
+
l1ToL2Messages,
|
|
187
|
+
checkpoint.blocks.length,
|
|
188
|
+
previousHeader,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
for (const block of checkpoint.blocks) {
|
|
192
|
+
const globalVariables = block.header.globalVariables;
|
|
193
|
+
const txs = this.getTxs(block);
|
|
194
|
+
|
|
195
|
+
this.log.verbose(`Starting processing block ${block.number}`, {
|
|
196
|
+
number: block.number,
|
|
197
|
+
blockHash: (await block.hash()).toString(),
|
|
198
|
+
lastArchive: block.header.lastArchive.root,
|
|
199
|
+
noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
|
|
200
|
+
nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
|
|
201
|
+
publicDataTreeRoot: block.header.state.partial.publicDataTree.root,
|
|
202
|
+
...globalVariables,
|
|
203
|
+
numTxs: txs.length,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Start block proving
|
|
207
|
+
await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
|
|
208
|
+
|
|
209
|
+
// Process public fns
|
|
210
|
+
const db = await this.createFork(BlockNumber(block.number - 1), l1ToL2Messages);
|
|
211
|
+
const config = PublicSimulatorConfig.from({
|
|
212
|
+
proverId: this.prover.getProverId().toField(),
|
|
213
|
+
skipFeeEnforcement: false,
|
|
214
|
+
collectDebugLogs: false,
|
|
215
|
+
collectHints: true,
|
|
216
|
+
collectPublicInputs: true,
|
|
217
|
+
collectStatistics: false,
|
|
218
|
+
});
|
|
219
|
+
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
|
|
220
|
+
const processed = await this.processTxs(publicProcessor, txs);
|
|
221
|
+
await this.prover.addTxs(processed);
|
|
222
|
+
await db.close();
|
|
223
|
+
this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
|
|
224
|
+
blockNumber: block.number,
|
|
225
|
+
blockHash: (await block.hash()).toString(),
|
|
226
|
+
uuid: this.uuid,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Mark block as completed to pad it
|
|
230
|
+
const expectedBlockHeader = block.header;
|
|
231
|
+
await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
|
|
232
|
+
}
|
|
135
233
|
});
|
|
136
234
|
|
|
137
235
|
const executionTime = timer.ms();
|
|
138
236
|
|
|
139
237
|
this.progressState('awaiting-prover');
|
|
140
|
-
const { publicInputs, proof } = await this.prover.
|
|
141
|
-
this.log.info(`
|
|
238
|
+
const { publicInputs, proof, batchedBlobInputs } = await this.prover.finalizeEpoch();
|
|
239
|
+
this.log.info(`Finalized proof for epoch ${epochNumber}`, { epochNumber, uuid: this.uuid, duration: timer.ms() });
|
|
142
240
|
|
|
143
241
|
this.progressState('publishing-proof');
|
|
144
|
-
|
|
242
|
+
|
|
243
|
+
if (this.config.skipSubmitProof) {
|
|
244
|
+
this.log.info(
|
|
245
|
+
`Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
|
|
246
|
+
);
|
|
247
|
+
this.state = 'completed';
|
|
248
|
+
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const success = await this.publisher.submitEpochProof({
|
|
253
|
+
fromCheckpoint,
|
|
254
|
+
toCheckpoint,
|
|
255
|
+
epochNumber,
|
|
256
|
+
publicInputs,
|
|
257
|
+
proof,
|
|
258
|
+
batchedBlobInputs,
|
|
259
|
+
attestations,
|
|
260
|
+
});
|
|
145
261
|
if (!success) {
|
|
146
262
|
throw new Error('Failed to submit epoch proof to L1');
|
|
147
263
|
}
|
|
148
264
|
|
|
149
|
-
this.log.info(`Submitted proof for epoch ${epochNumber} (
|
|
265
|
+
this.log.info(`Submitted proof for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`, {
|
|
150
266
|
epochNumber,
|
|
151
267
|
uuid: this.uuid,
|
|
152
268
|
});
|
|
153
269
|
this.state = 'completed';
|
|
154
|
-
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
|
|
270
|
+
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
|
|
155
271
|
} catch (err: any) {
|
|
156
272
|
if (err && err.name === 'HaltExecutionError') {
|
|
157
|
-
this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
|
|
273
|
+
this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
|
|
274
|
+
uuid: this.uuid,
|
|
275
|
+
epochNumber,
|
|
276
|
+
details: err.message,
|
|
277
|
+
});
|
|
158
278
|
return;
|
|
159
279
|
}
|
|
160
280
|
this.log.error(`Error running epoch ${epochNumber} prover job`, err, { uuid: this.uuid, epochNumber });
|
|
161
|
-
this.state
|
|
281
|
+
if (this.state === 'processing' || this.state === 'awaiting-prover' || this.state === 'publishing-proof') {
|
|
282
|
+
this.state = 'failed';
|
|
283
|
+
}
|
|
162
284
|
} finally {
|
|
163
285
|
clearTimeout(this.deadlineTimeoutHandler);
|
|
164
|
-
await this.
|
|
286
|
+
await this.epochCheckPromise?.stop();
|
|
165
287
|
await this.prover.stop();
|
|
166
288
|
resolve();
|
|
167
289
|
}
|
|
168
290
|
}
|
|
169
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Create a new db fork for tx processing, inserting all L1 to L2.
|
|
294
|
+
* 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.
|
|
295
|
+
*/
|
|
296
|
+
private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) {
|
|
297
|
+
const db = await this.dbProvider.fork(blockNumber);
|
|
298
|
+
const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
|
|
299
|
+
l1ToL2Messages,
|
|
300
|
+
Fr.ZERO,
|
|
301
|
+
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
302
|
+
'Too many L1 to L2 messages',
|
|
303
|
+
);
|
|
304
|
+
this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
|
|
305
|
+
blockNumber,
|
|
306
|
+
l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
|
|
307
|
+
});
|
|
308
|
+
await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
|
|
309
|
+
return db;
|
|
310
|
+
}
|
|
311
|
+
|
|
170
312
|
private progressState(state: EpochProvingJobState) {
|
|
171
313
|
this.checkState();
|
|
172
314
|
this.state = state;
|
|
173
315
|
}
|
|
174
316
|
|
|
175
317
|
private checkState() {
|
|
176
|
-
if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed') {
|
|
318
|
+
if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed' || this.state === 'reorg') {
|
|
177
319
|
throw new HaltExecutionError(this.state);
|
|
178
320
|
}
|
|
179
321
|
}
|
|
180
322
|
|
|
181
|
-
public async stop(state:
|
|
323
|
+
public async stop(state: EpochProvingJobTerminalState = 'stopped') {
|
|
182
324
|
this.state = state;
|
|
183
325
|
this.prover.cancel();
|
|
184
|
-
// TODO(palla/prover): Stop the publisher as well
|
|
185
326
|
if (this.runPromise) {
|
|
186
327
|
await this.runPromise;
|
|
187
328
|
}
|
|
@@ -207,24 +348,52 @@ export class EpochProvingJob implements Traceable {
|
|
|
207
348
|
}
|
|
208
349
|
}
|
|
209
350
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
351
|
+
/**
|
|
352
|
+
* Kicks off a running promise that queries the archiver for the set of L2 blocks of the current epoch.
|
|
353
|
+
* If those change, stops the proving job with a `rerun` state, so the node re-enqueues it.
|
|
354
|
+
*/
|
|
355
|
+
private async scheduleEpochCheck() {
|
|
356
|
+
const l2BlockSource = this.l2BlockSource;
|
|
357
|
+
if (!l2BlockSource) {
|
|
358
|
+
this.log.warn(`No L2 block source available, skipping epoch check`);
|
|
359
|
+
return;
|
|
214
360
|
}
|
|
215
|
-
|
|
361
|
+
|
|
362
|
+
const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
|
|
363
|
+
this.epochCheckPromise = new RunningPromise(
|
|
364
|
+
async () => {
|
|
365
|
+
const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
|
|
366
|
+
const blockHashes = await Promise.all(blocks.map(block => block.hash()));
|
|
367
|
+
const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
|
|
368
|
+
const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
|
|
369
|
+
if (blocks.length !== thisBlocks.length || !blockHashes.every((block, i) => block.equals(thisBlockHashes[i]))) {
|
|
370
|
+
this.log.warn('Epoch blocks changed underfoot', {
|
|
371
|
+
uuid: this.uuid,
|
|
372
|
+
epochNumber: this.epochNumber,
|
|
373
|
+
oldBlockHashes: thisBlockHashes,
|
|
374
|
+
newBlockHashes: blockHashes,
|
|
375
|
+
});
|
|
376
|
+
void this.stop('reorg');
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
this.log,
|
|
380
|
+
intervalMs,
|
|
381
|
+
).start();
|
|
382
|
+
this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/* Returns the last block header in the previous checkpoint for all checkpoints in the epoch */
|
|
386
|
+
private gatherPreviousBlockHeaders() {
|
|
387
|
+
const lastBlocks = this.checkpoints.map(checkpoint => checkpoint.blocks.at(-1)!);
|
|
388
|
+
return [this.data.previousBlockHeader, ...lastBlocks.map(block => block.header).slice(0, -1)];
|
|
216
389
|
}
|
|
217
390
|
|
|
218
|
-
private
|
|
219
|
-
|
|
220
|
-
const txsAndHashes = await Promise.all(this.txs.map(async tx => ({ tx, hash: await tx.getTxHash() })));
|
|
221
|
-
return txsAndHashes
|
|
222
|
-
.filter(txAndHash => txHashes.includes(txAndHash.hash.toBigInt()))
|
|
223
|
-
.map(txAndHash => txAndHash.tx);
|
|
391
|
+
private getTxs(block: L2BlockNew): Tx[] {
|
|
392
|
+
return block.body.txEffects.map(txEffect => this.txs.get(txEffect.txHash.toString())!);
|
|
224
393
|
}
|
|
225
394
|
|
|
226
|
-
private getL1ToL2Messages(
|
|
227
|
-
return this.
|
|
395
|
+
private getL1ToL2Messages(checkpoint: Checkpoint) {
|
|
396
|
+
return this.data.l1ToL2Messages[checkpoint.number];
|
|
228
397
|
}
|
|
229
398
|
|
|
230
399
|
private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
|
|
@@ -232,8 +401,11 @@ export class EpochProvingJob implements Traceable {
|
|
|
232
401
|
const [processedTxs, failedTxs] = await publicProcessor.process(txs, { deadline });
|
|
233
402
|
|
|
234
403
|
if (failedTxs.length) {
|
|
404
|
+
const failedTxHashes = await Promise.all(failedTxs.map(({ tx }) => tx.getTxHash()));
|
|
235
405
|
throw new Error(
|
|
236
|
-
`Txs failed processing: ${failedTxs
|
|
406
|
+
`Txs failed processing: ${failedTxs
|
|
407
|
+
.map(({ error }, index) => `${failedTxHashes[index]} (${error})`)
|
|
408
|
+
.join(', ')}`,
|
|
237
409
|
);
|
|
238
410
|
}
|
|
239
411
|
|
|
@@ -246,7 +418,7 @@ export class EpochProvingJob implements Traceable {
|
|
|
246
418
|
}
|
|
247
419
|
|
|
248
420
|
class HaltExecutionError extends Error {
|
|
249
|
-
constructor(state: EpochProvingJobState) {
|
|
421
|
+
constructor(public readonly state: EpochProvingJobState) {
|
|
250
422
|
super(`Halted execution due to state ${state}`);
|
|
251
423
|
this.name = 'HaltExecutionError';
|
|
252
424
|
}
|