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