@aztec/prover-node 5.0.0-private.20260318 → 5.0.0-rc.1
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/README.md +506 -0
- package/dest/actions/download-epoch-proving-job.js +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +4 -3
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +103 -21
- package/dest/bin/run-failed-epoch.js +1 -3
- package/dest/checkpoint-store.d.ts +83 -0
- package/dest/checkpoint-store.d.ts.map +1 -0
- package/dest/checkpoint-store.js +181 -0
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +1 -1
- package/dest/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +22 -8
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/job/checkpoint-prover.d.ts +134 -0
- package/dest/job/checkpoint-prover.d.ts.map +1 -0
- package/dest/job/checkpoint-prover.js +350 -0
- package/dest/job/epoch-session.d.ts +146 -0
- package/dest/job/epoch-session.d.ts.map +1 -0
- package/dest/job/epoch-session.js +709 -0
- package/dest/job/top-tree-job.d.ts +82 -0
- package/dest/job/top-tree-job.d.ts.map +1 -0
- package/dest/job/top-tree-job.js +152 -0
- package/dest/metrics.d.ts +29 -5
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +73 -9
- package/dest/monitors/epoch-monitor.js +6 -2
- package/dest/proof-publishing-service.d.ts +159 -0
- package/dest/proof-publishing-service.d.ts.map +1 -0
- package/dest/proof-publishing-service.js +334 -0
- package/dest/prover-node-publisher.d.ts +18 -11
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +195 -57
- package/dest/prover-node.d.ts +96 -68
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +382 -227
- package/dest/prover-publisher-factory.d.ts +2 -2
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +3 -3
- package/dest/session-manager.d.ts +158 -0
- package/dest/session-manager.d.ts.map +1 -0
- package/dest/session-manager.js +452 -0
- package/dest/test/index.d.ts +7 -6
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +114 -28
- package/src/bin/run-failed-epoch.ts +1 -2
- package/src/checkpoint-store.ts +213 -0
- package/src/config.ts +2 -1
- package/src/factory.ts +18 -10
- package/src/index.ts +1 -0
- package/src/job/checkpoint-prover.ts +465 -0
- package/src/job/epoch-session.ts +424 -0
- package/src/job/top-tree-job.ts +227 -0
- package/src/metrics.ts +88 -12
- package/src/monitors/epoch-monitor.ts +2 -2
- package/src/proof-publishing-service.ts +424 -0
- package/src/prover-node-publisher.ts +220 -67
- package/src/prover-node.ts +439 -249
- package/src/prover-publisher-factory.ts +3 -3
- package/src/session-manager.ts +552 -0
- package/src/test/index.ts +6 -6
- package/dest/job/epoch-proving-job.d.ts +0 -63
- package/dest/job/epoch-proving-job.d.ts.map +0 -1
- package/dest/job/epoch-proving-job.js +0 -762
- package/src/job/epoch-proving-job.ts +0 -465
package/dest/test/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { EpochProverFactory } from '@aztec/prover-client';
|
|
1
2
|
import type { EpochProverManager } from '@aztec/stdlib/interfaces/server';
|
|
2
|
-
import type {
|
|
3
|
-
import type { ProverNodePublisher } from '../prover-node-publisher.js';
|
|
3
|
+
import type { ProofPublishingService } from '../proof-publishing-service.js';
|
|
4
4
|
import { ProverNode } from '../prover-node.js';
|
|
5
|
+
import type { SessionManager } from '../session-manager.js';
|
|
5
6
|
declare abstract class TestProverNodeClass extends ProverNode {
|
|
6
|
-
prover: EpochProverManager;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
prover: EpochProverManager & EpochProverFactory;
|
|
8
|
+
publishingService: ProofPublishingService;
|
|
9
|
+
sessionManager: SessionManager;
|
|
9
10
|
}
|
|
10
11
|
export type TestProverNode = TestProverNodeClass;
|
|
11
12
|
export {};
|
|
12
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDL0QsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUUxRSxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUU1RCx1QkFBZSxtQkFBb0IsU0FBUSxVQUFVO0lBQ3BDLE1BQU0sRUFBRSxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztJQUNoRCxpQkFBaUIsRUFBRSxzQkFBc0IsQ0FBQztJQUMxQyxjQUFjLEVBQUUsY0FBYyxDQUFDO0NBQy9DO0FBRUQsTUFBTSxNQUFNLGNBQWMsR0FBRyxtQkFBbUIsQ0FBQyJ9
|
package/dest/test/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,uBAAe,mBAAoB,SAAQ,UAAU;IACpC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAAC;IAChD,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;CAC/C;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/prover-node",
|
|
3
|
-
"version": "5.0.0-
|
|
3
|
+
"version": "5.0.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -56,28 +56,28 @@
|
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aztec/archiver": "5.0.0-
|
|
60
|
-
"@aztec/bb-prover": "5.0.0-
|
|
61
|
-
"@aztec/blob-client": "5.0.0-
|
|
62
|
-
"@aztec/blob-lib": "5.0.0-
|
|
63
|
-
"@aztec/constants": "5.0.0-
|
|
64
|
-
"@aztec/epoch-cache": "5.0.0-
|
|
65
|
-
"@aztec/ethereum": "5.0.0-
|
|
66
|
-
"@aztec/foundation": "5.0.0-
|
|
67
|
-
"@aztec/kv-store": "5.0.0-
|
|
68
|
-
"@aztec/l1-artifacts": "5.0.0-
|
|
69
|
-
"@aztec/native": "5.0.0-
|
|
70
|
-
"@aztec/node-keystore": "5.0.0-
|
|
71
|
-
"@aztec/node-lib": "5.0.0-
|
|
72
|
-
"@aztec/noir-protocol-circuits-types": "5.0.0-
|
|
73
|
-
"@aztec/p2p": "5.0.0-
|
|
74
|
-
"@aztec/protocol-contracts": "5.0.0-
|
|
75
|
-
"@aztec/prover-client": "5.0.0-
|
|
76
|
-
"@aztec/sequencer-client": "5.0.0-
|
|
77
|
-
"@aztec/simulator": "5.0.0-
|
|
78
|
-
"@aztec/stdlib": "5.0.0-
|
|
79
|
-
"@aztec/telemetry-client": "5.0.0-
|
|
80
|
-
"@aztec/world-state": "5.0.0-
|
|
59
|
+
"@aztec/archiver": "5.0.0-rc.1",
|
|
60
|
+
"@aztec/bb-prover": "5.0.0-rc.1",
|
|
61
|
+
"@aztec/blob-client": "5.0.0-rc.1",
|
|
62
|
+
"@aztec/blob-lib": "5.0.0-rc.1",
|
|
63
|
+
"@aztec/constants": "5.0.0-rc.1",
|
|
64
|
+
"@aztec/epoch-cache": "5.0.0-rc.1",
|
|
65
|
+
"@aztec/ethereum": "5.0.0-rc.1",
|
|
66
|
+
"@aztec/foundation": "5.0.0-rc.1",
|
|
67
|
+
"@aztec/kv-store": "5.0.0-rc.1",
|
|
68
|
+
"@aztec/l1-artifacts": "5.0.0-rc.1",
|
|
69
|
+
"@aztec/native": "5.0.0-rc.1",
|
|
70
|
+
"@aztec/node-keystore": "5.0.0-rc.1",
|
|
71
|
+
"@aztec/node-lib": "5.0.0-rc.1",
|
|
72
|
+
"@aztec/noir-protocol-circuits-types": "5.0.0-rc.1",
|
|
73
|
+
"@aztec/p2p": "5.0.0-rc.1",
|
|
74
|
+
"@aztec/protocol-contracts": "5.0.0-rc.1",
|
|
75
|
+
"@aztec/prover-client": "5.0.0-rc.1",
|
|
76
|
+
"@aztec/sequencer-client": "5.0.0-rc.1",
|
|
77
|
+
"@aztec/simulator": "5.0.0-rc.1",
|
|
78
|
+
"@aztec/stdlib": "5.0.0-rc.1",
|
|
79
|
+
"@aztec/telemetry-client": "5.0.0-rc.1",
|
|
80
|
+
"@aztec/world-state": "5.0.0-rc.1",
|
|
81
81
|
"source-map-support": "^0.5.21",
|
|
82
82
|
"tslib": "^2.4.0",
|
|
83
83
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
@@ -30,7 +30,7 @@ export async function downloadEpochProvingJob(
|
|
|
30
30
|
|
|
31
31
|
const dataUrls = makeSnapshotPaths(location);
|
|
32
32
|
log.info(`Downloading state snapshot from ${location} to local data directory`, { metadata, dataUrls });
|
|
33
|
-
await snapshotSync({ dataUrls }, log, { ...config, ...metadata,
|
|
33
|
+
await snapshotSync({ dataUrls }, log, { ...config, ...metadata, fileStore });
|
|
34
34
|
|
|
35
35
|
const dataPath = urlJoin(location, 'data.bin');
|
|
36
36
|
const localPath = config.jobDataDownloadPath;
|
|
@@ -1,63 +1,149 @@
|
|
|
1
|
-
import { createArchiverStore } from '@aztec/archiver';
|
|
1
|
+
import { createArchiverStore, createContractDataSource } from '@aztec/archiver';
|
|
2
2
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
3
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
4
|
import type { Logger } from '@aztec/foundation/log';
|
|
5
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
4
6
|
import { type ProverClientConfig, createProverClient } from '@aztec/prover-client';
|
|
5
7
|
import { ProverBrokerConfig, createAndStartProvingBroker } from '@aztec/prover-client/broker';
|
|
8
|
+
import { getLastSiblingPath } from '@aztec/prover-client/helpers';
|
|
9
|
+
import { ChonkCache } from '@aztec/prover-client/orchestrator';
|
|
6
10
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
11
|
+
import type { L2Block } from '@aztec/stdlib/block';
|
|
12
|
+
import { getEpochAtSlot, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
13
|
+
import type { ITxProvider } from '@aztec/stdlib/interfaces/server';
|
|
7
14
|
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
15
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
16
|
+
import type { Tx, TxHash } from '@aztec/stdlib/tx';
|
|
17
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
8
18
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
9
19
|
import { createWorldState } from '@aztec/world-state';
|
|
10
20
|
|
|
11
21
|
import { readFileSync } from 'fs';
|
|
12
22
|
|
|
23
|
+
import { CheckpointProver } from '../job/checkpoint-prover.js';
|
|
13
24
|
import { deserializeEpochProvingJobData } from '../job/epoch-proving-job-data.js';
|
|
14
|
-
import {
|
|
25
|
+
import { EpochSession, type SessionSpec } from '../job/epoch-session.js';
|
|
15
26
|
import { ProverNodeJobMetrics } from '../metrics.js';
|
|
16
27
|
|
|
17
28
|
/**
|
|
18
29
|
* Given a local folder where `downloadEpochProvingJob` was called, creates a new archiver and world state
|
|
19
|
-
* using the state snapshots, and creates a new epoch proving
|
|
30
|
+
* using the state snapshots, and creates a new epoch proving session to prove the downloaded proving job.
|
|
20
31
|
* Proving is done with a local proving broker and agents as specified by the config.
|
|
21
32
|
*/
|
|
22
33
|
export async function rerunEpochProvingJob(
|
|
23
34
|
localPath: string,
|
|
24
35
|
log: Logger,
|
|
25
36
|
config: DataStoreConfig & ProverBrokerConfig & ProverClientConfig & Pick<L1ContractsConfig, 'aztecEpochDuration'>,
|
|
37
|
+
genesis?: GenesisData,
|
|
26
38
|
) {
|
|
27
39
|
const jobData = deserializeEpochProvingJobData(readFileSync(localPath));
|
|
28
40
|
log.info(`Loaded proving job data for epoch ${jobData.epochNumber}`);
|
|
29
41
|
|
|
30
42
|
const telemetry = getTelemetryClient();
|
|
31
43
|
const metrics = new ProverNodeJobMetrics(telemetry.getMeter('prover-job'), telemetry.getTracer('prover-job'));
|
|
32
|
-
const worldState = await createWorldState(config);
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
const worldState = await createWorldState(config, genesis);
|
|
45
|
+
const initialBlockHash = await worldState.getInitialHeader().hash();
|
|
46
|
+
const archiver = await createArchiverStore(config, initialBlockHash);
|
|
47
|
+
const publicProcessorFactory = new PublicProcessorFactory(
|
|
48
|
+
createContractDataSource(archiver),
|
|
49
|
+
undefined,
|
|
50
|
+
undefined,
|
|
51
|
+
log.getBindings(),
|
|
52
|
+
);
|
|
39
53
|
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
54
|
+
// Local rerun never publishes — stub the service so submit() always resolves 'published'
|
|
55
|
+
// and withdraw is a no-op.
|
|
56
|
+
const publishingService = {
|
|
57
|
+
submit: () => Promise.resolve('published' as const),
|
|
58
|
+
withdraw: () => {},
|
|
59
|
+
};
|
|
43
60
|
const broker = await createAndStartProvingBroker(config, telemetry);
|
|
44
61
|
const prover = await createProverClient(config, worldState, broker, telemetry);
|
|
62
|
+
const chonkCache = new ChonkCache(log.getBindings());
|
|
63
|
+
|
|
64
|
+
const txProvider = makeReplayingTxProvider(jobData.txs);
|
|
65
|
+
|
|
66
|
+
log.info(`Rerunning epoch proving for epoch ${jobData.epochNumber}`);
|
|
67
|
+
|
|
68
|
+
const provers: CheckpointProver[] = [];
|
|
69
|
+
for (let i = 0; i < jobData.checkpoints.length; i++) {
|
|
70
|
+
const checkpoint = jobData.checkpoints[i];
|
|
71
|
+
const previousBlockHeader =
|
|
72
|
+
i === 0 ? jobData.previousBlockHeader : jobData.checkpoints[i - 1].blocks.at(-1)!.header;
|
|
73
|
+
const l1ToL2Messages = jobData.l1ToL2Messages[checkpoint.number] ?? [];
|
|
74
|
+
const previousArchiveSiblingPath = await getLastSiblingPath(
|
|
75
|
+
MerkleTreeId.ARCHIVE,
|
|
76
|
+
worldState.getSnapshot(BlockNumber(checkpoint.blocks[0].number - 1)),
|
|
77
|
+
);
|
|
78
|
+
const attestations = checkpoint.number === jobData.checkpoints.at(-1)!.number ? jobData.attestations : [];
|
|
79
|
+
provers.push(
|
|
80
|
+
new CheckpointProver(
|
|
81
|
+
{
|
|
82
|
+
checkpoint,
|
|
83
|
+
epochNumber: jobData.epochNumber,
|
|
84
|
+
attestations,
|
|
85
|
+
previousBlockHeader,
|
|
86
|
+
l1ToL2Messages,
|
|
87
|
+
previousArchiveSiblingPath,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
proverFactory: prover,
|
|
91
|
+
chonkCache,
|
|
92
|
+
publicProcessorFactory,
|
|
93
|
+
dbProvider: worldState,
|
|
94
|
+
txProvider,
|
|
95
|
+
dateProvider: new DateProvider(),
|
|
96
|
+
proverId: prover.getProverId(),
|
|
97
|
+
metrics,
|
|
98
|
+
txGatheringTimeoutMs: 120_000,
|
|
99
|
+
deadline: undefined,
|
|
100
|
+
log,
|
|
101
|
+
},
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
45
105
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
106
|
+
const l1Constants = { epochDuration: config.aztecEpochDuration };
|
|
107
|
+
const [fromSlot, toSlot] = getSlotRangeForEpoch(jobData.epochNumber, l1Constants);
|
|
108
|
+
const spec: SessionSpec = { kind: 'full', epochNumber: jobData.epochNumber, fromSlot, toSlot };
|
|
109
|
+
|
|
110
|
+
const session = new EpochSession(spec, provers, {
|
|
111
|
+
proverFactory: prover,
|
|
112
|
+
proverId: prover.getProverId(),
|
|
113
|
+
publishingService,
|
|
53
114
|
metrics,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
115
|
+
dateProvider: new DateProvider(),
|
|
116
|
+
deadline: undefined,
|
|
117
|
+
config: {},
|
|
118
|
+
bindings: log.getBindings(),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const finalState = await session.start();
|
|
122
|
+
log.info(`Completed proving for epoch ${jobData.epochNumber} with status ${finalState}`, {
|
|
123
|
+
derivedEpoch: getEpochAtSlot(provers[0].slotNumber, l1Constants),
|
|
124
|
+
});
|
|
125
|
+
return finalState;
|
|
126
|
+
}
|
|
58
127
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
128
|
+
/** Build a synthetic ITxProvider that returns the supplied txs map by lookup. */
|
|
129
|
+
function makeReplayingTxProvider(txs: Map<string, Tx>): ITxProvider {
|
|
130
|
+
const lookup = (hashes: TxHash[]) => {
|
|
131
|
+
const found: Tx[] = [];
|
|
132
|
+
const missing: TxHash[] = [];
|
|
133
|
+
for (const hash of hashes) {
|
|
134
|
+
const tx = txs.get(hash.toString());
|
|
135
|
+
if (tx) {
|
|
136
|
+
found.push(tx);
|
|
137
|
+
} else {
|
|
138
|
+
missing.push(hash);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { txs: found, missingTxs: missing };
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
getAvailableTxs: hashes => Promise.resolve(lookup(hashes)),
|
|
145
|
+
hasTxs: hashes => Promise.resolve(hashes.map(h => txs.has(h.toString()))),
|
|
146
|
+
getTxsForBlockProposal: () => Promise.resolve({ txs: [], missingTxs: [] }),
|
|
147
|
+
getTxsForBlock: (block: L2Block) => Promise.resolve(lookup(block.body.txEffects.map(e => e.txHash))),
|
|
148
|
+
};
|
|
63
149
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
|
|
3
|
-
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
4
3
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
4
|
import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
|
|
6
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -50,7 +49,7 @@ async function rerunFailedEpoch(provingJobUrl: string, baseLocalDir: string) {
|
|
|
50
49
|
logger.info(`Rerunning proving job from ${jobPath} with state from ${dataDir}`, metadata);
|
|
51
50
|
const result = await rerunEpochProvingJob(jobPath, logger, {
|
|
52
51
|
...config,
|
|
53
|
-
|
|
52
|
+
rollupAddress: metadata.rollupAddress,
|
|
54
53
|
rollupVersion: metadata.rollupVersion,
|
|
55
54
|
});
|
|
56
55
|
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { RunningPromise } from '@aztec/foundation/promise';
|
|
4
|
+
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
5
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
6
|
+
import { type L1RollupConstants, getEpochAtSlot, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
7
|
+
|
|
8
|
+
import { CheckpointProver, type CheckpointProverArgs, type CheckpointProverDeps } from './job/checkpoint-prover.js';
|
|
9
|
+
|
|
10
|
+
/** Register-time data needed to construct a `CheckpointProver` (everything except the checkpoint + epoch). */
|
|
11
|
+
export type RegisterCheckpointData = Omit<CheckpointProverArgs, 'checkpoint' | 'epochNumber'>;
|
|
12
|
+
|
|
13
|
+
/** Factory used by the store to construct new provers. Tests can inject a stub. */
|
|
14
|
+
export type CheckpointProverFactory = (args: CheckpointProverArgs, deps: CheckpointProverDeps) => CheckpointProver;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Prover-node-wide registry of `CheckpointProver` instances, content-addressed by
|
|
18
|
+
* `(checkpoint number, slot, checkpoint archive root)`.
|
|
19
|
+
*
|
|
20
|
+
* The store survives every epoch / session boundary. A prover lives from its first
|
|
21
|
+
* `addOrUpdate` call until either:
|
|
22
|
+
* - it has been pruned and the L2 chain has moved past its slot (no re-add possible), or
|
|
23
|
+
* - its epoch's proof-submission window has closed (`reapExpired`), so the proof could no
|
|
24
|
+
* longer be accepted on L1 even if produced.
|
|
25
|
+
*
|
|
26
|
+
* A re-add of a checkpoint that matches an existing prover's content key reuses the
|
|
27
|
+
* existing prover (and flips it back to canonical); the in-flight sub-tree work never
|
|
28
|
+
* stops, so a prune-then-re-add of the same content avoids re-proving entirely.
|
|
29
|
+
*/
|
|
30
|
+
export class CheckpointStore {
|
|
31
|
+
private readonly provers = new Map<string, CheckpointProver>();
|
|
32
|
+
private readonly slotWatcher: RunningPromise;
|
|
33
|
+
private readonly log: Logger;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
private readonly l2BlockSource: Pick<L2BlockSource, 'getSyncedL2SlotNumber' | 'getL1Constants'>,
|
|
37
|
+
private readonly proverDeps: Omit<CheckpointProverDeps, 'log'>,
|
|
38
|
+
private readonly options: { slotWatcherPollIntervalMs: number },
|
|
39
|
+
bindings?: LoggerBindings,
|
|
40
|
+
private readonly proverFactoryFn: CheckpointProverFactory = (args, deps) => new CheckpointProver(args, deps),
|
|
41
|
+
) {
|
|
42
|
+
this.log = createLogger('prover-node:checkpoint-store', bindings);
|
|
43
|
+
this.slotWatcher = new RunningPromise(
|
|
44
|
+
() => this.reapPrunedPastSlot(),
|
|
45
|
+
this.log,
|
|
46
|
+
this.options.slotWatcherPollIntervalMs,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public start(): Promise<void> {
|
|
51
|
+
this.slotWatcher.start();
|
|
52
|
+
return Promise.resolve();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async stop(): Promise<void> {
|
|
56
|
+
await this.slotWatcher.stop();
|
|
57
|
+
// Cancel every live prover; await teardown.
|
|
58
|
+
const provers = Array.from(this.provers.values());
|
|
59
|
+
this.provers.clear();
|
|
60
|
+
for (const prover of provers) {
|
|
61
|
+
prover.cancel();
|
|
62
|
+
}
|
|
63
|
+
await Promise.allSettled(provers.map(p => p.whenDone()));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Registers a checkpoint with the store. If a prover already exists for the
|
|
68
|
+
* `(number, slot, archive root)` content key, it is reused and marked canonical;
|
|
69
|
+
* otherwise a new prover is constructed.
|
|
70
|
+
*/
|
|
71
|
+
public async addOrUpdate(checkpoint: Checkpoint, data: RegisterCheckpointData): Promise<CheckpointProver> {
|
|
72
|
+
const l1Constants = await this.l2BlockSource.getL1Constants();
|
|
73
|
+
const epochNumber = getEpochAtSlot(checkpoint.header.slotNumber, l1Constants);
|
|
74
|
+
const id = CheckpointProver.idFor(checkpoint);
|
|
75
|
+
|
|
76
|
+
const existing = this.provers.get(id);
|
|
77
|
+
if (existing) {
|
|
78
|
+
existing.markCanonical();
|
|
79
|
+
return existing;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// At most one canonical checkpoint per slot. A different canonical checkpoint at the
|
|
83
|
+
// same slot means the caller forgot to prune the old chain before adding the replacement
|
|
84
|
+
// — surface it rather than silently creating a parallel canonical chain.
|
|
85
|
+
for (const prover of this.provers.values()) {
|
|
86
|
+
if (prover.slotNumber === checkpoint.header.slotNumber && !prover.isPruned()) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Cannot add checkpoint ${checkpoint.number} (archive ${checkpoint.archive.root}) at slot ${checkpoint.header.slotNumber}: ` +
|
|
89
|
+
`a different canonical checkpoint already occupies this slot. Prune it first.`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const prover = this.proverFactoryFn({ ...data, checkpoint, epochNumber }, { ...this.proverDeps, log: this.log });
|
|
95
|
+
this.provers.set(id, prover);
|
|
96
|
+
return prover;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Marks every canonical prover whose checkpoint number is strictly greater than
|
|
101
|
+
* `prunedNumber` as pruned. Sub-tree work keeps running so a re-add of the same
|
|
102
|
+
* content can pick it up. Returns the affected provers.
|
|
103
|
+
*/
|
|
104
|
+
public markPrunedAfter(prunedNumber: CheckpointNumber): CheckpointProver[] {
|
|
105
|
+
const affected: CheckpointProver[] = [];
|
|
106
|
+
for (const prover of this.provers.values()) {
|
|
107
|
+
if (prover.checkpoint.number > prunedNumber && !prover.isPruned()) {
|
|
108
|
+
prover.markPruned();
|
|
109
|
+
affected.push(prover);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return affected;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Drops canonical (non-pruned) provers whose epoch is at or below the supplied expired
|
|
117
|
+
* epoch. Once an epoch's proof-submission window has closed, its proof can no longer be
|
|
118
|
+
* accepted on L1, so the prover is no longer needed.
|
|
119
|
+
*/
|
|
120
|
+
public reapExpired(expiredEpoch: EpochNumber): void {
|
|
121
|
+
const reaped: { id: string; checkpointNumber: CheckpointNumber; epochNumber: EpochNumber }[] = [];
|
|
122
|
+
for (const [id, prover] of Array.from(this.provers.entries())) {
|
|
123
|
+
if (prover.isPruned()) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (prover.epochNumber <= expiredEpoch) {
|
|
127
|
+
reaped.push({ id, checkpointNumber: prover.checkpoint.number, epochNumber: prover.epochNumber });
|
|
128
|
+
prover.cancel({ routine: true });
|
|
129
|
+
void prover.whenDone();
|
|
130
|
+
this.provers.delete(id);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (reaped.length > 0) {
|
|
134
|
+
this.log.info(`Reaped ${reaped.length} expired CheckpointProver(s) for expiredEpoch ${expiredEpoch}`, {
|
|
135
|
+
expiredEpoch,
|
|
136
|
+
reapedCount: reaped.length,
|
|
137
|
+
reaped,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Returns the prover with the supplied id, or undefined. */
|
|
143
|
+
public get(id: string): CheckpointProver | undefined {
|
|
144
|
+
return this.provers.get(id);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Returns the prover for the supplied checkpoint (by its content-addressed id), or undefined. */
|
|
148
|
+
public getByCheckpoint(checkpoint: Checkpoint): CheckpointProver | undefined {
|
|
149
|
+
return this.provers.get(CheckpointProver.idFor(checkpoint));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Every prover currently in the store (canonical and pruned), in insertion order. */
|
|
153
|
+
public listAll(): CheckpointProver[] {
|
|
154
|
+
return Array.from(this.provers.values());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Canonical (non-pruned) provers in the store, sorted by checkpoint number. */
|
|
158
|
+
public listCanonical(): CheckpointProver[] {
|
|
159
|
+
return Array.from(this.provers.values())
|
|
160
|
+
.filter(p => !p.isPruned())
|
|
161
|
+
.sort((a, b) => a.checkpoint.number - b.checkpoint.number);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Canonical provers whose slot is in the supplied epoch's slot range, sorted by
|
|
166
|
+
* checkpoint number.
|
|
167
|
+
*/
|
|
168
|
+
public async listCanonicalForEpoch(epoch: EpochNumber): Promise<CheckpointProver[]> {
|
|
169
|
+
const l1Constants = await this.l2BlockSource.getL1Constants();
|
|
170
|
+
const [fromSlot, toSlot] = getSlotRangeForEpoch(epoch, l1Constants);
|
|
171
|
+
return this.listCanonicalInSlotRange(fromSlot, toSlot);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Canonical provers whose slot falls within `[fromSlot, toSlot]`, sorted by checkpoint number. */
|
|
175
|
+
public listCanonicalInSlotRange(fromSlot: SlotNumber, toSlot: SlotNumber): CheckpointProver[] {
|
|
176
|
+
return this.listCanonical().filter(p => p.slotNumber >= fromSlot && p.slotNumber <= toSlot);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* SlotWatcher tick: reap pruned provers whose slot has passed the chain's synced
|
|
181
|
+
* slot. Once the chain has moved past, no re-add can revive the prover and its
|
|
182
|
+
* content key is unique enough that an actual re-add would create a new entry.
|
|
183
|
+
*
|
|
184
|
+
* Protected so unit tests can drive a single tick without spinning up the
|
|
185
|
+
* `RunningPromise` and waiting on its interval.
|
|
186
|
+
*/
|
|
187
|
+
protected async reapPrunedPastSlot(): Promise<void> {
|
|
188
|
+
let syncedSlot: SlotNumber | undefined;
|
|
189
|
+
try {
|
|
190
|
+
syncedSlot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
191
|
+
} catch (err) {
|
|
192
|
+
this.log.debug(`SlotWatcher could not read synced slot`, { error: `${err}` });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (syncedSlot === undefined) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
for (const [id, prover] of Array.from(this.provers.entries())) {
|
|
199
|
+
if (prover.isPruned() && prover.slotNumber < syncedSlot) {
|
|
200
|
+
this.log.info(`Reaping pruned CheckpointProver ${id}: slot ${prover.slotNumber} < synced ${syncedSlot}`, {
|
|
201
|
+
checkpointNumber: prover.checkpoint.number,
|
|
202
|
+
slotNumber: prover.slotNumber,
|
|
203
|
+
});
|
|
204
|
+
prover.cancel();
|
|
205
|
+
void prover.whenDone();
|
|
206
|
+
this.provers.delete(id);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Sub-set of `L1RollupConstants` actually consumed by the store's slot helpers. */
|
|
213
|
+
export type CheckpointStoreL1Constants = Pick<L1RollupConstants, 'epochDuration'>;
|
package/src/config.ts
CHANGED
|
@@ -68,7 +68,8 @@ export const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProver
|
|
|
68
68
|
defaultValue: undefined,
|
|
69
69
|
},
|
|
70
70
|
proverNodeEpochProvingDelayMs: {
|
|
71
|
-
description:
|
|
71
|
+
description:
|
|
72
|
+
'Optional delay in milliseconds to wait for late-arriving events (e.g. reorgs) to settle before starting top-tree proving for an epoch',
|
|
72
73
|
defaultValue: undefined,
|
|
73
74
|
},
|
|
74
75
|
txGatheringIntervalMs: {
|
package/src/factory.ts
CHANGED
|
@@ -31,7 +31,6 @@ import { L1Metrics, type TelemetryClient, getTelemetryClient } from '@aztec/tele
|
|
|
31
31
|
import { createPublicClient } from 'viem';
|
|
32
32
|
|
|
33
33
|
import type { SpecificProverNodeConfig } from './config.js';
|
|
34
|
-
import { EpochMonitor } from './monitors/epoch-monitor.js';
|
|
35
34
|
import { ProverNode } from './prover-node.js';
|
|
36
35
|
import { ProverPublisherFactory } from './prover-publisher-factory.js';
|
|
37
36
|
|
|
@@ -100,7 +99,7 @@ export async function createProverNode(
|
|
|
100
99
|
pollingInterval: config.viemPollingIntervalMS,
|
|
101
100
|
});
|
|
102
101
|
|
|
103
|
-
const rollupContract = new RollupContract(publicClient, config.
|
|
102
|
+
const rollupContract = new RollupContract(publicClient, config.rollupAddress.toString());
|
|
104
103
|
|
|
105
104
|
const l1TxUtils = deps.l1TxUtils
|
|
106
105
|
? [deps.l1TxUtils]
|
|
@@ -119,11 +118,27 @@ export async function createProverNode(
|
|
|
119
118
|
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
120
119
|
);
|
|
121
120
|
|
|
121
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
122
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
123
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
124
|
+
if (fundingSigner) {
|
|
125
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
126
|
+
publicClient,
|
|
127
|
+
[fundingSigner],
|
|
128
|
+
{ ...config, scope: 'prover' },
|
|
129
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
130
|
+
);
|
|
131
|
+
funderL1TxUtils = funder;
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
const publisherFactory =
|
|
123
135
|
deps.publisherFactory ??
|
|
124
136
|
new ProverPublisherFactory(config, {
|
|
125
137
|
rollupContract,
|
|
126
|
-
publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config),
|
|
138
|
+
publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config), {
|
|
139
|
+
bindings: log.getBindings(),
|
|
140
|
+
funder: funderL1TxUtils,
|
|
141
|
+
}),
|
|
127
142
|
telemetry,
|
|
128
143
|
});
|
|
129
144
|
|
|
@@ -145,12 +160,6 @@ export async function createProverNode(
|
|
|
145
160
|
),
|
|
146
161
|
};
|
|
147
162
|
|
|
148
|
-
const epochMonitor = await EpochMonitor.create(
|
|
149
|
-
archiver,
|
|
150
|
-
{ pollingIntervalMs: config.proverNodePollingIntervalMs, provingDelayMs: config.proverNodeEpochProvingDelayMs },
|
|
151
|
-
telemetry,
|
|
152
|
-
);
|
|
153
|
-
|
|
154
163
|
const l1Metrics = new L1Metrics(
|
|
155
164
|
telemetry.getMeter('ProverNodeL1Metrics'),
|
|
156
165
|
publicClient,
|
|
@@ -168,7 +177,6 @@ export async function createProverNode(
|
|
|
168
177
|
archiver,
|
|
169
178
|
worldStateSynchronizer,
|
|
170
179
|
p2pClient,
|
|
171
|
-
epochMonitor,
|
|
172
180
|
rollupContract,
|
|
173
181
|
l1Metrics,
|
|
174
182
|
proverNodeConfig,
|
package/src/index.ts
CHANGED