@aztec/prover-node 4.0.0-nightly.20250907 → 4.0.0-nightly.20260108
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/index.d.ts +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts +1 -1
- package/dest/bin/run-failed-epoch.d.ts +1 -1
- package/dest/bin/run-failed-epoch.js +1 -1
- package/dest/config.d.ts +7 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +21 -15
- package/dest/factory.d.ts +2 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +31 -13
- package/dest/index.d.ts +1 -1
- 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 -16
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +493 -77
- package/dest/metrics.d.ts +4 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +9 -3
- 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 +11 -11
- package/dest/monitors/index.d.ts +1 -1
- package/dest/prover-node-publisher.d.ts +9 -10
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +51 -53
- package/dest/prover-node.d.ts +8 -7
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +435 -50
- package/dest/prover-publisher-factory.d.ts +6 -2
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +6 -0
- package/dest/test/index.d.ts +1 -1
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +26 -25
- package/src/bin/run-failed-epoch.ts +2 -2
- package/src/config.ts +33 -30
- package/src/factory.ts +37 -20
- package/src/job/epoch-proving-job-data.ts +31 -25
- package/src/job/epoch-proving-job.ts +138 -82
- package/src/metrics.ts +16 -4
- package/src/monitors/epoch-monitor.ts +16 -13
- package/src/prover-node-publisher.ts +74 -73
- package/src/prover-node.ts +52 -45
- package/src/prover-publisher-factory.ts +12 -1
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BatchedBlob, getEthBlobEvaluationInputs } from '@aztec/blob-lib';
|
|
2
2
|
import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type RollupContract,
|
|
6
|
-
RollupContract as RollupContractClass,
|
|
7
|
-
type ViemCommitteeAttestation,
|
|
8
|
-
} from '@aztec/ethereum';
|
|
3
|
+
import type { RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum/contracts';
|
|
4
|
+
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
9
5
|
import { makeTuple } from '@aztec/foundation/array';
|
|
6
|
+
import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
10
7
|
import { areArraysEqual } from '@aztec/foundation/collection';
|
|
8
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
11
9
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
12
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
13
10
|
import { createLogger } from '@aztec/foundation/log';
|
|
14
11
|
import type { Tuple } from '@aztec/foundation/serialize';
|
|
15
|
-
import { InterruptibleSleep } from '@aztec/foundation/sleep';
|
|
16
12
|
import { Timer } from '@aztec/foundation/timer';
|
|
17
13
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
18
14
|
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
|
|
15
|
+
import { CommitteeAttestation, CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
19
16
|
import type { Proof } from '@aztec/stdlib/proofs';
|
|
20
17
|
import type { FeeRecipient, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
|
|
21
18
|
import type { L1PublishProofStats } from '@aztec/stdlib/stats';
|
|
@@ -39,8 +36,6 @@ export type L1SubmitEpochProofArgs = {
|
|
|
39
36
|
};
|
|
40
37
|
|
|
41
38
|
export class ProverNodePublisher {
|
|
42
|
-
private interruptibleSleep = new InterruptibleSleep();
|
|
43
|
-
private sleepTimeMs: number;
|
|
44
39
|
private interrupted = false;
|
|
45
40
|
private metrics: ProverNodePublisherMetrics;
|
|
46
41
|
|
|
@@ -58,8 +53,6 @@ export class ProverNodePublisher {
|
|
|
58
53
|
telemetry?: TelemetryClient;
|
|
59
54
|
},
|
|
60
55
|
) {
|
|
61
|
-
this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
|
|
62
|
-
|
|
63
56
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
64
57
|
|
|
65
58
|
this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
|
|
@@ -80,12 +73,13 @@ export class ProverNodePublisher {
|
|
|
80
73
|
*/
|
|
81
74
|
public interrupt() {
|
|
82
75
|
this.interrupted = true;
|
|
83
|
-
this.
|
|
76
|
+
this.l1TxUtils.interrupt();
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
/** Restarts the publisher after calling `interrupt`. */
|
|
87
80
|
public restart() {
|
|
88
81
|
this.interrupted = false;
|
|
82
|
+
this.l1TxUtils.restart();
|
|
89
83
|
}
|
|
90
84
|
|
|
91
85
|
public getSenderAddress() {
|
|
@@ -93,16 +87,17 @@ export class ProverNodePublisher {
|
|
|
93
87
|
}
|
|
94
88
|
|
|
95
89
|
public async submitEpochProof(args: {
|
|
96
|
-
epochNumber:
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
epochNumber: EpochNumber;
|
|
91
|
+
fromCheckpoint: CheckpointNumber;
|
|
92
|
+
toCheckpoint: CheckpointNumber;
|
|
99
93
|
publicInputs: RootRollupPublicInputs;
|
|
100
94
|
proof: Proof;
|
|
101
95
|
batchedBlobInputs: BatchedBlob;
|
|
102
96
|
attestations: ViemCommitteeAttestation[];
|
|
103
97
|
}): Promise<boolean> {
|
|
104
|
-
const { epochNumber,
|
|
105
|
-
const ctx = { epochNumber,
|
|
98
|
+
const { epochNumber, fromCheckpoint, toCheckpoint } = args;
|
|
99
|
+
const ctx = { epochNumber, fromCheckpoint, toCheckpoint };
|
|
100
|
+
|
|
106
101
|
if (!this.interrupted) {
|
|
107
102
|
const timer = new Timer();
|
|
108
103
|
// Validate epoch proof range and hashes are correct before submitting
|
|
@@ -110,6 +105,7 @@ export class ProverNodePublisher {
|
|
|
110
105
|
|
|
111
106
|
const txReceipt = await this.sendSubmitEpochProofTx(args);
|
|
112
107
|
if (!txReceipt) {
|
|
108
|
+
this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
|
|
113
109
|
return false;
|
|
114
110
|
}
|
|
115
111
|
|
|
@@ -142,55 +138,59 @@ export class ProverNodePublisher {
|
|
|
142
138
|
}
|
|
143
139
|
|
|
144
140
|
this.metrics.recordFailedTx();
|
|
145
|
-
this.log.error(`Rollup
|
|
146
|
-
await this.sleepOrInterrupted();
|
|
141
|
+
this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
|
|
147
142
|
}
|
|
148
143
|
|
|
149
|
-
this.log.verbose('
|
|
144
|
+
this.log.verbose('Checkpoint data syncing interrupted', ctx);
|
|
150
145
|
return false;
|
|
151
146
|
}
|
|
152
147
|
|
|
153
148
|
private async validateEpochProofSubmission(args: {
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
fromCheckpoint: CheckpointNumber;
|
|
150
|
+
toCheckpoint: CheckpointNumber;
|
|
156
151
|
publicInputs: RootRollupPublicInputs;
|
|
157
152
|
proof: Proof;
|
|
158
153
|
batchedBlobInputs: BatchedBlob;
|
|
159
154
|
attestations: ViemCommitteeAttestation[];
|
|
160
155
|
}) {
|
|
161
|
-
const {
|
|
156
|
+
const { fromCheckpoint, toCheckpoint, publicInputs, batchedBlobInputs } = args;
|
|
162
157
|
|
|
163
|
-
// Check that the
|
|
164
|
-
const {
|
|
165
|
-
// Don't publish if proven is beyond our
|
|
166
|
-
if (proven >
|
|
167
|
-
throw new Error(
|
|
158
|
+
// Check that the checkpoint numbers match the expected epoch to be proven
|
|
159
|
+
const { pending, proven } = await this.rollupContract.getTips();
|
|
160
|
+
// Don't publish if proven is beyond our toCheckpoint, pointless to do so
|
|
161
|
+
if (proven > toCheckpoint) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proven checkpoint is ${proven}`,
|
|
164
|
+
);
|
|
168
165
|
}
|
|
169
|
-
//
|
|
170
|
-
if (
|
|
171
|
-
throw new Error(
|
|
166
|
+
// toCheckpoint can't be greater than pending
|
|
167
|
+
if (toCheckpoint > pending) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as pending checkpoint is ${pending}`,
|
|
170
|
+
);
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
// Check the archive for the immediate
|
|
175
|
-
const
|
|
176
|
-
if (publicInputs.previousArchiveRoot.
|
|
173
|
+
// Check the archive for the immediate checkpoint before the epoch
|
|
174
|
+
const checkpointLog = await this.rollupContract.getCheckpoint(CheckpointNumber(fromCheckpoint - 1));
|
|
175
|
+
if (!publicInputs.previousArchiveRoot.equals(checkpointLog.archive)) {
|
|
177
176
|
throw new Error(
|
|
178
|
-
`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${
|
|
177
|
+
`Previous archive root mismatch: ${publicInputs.previousArchiveRoot.toString()} !== ${checkpointLog.archive.toString()}`,
|
|
179
178
|
);
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
// Check the archive for the last
|
|
183
|
-
const
|
|
184
|
-
if (publicInputs.endArchiveRoot.
|
|
181
|
+
// Check the archive for the last checkpoint in the epoch
|
|
182
|
+
const endCheckpointLog = await this.rollupContract.getCheckpoint(toCheckpoint);
|
|
183
|
+
if (!publicInputs.endArchiveRoot.equals(endCheckpointLog.archive)) {
|
|
185
184
|
throw new Error(
|
|
186
|
-
`End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${
|
|
185
|
+
`End archive root mismatch: ${publicInputs.endArchiveRoot.toString()} !== ${endCheckpointLog.archive.toString()}`,
|
|
187
186
|
);
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
// Check the batched blob inputs from the root rollup against the batched blob computed in ts
|
|
191
|
-
|
|
190
|
+
const finalBlobAccumulator = batchedBlobInputs.toFinalBlobAccumulator();
|
|
191
|
+
if (!publicInputs.blobPublicInputs.equals(finalBlobAccumulator)) {
|
|
192
192
|
throw new Error(
|
|
193
|
-
`Batched blob mismatch: ${inspect(publicInputs.blobPublicInputs)} !== ${inspect(
|
|
193
|
+
`Batched blob mismatch: ${inspect(publicInputs.blobPublicInputs)} !== ${inspect(finalBlobAccumulator)}`,
|
|
194
194
|
);
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -200,7 +200,7 @@ export class ProverNodePublisher {
|
|
|
200
200
|
);
|
|
201
201
|
const argsPublicInputs = [...publicInputs.toFields()];
|
|
202
202
|
|
|
203
|
-
if (!areArraysEqual(rollupPublicInputs
|
|
203
|
+
if (!areArraysEqual(rollupPublicInputs, argsPublicInputs, (a, b) => a.equals(b))) {
|
|
204
204
|
const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
|
|
205
205
|
throw new Error(
|
|
206
206
|
`Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`,
|
|
@@ -209,8 +209,8 @@ export class ProverNodePublisher {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
private async sendSubmitEpochProofTx(args: {
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
fromCheckpoint: CheckpointNumber;
|
|
213
|
+
toCheckpoint: CheckpointNumber;
|
|
214
214
|
publicInputs: RootRollupPublicInputs;
|
|
215
215
|
proof: Proof;
|
|
216
216
|
batchedBlobInputs: BatchedBlob;
|
|
@@ -220,8 +220,8 @@ export class ProverNodePublisher {
|
|
|
220
220
|
|
|
221
221
|
this.log.info(`Submitting epoch proof to L1 rollup contract`, {
|
|
222
222
|
proofSize: args.proof.withoutPublicInputs().length,
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
fromCheckpoint: args.fromCheckpoint,
|
|
224
|
+
toCheckpoint: args.toCheckpoint,
|
|
225
225
|
});
|
|
226
226
|
const data = encodeFunctionData({
|
|
227
227
|
abi: RollupAbi,
|
|
@@ -230,53 +230,56 @@ export class ProverNodePublisher {
|
|
|
230
230
|
});
|
|
231
231
|
try {
|
|
232
232
|
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data });
|
|
233
|
+
if (receipt.status !== 'success') {
|
|
234
|
+
const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
|
|
235
|
+
data,
|
|
236
|
+
{
|
|
237
|
+
args: [...txArgs],
|
|
238
|
+
functionName: 'submitEpochRootProof',
|
|
239
|
+
abi: RollupAbi,
|
|
240
|
+
address: this.rollupContract.address,
|
|
241
|
+
},
|
|
242
|
+
/*blobInputs*/ undefined,
|
|
243
|
+
/*stateOverride*/ [],
|
|
244
|
+
);
|
|
245
|
+
this.log.error(`Rollup submit epoch proof tx reverted with ${errorMsg ?? 'unknown error'}`);
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
233
248
|
return receipt;
|
|
234
249
|
} catch (err) {
|
|
235
250
|
this.log.error(`Rollup submit epoch proof failed`, err);
|
|
236
|
-
const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
|
|
237
|
-
data,
|
|
238
|
-
{
|
|
239
|
-
args: [...txArgs],
|
|
240
|
-
functionName: 'submitEpochRootProof',
|
|
241
|
-
abi: RollupAbi,
|
|
242
|
-
address: this.rollupContract.address,
|
|
243
|
-
},
|
|
244
|
-
/*blobInputs*/ undefined,
|
|
245
|
-
/*stateOverride*/ [],
|
|
246
|
-
);
|
|
247
|
-
this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
|
|
248
251
|
return undefined;
|
|
249
252
|
}
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
private getEpochProofPublicInputsArgs(args: {
|
|
253
|
-
|
|
254
|
-
|
|
256
|
+
fromCheckpoint: CheckpointNumber;
|
|
257
|
+
toCheckpoint: CheckpointNumber;
|
|
255
258
|
publicInputs: RootRollupPublicInputs;
|
|
256
259
|
batchedBlobInputs: BatchedBlob;
|
|
257
260
|
attestations: ViemCommitteeAttestation[];
|
|
258
261
|
}) {
|
|
259
262
|
// Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
|
|
260
263
|
return [
|
|
261
|
-
BigInt(args.
|
|
262
|
-
BigInt(args.
|
|
264
|
+
BigInt(args.fromCheckpoint) /*_start*/,
|
|
265
|
+
BigInt(args.toCheckpoint) /*_end*/,
|
|
263
266
|
{
|
|
264
267
|
previousArchive: args.publicInputs.previousArchiveRoot.toString(),
|
|
265
268
|
endArchive: args.publicInputs.endArchiveRoot.toString(),
|
|
266
|
-
proverId: EthAddress.fromField(args.publicInputs.proverId).toString(),
|
|
269
|
+
proverId: EthAddress.fromField(args.publicInputs.constants.proverId).toString(),
|
|
267
270
|
} /*_args*/,
|
|
268
271
|
makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
|
|
269
272
|
i % 2 === 0
|
|
270
273
|
? args.publicInputs.fees[i / 2].recipient.toField().toString()
|
|
271
274
|
: args.publicInputs.fees[(i - 1) / 2].value.toString(),
|
|
272
275
|
) /*_fees*/,
|
|
273
|
-
args.batchedBlobInputs
|
|
276
|
+
getEthBlobEvaluationInputs(args.batchedBlobInputs) /*_blobPublicInputs*/,
|
|
274
277
|
] as const;
|
|
275
278
|
}
|
|
276
279
|
|
|
277
280
|
private getSubmitEpochProofArgs(args: {
|
|
278
|
-
|
|
279
|
-
|
|
281
|
+
fromCheckpoint: CheckpointNumber;
|
|
282
|
+
toCheckpoint: CheckpointNumber;
|
|
280
283
|
publicInputs: RootRollupPublicInputs;
|
|
281
284
|
proof: Proof;
|
|
282
285
|
batchedBlobInputs: BatchedBlob;
|
|
@@ -290,13 +293,11 @@ export class ProverNodePublisher {
|
|
|
290
293
|
end: argsArray[1],
|
|
291
294
|
args: argsArray[2],
|
|
292
295
|
fees: argsArray[3],
|
|
293
|
-
attestations:
|
|
296
|
+
attestations: new CommitteeAttestationsAndSigners(
|
|
297
|
+
args.attestations.map(a => CommitteeAttestation.fromViem(a)),
|
|
298
|
+
).getPackedAttestations(),
|
|
294
299
|
blobInputs: argsArray[4],
|
|
295
300
|
proof: proofHex,
|
|
296
301
|
};
|
|
297
302
|
}
|
|
298
|
-
|
|
299
|
-
protected async sleepOrInterrupted() {
|
|
300
|
-
await this.interruptibleSleep.sleep(this.sleepTimeMs);
|
|
301
|
-
}
|
|
302
303
|
}
|
package/src/prover-node.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { Archiver } from '@aztec/archiver';
|
|
2
|
-
import type { RollupContract } from '@aztec/ethereum';
|
|
2
|
+
import type { RollupContract } from '@aztec/ethereum/contracts';
|
|
3
|
+
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
3
4
|
import { assertRequired, compact, pick, sum } from '@aztec/foundation/collection';
|
|
5
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
6
|
import { memoize } from '@aztec/foundation/decorators';
|
|
5
|
-
import type { Fr } from '@aztec/foundation/fields';
|
|
6
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
8
9
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
9
10
|
import type { P2PClient } from '@aztec/p2p';
|
|
10
11
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
11
|
-
import type {
|
|
12
|
+
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
13
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
12
14
|
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
13
15
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
14
16
|
import { getProofSubmissionDeadlineTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
@@ -87,6 +89,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
87
89
|
txGatheringMaxParallelRequestsPerNode: 100,
|
|
88
90
|
txGatheringTimeoutMs: 120_000,
|
|
89
91
|
proverNodeFailedEpochStore: undefined,
|
|
92
|
+
proverNodeEpochProvingDelayMs: undefined,
|
|
90
93
|
...compact(config),
|
|
91
94
|
};
|
|
92
95
|
|
|
@@ -113,7 +116,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
113
116
|
* @param epochNumber - The epoch number that was just completed.
|
|
114
117
|
* @returns false if there is an error, true otherwise
|
|
115
118
|
*/
|
|
116
|
-
async handleEpochReadyToProve(epochNumber:
|
|
119
|
+
async handleEpochReadyToProve(epochNumber: EpochNumber): Promise<boolean> {
|
|
117
120
|
try {
|
|
118
121
|
this.log.debug(`Running jobs as ${epochNumber} is ready to prove`, {
|
|
119
122
|
jobs: Array.from(this.jobs.values()).map(job => `${job.getEpochNumber()}:${job.getId()}`),
|
|
@@ -143,6 +146,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
143
146
|
*/
|
|
144
147
|
async start() {
|
|
145
148
|
this.epochsMonitor.start(this);
|
|
149
|
+
await this.publisherFactory.start();
|
|
146
150
|
this.publisher = await this.publisherFactory.create();
|
|
147
151
|
await this.rewardsMetrics.start();
|
|
148
152
|
this.l1Metrics.start();
|
|
@@ -158,6 +162,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
158
162
|
await this.prover.stop();
|
|
159
163
|
await tryStop(this.p2pClient);
|
|
160
164
|
await tryStop(this.l2BlockSource);
|
|
165
|
+
await tryStop(this.publisherFactory);
|
|
161
166
|
this.publisher?.interrupt();
|
|
162
167
|
await Promise.all(Array.from(this.jobs.values()).map(job => job.stop()));
|
|
163
168
|
await this.worldState.stop();
|
|
@@ -181,8 +186,8 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
181
186
|
/**
|
|
182
187
|
* Starts a proving process and returns immediately.
|
|
183
188
|
*/
|
|
184
|
-
public async startProof(epochNumber:
|
|
185
|
-
const job = await this.createProvingJob(
|
|
189
|
+
public async startProof(epochNumber: EpochNumber) {
|
|
190
|
+
const job = await this.createProvingJob(epochNumber, { skipEpochCheck: true });
|
|
186
191
|
void this.runJob(job);
|
|
187
192
|
}
|
|
188
193
|
|
|
@@ -235,21 +240,20 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
235
240
|
/**
|
|
236
241
|
* Returns an array of jobs being processed.
|
|
237
242
|
*/
|
|
238
|
-
public getJobs(): Promise<{ uuid: string; status: EpochProvingJobState; epochNumber:
|
|
243
|
+
public getJobs(): Promise<{ uuid: string; status: EpochProvingJobState; epochNumber: EpochNumber }[]> {
|
|
239
244
|
return Promise.resolve(
|
|
240
245
|
Array.from(this.jobs.entries()).map(([uuid, job]) => ({
|
|
241
246
|
uuid,
|
|
242
247
|
status: job.getState(),
|
|
243
|
-
epochNumber:
|
|
248
|
+
epochNumber: job.getEpochNumber(),
|
|
244
249
|
})),
|
|
245
250
|
);
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
protected async getActiveJobsForEpoch(
|
|
249
|
-
|
|
254
|
+
epochNumber: EpochNumber,
|
|
250
255
|
): Promise<{ uuid: string; status: EpochProvingJobState }[]> {
|
|
251
256
|
const jobs = await this.getJobs();
|
|
252
|
-
const epochNumber = Number(epochBigInt);
|
|
253
257
|
return jobs.filter(job => job.epochNumber === epochNumber && !EpochProvingJobTerminalState.includes(job.status));
|
|
254
258
|
}
|
|
255
259
|
|
|
@@ -260,18 +264,21 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
260
264
|
}
|
|
261
265
|
}
|
|
262
266
|
|
|
263
|
-
@trackSpan('ProverNode.createProvingJob', epochNumber => ({ [Attributes.EPOCH_NUMBER]:
|
|
264
|
-
private async createProvingJob(epochNumber:
|
|
267
|
+
@trackSpan('ProverNode.createProvingJob', epochNumber => ({ [Attributes.EPOCH_NUMBER]: epochNumber }))
|
|
268
|
+
private async createProvingJob(epochNumber: EpochNumber, opts: { skipEpochCheck?: boolean } = {}) {
|
|
265
269
|
this.checkMaximumPendingJobs();
|
|
266
270
|
|
|
267
271
|
this.publisher = await this.publisherFactory.create();
|
|
268
272
|
|
|
269
273
|
// Gather all data for this epoch
|
|
270
274
|
const epochData = await this.gatherEpochData(epochNumber);
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
+
const fromCheckpoint = epochData.checkpoints[0].number;
|
|
276
|
+
const toCheckpoint = epochData.checkpoints.at(-1)!.number;
|
|
277
|
+
const fromBlock = epochData.checkpoints[0].blocks[0].number;
|
|
278
|
+
const toBlock = epochData.checkpoints.at(-1)!.blocks.at(-1)!.number;
|
|
279
|
+
this.log.verbose(
|
|
280
|
+
`Creating proving job for epoch ${epochNumber} for checkpoint range ${fromCheckpoint} to ${toCheckpoint} and block range ${fromBlock} to ${toBlock}`,
|
|
281
|
+
);
|
|
275
282
|
|
|
276
283
|
// Fast forward world state to right before the target block and get a fork
|
|
277
284
|
await this.worldState.syncImmediate(toBlock);
|
|
@@ -286,7 +293,6 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
286
293
|
// Set deadline for this job to run. It will abort if it takes too long.
|
|
287
294
|
const deadlineTs = getProofSubmissionDeadlineTimestamp(epochNumber, await this.getL1Constants());
|
|
288
295
|
const deadline = new Date(Number(deadlineTs) * 1000);
|
|
289
|
-
|
|
290
296
|
const job = this.doCreateEpochProvingJob(epochData, deadline, publicProcessorFactory, this.publisher, opts);
|
|
291
297
|
this.jobs.set(job.getId(), job);
|
|
292
298
|
return job;
|
|
@@ -297,30 +303,32 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
297
303
|
return this.l2BlockSource.getL1Constants();
|
|
298
304
|
}
|
|
299
305
|
|
|
300
|
-
@trackSpan('ProverNode.gatherEpochData', epochNumber => ({ [Attributes.EPOCH_NUMBER]:
|
|
301
|
-
private async gatherEpochData(epochNumber:
|
|
302
|
-
const
|
|
303
|
-
const txArray = await this.gatherTxs(epochNumber,
|
|
306
|
+
@trackSpan('ProverNode.gatherEpochData', epochNumber => ({ [Attributes.EPOCH_NUMBER]: epochNumber }))
|
|
307
|
+
private async gatherEpochData(epochNumber: EpochNumber): Promise<EpochProvingJobData> {
|
|
308
|
+
const checkpoints = await this.gatherCheckpoints(epochNumber);
|
|
309
|
+
const txArray = await this.gatherTxs(epochNumber, checkpoints);
|
|
304
310
|
const txs = new Map<string, Tx>(txArray.map(tx => [tx.getTxHash().toString(), tx]));
|
|
305
|
-
const l1ToL2Messages = await this.gatherMessages(epochNumber,
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
const
|
|
311
|
+
const l1ToL2Messages = await this.gatherMessages(epochNumber, checkpoints);
|
|
312
|
+
const [firstBlock] = checkpoints[0].blocks;
|
|
313
|
+
const previousBlockHeader = await this.gatherPreviousBlockHeader(epochNumber, firstBlock.number - 1);
|
|
314
|
+
const [lastPublishedCheckpoint] = await this.l2BlockSource.getPublishedCheckpoints(checkpoints.at(-1)!.number, 1);
|
|
315
|
+
const attestations = lastPublishedCheckpoint?.attestations ?? [];
|
|
309
316
|
|
|
310
|
-
return {
|
|
317
|
+
return { checkpoints, txs, l1ToL2Messages, epochNumber, previousBlockHeader, attestations };
|
|
311
318
|
}
|
|
312
319
|
|
|
313
|
-
private async
|
|
314
|
-
const
|
|
315
|
-
if (
|
|
320
|
+
private async gatherCheckpoints(epochNumber: EpochNumber) {
|
|
321
|
+
const checkpoints = await this.l2BlockSource.getCheckpointsForEpoch(epochNumber);
|
|
322
|
+
if (checkpoints.length === 0) {
|
|
316
323
|
throw new EmptyEpochError(epochNumber);
|
|
317
324
|
}
|
|
318
|
-
return
|
|
325
|
+
return checkpoints;
|
|
319
326
|
}
|
|
320
327
|
|
|
321
|
-
private async gatherTxs(epochNumber:
|
|
328
|
+
private async gatherTxs(epochNumber: EpochNumber, checkpoints: Checkpoint[]) {
|
|
322
329
|
const deadline = new Date(this.dateProvider.now() + this.config.txGatheringTimeoutMs);
|
|
323
330
|
const txProvider = this.p2pClient.getTxProvider();
|
|
331
|
+
const blocks = checkpoints.flatMap(checkpoint => checkpoint.blocks);
|
|
324
332
|
const txsByBlock = await Promise.all(blocks.map(block => txProvider.getTxsForBlock(block, { deadline })));
|
|
325
333
|
const txs = txsByBlock.map(({ txs }) => txs).flat();
|
|
326
334
|
const missingTxs = txsByBlock.map(({ missingTxs }) => missingTxs).flat();
|
|
@@ -333,25 +341,24 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
333
341
|
throw new Error(`Txs not found for epoch ${epochNumber}: ${missingTxs.map(hash => hash.toString()).join(', ')}`);
|
|
334
342
|
}
|
|
335
343
|
|
|
336
|
-
private async gatherMessages(epochNumber:
|
|
337
|
-
const messages = await Promise.all(
|
|
344
|
+
private async gatherMessages(epochNumber: EpochNumber, checkpoints: Checkpoint[]) {
|
|
345
|
+
const messages = await Promise.all(checkpoints.map(c => this.l1ToL2MessageSource.getL1ToL2Messages(c.number)));
|
|
338
346
|
const messageCount = sum(messages.map(m => m.length));
|
|
339
347
|
this.log.verbose(`Gathered all ${messageCount} messages for epoch ${epochNumber}`, { epochNumber });
|
|
340
|
-
const
|
|
341
|
-
for (let i = 0; i <
|
|
342
|
-
|
|
348
|
+
const messagesByCheckpoint: Record<CheckpointNumber, Fr[]> = {};
|
|
349
|
+
for (let i = 0; i < checkpoints.length; i++) {
|
|
350
|
+
messagesByCheckpoint[checkpoints[i].number] = messages[i];
|
|
343
351
|
}
|
|
344
|
-
return
|
|
352
|
+
return messagesByCheckpoint;
|
|
345
353
|
}
|
|
346
354
|
|
|
347
|
-
private async gatherPreviousBlockHeader(epochNumber:
|
|
348
|
-
const previousBlockNumber = initialBlock.number - 1;
|
|
355
|
+
private async gatherPreviousBlockHeader(epochNumber: EpochNumber, previousBlockNumber: number) {
|
|
349
356
|
const header = await (previousBlockNumber === 0
|
|
350
357
|
? this.worldState.getCommitted().getInitialHeader()
|
|
351
|
-
: this.l2BlockSource.getBlockHeader(previousBlockNumber));
|
|
358
|
+
: this.l2BlockSource.getBlockHeader(BlockNumber(previousBlockNumber)));
|
|
352
359
|
|
|
353
360
|
if (!header) {
|
|
354
|
-
throw new Error(`Previous block header ${
|
|
361
|
+
throw new Error(`Previous block header ${previousBlockNumber} not found for proving epoch ${epochNumber}`);
|
|
355
362
|
}
|
|
356
363
|
|
|
357
364
|
this.log.verbose(`Gathered previous block header ${header.getBlockNumber()} for epoch ${epochNumber}`);
|
|
@@ -366,7 +373,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
366
373
|
publisher: ProverNodePublisher,
|
|
367
374
|
opts: { skipEpochCheck?: boolean } = {},
|
|
368
375
|
) {
|
|
369
|
-
const { proverNodeMaxParallelBlocksPerEpoch: parallelBlockLimit } = this.config;
|
|
376
|
+
const { proverNodeMaxParallelBlocksPerEpoch: parallelBlockLimit, proverNodeDisableProofPublish } = this.config;
|
|
370
377
|
return new EpochProvingJob(
|
|
371
378
|
data,
|
|
372
379
|
this.worldState,
|
|
@@ -376,7 +383,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
376
383
|
this.l2BlockSource,
|
|
377
384
|
this.jobMetrics,
|
|
378
385
|
deadline,
|
|
379
|
-
{ parallelBlockLimit, ...opts },
|
|
386
|
+
{ parallelBlockLimit, skipSubmitProof: proverNodeDisableProofPublish, ...opts },
|
|
380
387
|
);
|
|
381
388
|
}
|
|
382
389
|
|
|
@@ -388,7 +395,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
388
395
|
private validateConfig() {
|
|
389
396
|
if (
|
|
390
397
|
this.config.proverNodeFailedEpochStore &&
|
|
391
|
-
(!this.config.dataDirectory || !this.config.l1ChainId ||
|
|
398
|
+
(!this.config.dataDirectory || !this.config.l1ChainId || this.config.rollupVersion === undefined)
|
|
392
399
|
) {
|
|
393
400
|
this.log.warn(
|
|
394
401
|
`Invalid prover-node config (missing dataDirectory, l1ChainId, or rollupVersion)`,
|
|
@@ -402,7 +409,7 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable
|
|
|
402
409
|
}
|
|
403
410
|
|
|
404
411
|
class EmptyEpochError extends Error {
|
|
405
|
-
constructor(epochNumber:
|
|
412
|
+
constructor(epochNumber: EpochNumber) {
|
|
406
413
|
super(`No blocks found for epoch ${epochNumber}`);
|
|
407
414
|
this.name = 'EmptyEpochError';
|
|
408
415
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RollupContract } from '@aztec/ethereum/contracts';
|
|
2
|
+
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
3
|
+
import type { PublisherManager } from '@aztec/ethereum/publisher-manager';
|
|
2
4
|
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
|
|
3
5
|
import type { TelemetryClient } from '@aztec/telemetry-client';
|
|
4
6
|
|
|
@@ -13,6 +15,15 @@ export class ProverPublisherFactory {
|
|
|
13
15
|
telemetry?: TelemetryClient;
|
|
14
16
|
},
|
|
15
17
|
) {}
|
|
18
|
+
|
|
19
|
+
public async start() {
|
|
20
|
+
await this.deps.publisherManager.loadState();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public stop() {
|
|
24
|
+
this.deps.publisherManager.interrupt();
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
/**
|
|
17
28
|
* Creates a new Prover Publisher instance.
|
|
18
29
|
* @returns A new ProverNodePublisher instance.
|