@aztec/prover-node 0.0.1-commit.a072138 → 0.0.1-commit.a5db02d
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 +511 -0
- package/dest/actions/download-epoch-proving-job.js +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +5 -4
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +103 -23
- package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
- package/dest/bin/run-failed-epoch.js +6 -5
- package/dest/checkpoint-store.d.ts +88 -0
- package/dest/checkpoint-store.d.ts.map +1 -0
- package/dest/checkpoint-store.js +169 -0
- package/dest/config.d.ts +5 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +17 -20
- package/dest/factory.d.ts +19 -13
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +40 -65
- 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 +124 -0
- package/dest/job/checkpoint-prover.d.ts.map +1 -0
- package/dest/job/checkpoint-prover.js +330 -0
- package/dest/job/epoch-session.d.ts +148 -0
- package/dest/job/epoch-session.d.ts.map +1 -0
- package/dest/job/{epoch-proving-job.js → epoch-session.js} +278 -297
- 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 +40 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +101 -4
- package/dest/monitors/epoch-monitor.d.ts +1 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +11 -9
- package/dest/proof-publishing-service.d.ts +161 -0
- package/dest/proof-publishing-service.d.ts.map +1 -0
- package/dest/proof-publishing-service.js +335 -0
- package/dest/prover-node-publisher.d.ts +22 -15
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +198 -61
- package/dest/prover-node.d.ts +118 -70
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +487 -226
- package/dest/prover-publisher-factory.d.ts +4 -4
- 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 +486 -0
- package/dest/test/index.d.ts +7 -6
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +23 -22
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +115 -29
- package/src/actions/upload-epoch-proof-failure.ts +1 -1
- package/src/bin/run-failed-epoch.ts +5 -3
- package/src/checkpoint-store.ts +194 -0
- package/src/config.ts +25 -32
- package/src/factory.ts +68 -111
- package/src/index.ts +1 -0
- package/src/job/checkpoint-prover.ts +442 -0
- package/src/job/epoch-session.ts +438 -0
- package/src/job/top-tree-job.ts +227 -0
- package/src/metrics.ts +123 -10
- package/src/monitors/epoch-monitor.ts +5 -6
- package/src/proof-publishing-service.ts +427 -0
- package/src/prover-node-publisher.ts +232 -78
- package/src/prover-node.ts +562 -256
- package/src/prover-publisher-factory.ts +5 -5
- package/src/session-manager.ts +585 -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/src/job/epoch-proving-job.ts +0 -435
|
@@ -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';
|
|
4
|
-
import
|
|
5
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
5
6
|
import { type ProverClientConfig, createProverClient } from '@aztec/prover-client';
|
|
6
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';
|
|
7
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';
|
|
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
|
}
|
|
@@ -3,11 +3,11 @@ import { tryRmDir } from '@aztec/foundation/fs';
|
|
|
3
3
|
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
4
4
|
import type { Logger } from '@aztec/foundation/log';
|
|
5
5
|
import { isoDate } from '@aztec/foundation/string';
|
|
6
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
7
6
|
import { buildSnapshotMetadata, createBackups } from '@aztec/node-lib/actions';
|
|
8
7
|
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
9
8
|
import { type FileStore, createFileStore } from '@aztec/stdlib/file-store';
|
|
10
9
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
10
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
11
11
|
import { type UploadSnapshotMetadata, getBasePath, uploadSnapshotData } from '@aztec/stdlib/snapshots';
|
|
12
12
|
import { WORLD_STATE_DB_VERSION } from '@aztec/world-state';
|
|
13
13
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import
|
|
2
|
+
import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
|
|
3
3
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
4
|
import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -22,8 +22,10 @@ async function rerunFailedEpoch(provingJobUrl: string, baseLocalDir: string) {
|
|
|
22
22
|
const dataDir = join(localDir, 'state');
|
|
23
23
|
|
|
24
24
|
const env = getProverNodeConfigFromEnv();
|
|
25
|
+
const l1Config = getL1ContractsConfigEnvVars();
|
|
25
26
|
const config = {
|
|
26
|
-
...
|
|
27
|
+
...env,
|
|
28
|
+
...l1Config,
|
|
27
29
|
dataDirectory: dataDir,
|
|
28
30
|
dataStoreMapSizeKb: env.dataStoreMapSizeKb ?? 1024 * 1024,
|
|
29
31
|
proverId: env.proverId ?? EthAddress.random(),
|
|
@@ -47,7 +49,7 @@ async function rerunFailedEpoch(provingJobUrl: string, baseLocalDir: string) {
|
|
|
47
49
|
logger.info(`Rerunning proving job from ${jobPath} with state from ${dataDir}`, metadata);
|
|
48
50
|
const result = await rerunEpochProvingJob(jobPath, logger, {
|
|
49
51
|
...config,
|
|
50
|
-
|
|
52
|
+
rollupAddress: metadata.rollupAddress,
|
|
51
53
|
rollupVersion: metadata.rollupVersion,
|
|
52
54
|
});
|
|
53
55
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
4
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
5
|
+
import { type L1RollupConstants, getEpochAtSlot, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
+
|
|
7
|
+
import { CheckpointProver, type CheckpointProverArgs, type CheckpointProverDeps } from './job/checkpoint-prover.js';
|
|
8
|
+
|
|
9
|
+
/** Register-time data needed to construct a `CheckpointProver` (everything except the checkpoint + epoch). */
|
|
10
|
+
export type RegisterCheckpointData = Omit<CheckpointProverArgs, 'checkpoint' | 'epochNumber'>;
|
|
11
|
+
|
|
12
|
+
/** Factory used by the store to construct new provers. Tests can inject a stub. */
|
|
13
|
+
export type CheckpointProverFactory = (args: CheckpointProverArgs, deps: CheckpointProverDeps) => CheckpointProver;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Prover-node-wide registry of `CheckpointProver` instances, content-addressed by
|
|
17
|
+
* `(checkpoint number, slot, checkpoint archive root)`.
|
|
18
|
+
*
|
|
19
|
+
* The store survives every epoch / session boundary. A prover lives from its first
|
|
20
|
+
* `addOrUpdate` call until either:
|
|
21
|
+
* - its checkpoint is pruned by an L1 reorg (`cancelAndRemoveAboveBlock`), or
|
|
22
|
+
* - its epoch's proof-submission window has closed (`reapExpired`), so the proof could no
|
|
23
|
+
* longer be accepted on L1 even if produced.
|
|
24
|
+
*
|
|
25
|
+
* A prover's sub-tree work forks world-state per block and does not survive a prune of a base
|
|
26
|
+
* block, so there is nothing to preserve across a reorg: a pruned prover is cancelled and
|
|
27
|
+
* dropped, and a re-add (even of identical content) constructs a fresh prover.
|
|
28
|
+
*/
|
|
29
|
+
export class CheckpointStore {
|
|
30
|
+
private readonly provers = new Map<string, CheckpointProver>();
|
|
31
|
+
/**
|
|
32
|
+
* Teardowns of provers already removed from `provers` (by prune or reap), awaited on `stop()`.
|
|
33
|
+
* Keyed by a monotonic id rather than the prover's content id: a prune-then-re-add can leave two
|
|
34
|
+
* teardowns for the same content id in flight at once, which a content-id key would clobber.
|
|
35
|
+
*/
|
|
36
|
+
private readonly pendingTeardowns = new Map<number, Promise<void>>();
|
|
37
|
+
private nextTeardownId = 0;
|
|
38
|
+
private readonly log: Logger;
|
|
39
|
+
|
|
40
|
+
constructor(
|
|
41
|
+
private readonly l2BlockSource: Pick<L2BlockSource, 'getL1Constants'>,
|
|
42
|
+
private readonly proverDeps: Omit<CheckpointProverDeps, 'log'>,
|
|
43
|
+
bindings?: LoggerBindings,
|
|
44
|
+
private readonly proverFactoryFn: CheckpointProverFactory = (args, deps) => new CheckpointProver(args, deps),
|
|
45
|
+
) {
|
|
46
|
+
this.log = createLogger('prover-node:checkpoint-store', bindings);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public start(): Promise<void> {
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async stop(): Promise<void> {
|
|
54
|
+
// Cancel every live prover, then await both their teardown and any still in flight for provers
|
|
55
|
+
// already removed by a prune or reap.
|
|
56
|
+
const provers = Array.from(this.provers.values());
|
|
57
|
+
this.provers.clear();
|
|
58
|
+
for (const prover of provers) {
|
|
59
|
+
prover.cancel();
|
|
60
|
+
}
|
|
61
|
+
await Promise.allSettled([...provers.map(p => p.whenDone()), ...this.pendingTeardowns.values()]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Tracks the teardown of a prover just removed from the store so `stop()` can await it. The entry
|
|
66
|
+
* removes itself once teardown settles, so the map stays bounded by the number in flight.
|
|
67
|
+
*/
|
|
68
|
+
private trackTeardown(prover: CheckpointProver): void {
|
|
69
|
+
const id = this.nextTeardownId++;
|
|
70
|
+
const done = prover.whenDone();
|
|
71
|
+
this.pendingTeardowns.set(id, done);
|
|
72
|
+
void done.finally(() => this.pendingTeardowns.delete(id));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Registers a checkpoint with the store. If a prover already exists for the
|
|
77
|
+
* `(number, slot, archive root)` content key it is reused (an at-least-once re-registration of
|
|
78
|
+
* still-canonical content); otherwise a new prover is constructed.
|
|
79
|
+
*/
|
|
80
|
+
public async addOrUpdate(checkpoint: Checkpoint, data: RegisterCheckpointData): Promise<CheckpointProver> {
|
|
81
|
+
const l1Constants = await this.l2BlockSource.getL1Constants();
|
|
82
|
+
const epochNumber = getEpochAtSlot(checkpoint.header.slotNumber, l1Constants);
|
|
83
|
+
const id = CheckpointProver.idFor(checkpoint);
|
|
84
|
+
|
|
85
|
+
const existing = this.provers.get(id);
|
|
86
|
+
if (existing) {
|
|
87
|
+
return existing;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// At most one canonical checkpoint per slot. A different checkpoint at the same slot means the
|
|
91
|
+
// caller forgot to prune the old chain before adding the replacement — surface it rather than
|
|
92
|
+
// silently creating a parallel canonical chain. A pruned checkpoint has already been removed,
|
|
93
|
+
// so every prover still in the store is canonical.
|
|
94
|
+
for (const prover of this.provers.values()) {
|
|
95
|
+
if (prover.slotNumber === checkpoint.header.slotNumber) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Cannot add checkpoint ${checkpoint.number} (archive ${checkpoint.archive.root}) at slot ${checkpoint.header.slotNumber}: ` +
|
|
98
|
+
`a different checkpoint already occupies this slot. Prune it first.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const prover = this.proverFactoryFn({ ...data, checkpoint, epochNumber }, { ...this.proverDeps, log: this.log });
|
|
104
|
+
this.provers.set(id, prover);
|
|
105
|
+
return prover;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Cancels and removes every prover that holds a block above the prune target. A checkpoint is orphaned by a prune to
|
|
110
|
+
* block `targetBlockNumber` iff its last block sits above the target — including a checkpoint whose range straddles
|
|
111
|
+
* the target (partially orphaned), which block-range marking catches without boundary ambiguity. Keying off the
|
|
112
|
+
* surviving block number (rather than a checkpoint number) is correct even when the source has already
|
|
113
|
+
* re-checkpointed past the divergence: the prune event reports the highest surviving block, which by construction
|
|
114
|
+
* survives on the source, whereas the source's current checkpointed tip can sit above the prune target.
|
|
115
|
+
*
|
|
116
|
+
* The prover's in-flight sub-tree work forks world-state per block and faults once its base block is pruned, so it
|
|
117
|
+
* cannot be reused; it is cancelled (aborting the fork reads) and dropped. A subsequent re-add constructs a fresh
|
|
118
|
+
* prover. Returns the removed provers.
|
|
119
|
+
*/
|
|
120
|
+
public cancelAndRemoveAboveBlock(targetBlockNumber: BlockNumber): CheckpointProver[] {
|
|
121
|
+
const affected: CheckpointProver[] = [];
|
|
122
|
+
for (const [id, prover] of Array.from(this.provers.entries())) {
|
|
123
|
+
const lastBlockNumber = prover.checkpoint.blocks.at(-1)!.number;
|
|
124
|
+
if (lastBlockNumber > targetBlockNumber) {
|
|
125
|
+
prover.cancel();
|
|
126
|
+
this.trackTeardown(prover);
|
|
127
|
+
this.provers.delete(id);
|
|
128
|
+
affected.push(prover);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return affected;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Drops provers whose epoch is at or below the supplied expired epoch. Once an epoch's
|
|
136
|
+
* proof-submission window has closed, its proof can no longer be accepted on L1, so the
|
|
137
|
+
* prover is no longer needed.
|
|
138
|
+
*/
|
|
139
|
+
public reapExpired(expiredEpoch: EpochNumber): void {
|
|
140
|
+
const reaped: { id: string; checkpointNumber: CheckpointNumber; epochNumber: EpochNumber }[] = [];
|
|
141
|
+
for (const [id, prover] of Array.from(this.provers.entries())) {
|
|
142
|
+
if (prover.epochNumber <= expiredEpoch) {
|
|
143
|
+
reaped.push({ id, checkpointNumber: prover.checkpoint.number, epochNumber: prover.epochNumber });
|
|
144
|
+
prover.cancel({ routine: true });
|
|
145
|
+
this.trackTeardown(prover);
|
|
146
|
+
this.provers.delete(id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (reaped.length > 0) {
|
|
150
|
+
this.log.info(`Reaped ${reaped.length} expired CheckpointProver(s) for expiredEpoch ${expiredEpoch}`, {
|
|
151
|
+
expiredEpoch,
|
|
152
|
+
reapedCount: reaped.length,
|
|
153
|
+
reaped,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Returns the prover with the supplied id, or undefined. */
|
|
159
|
+
public get(id: string): CheckpointProver | undefined {
|
|
160
|
+
return this.provers.get(id);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Returns the prover for the supplied checkpoint (by its content-addressed id), or undefined. */
|
|
164
|
+
public getByCheckpoint(checkpoint: Checkpoint): CheckpointProver | undefined {
|
|
165
|
+
return this.provers.get(CheckpointProver.idFor(checkpoint));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Every prover currently in the store, in insertion order. */
|
|
169
|
+
public listAll(): CheckpointProver[] {
|
|
170
|
+
return Array.from(this.provers.values());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Provers in the store, sorted by checkpoint number. */
|
|
174
|
+
public list(): CheckpointProver[] {
|
|
175
|
+
return Array.from(this.provers.values()).sort((a, b) => a.checkpoint.number - b.checkpoint.number);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Provers whose slot is in the supplied epoch's slot range, sorted by checkpoint number.
|
|
180
|
+
*/
|
|
181
|
+
public async listForEpoch(epoch: EpochNumber): Promise<CheckpointProver[]> {
|
|
182
|
+
const l1Constants = await this.l2BlockSource.getL1Constants();
|
|
183
|
+
const [fromSlot, toSlot] = getSlotRangeForEpoch(epoch, l1Constants);
|
|
184
|
+
return this.listInSlotRange(fromSlot, toSlot);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Provers whose slot falls within `[fromSlot, toSlot]`, sorted by checkpoint number. */
|
|
188
|
+
public listInSlotRange(fromSlot: SlotNumber, toSlot: SlotNumber): CheckpointProver[] {
|
|
189
|
+
return this.list().filter(p => p.slotNumber >= fromSlot && p.slotNumber <= toSlot);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Sub-set of `L1RollupConstants` actually consumed by the store's slot helpers. */
|
|
194
|
+
export type CheckpointStoreL1Constants = Pick<L1RollupConstants, 'epochDuration'>;
|
package/src/config.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { type ArchiverConfig, archiverConfigMappings } from '@aztec/archiver/config';
|
|
2
1
|
import type { ACVMConfig, BBConfig } from '@aztec/bb-prover/config';
|
|
3
|
-
import { type GenesisStateConfig, genesisStateConfigMappings } from '@aztec/ethereum/config';
|
|
4
2
|
import {
|
|
5
3
|
type ConfigMappingsType,
|
|
6
4
|
booleanConfigHelper,
|
|
7
5
|
getConfigFromMappings,
|
|
8
6
|
numberConfigHelper,
|
|
7
|
+
pickConfigMappings,
|
|
9
8
|
} from '@aztec/foundation/config';
|
|
10
|
-
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
11
9
|
import { type KeyStoreConfig, keyStoreConfigMappings } from '@aztec/node-keystore/config';
|
|
12
10
|
import { ethPrivateKeySchema } from '@aztec/node-keystore/schemas';
|
|
13
11
|
import type { KeyStore } from '@aztec/node-keystore/types';
|
|
14
12
|
import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib/config';
|
|
15
|
-
import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config';
|
|
16
13
|
import {
|
|
17
14
|
type ProverAgentConfig,
|
|
18
15
|
type ProverBrokerConfig,
|
|
@@ -21,24 +18,20 @@ import {
|
|
|
21
18
|
} from '@aztec/prover-client/broker/config';
|
|
22
19
|
import { type ProverClientUserConfig, bbConfigMappings, proverClientConfigMappings } from '@aztec/prover-client/config';
|
|
23
20
|
import {
|
|
24
|
-
type
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
type ProverPublisherConfig,
|
|
22
|
+
type ProverTxSenderConfig,
|
|
23
|
+
proverPublisherConfigMappings,
|
|
24
|
+
proverTxSenderConfigMappings,
|
|
28
25
|
} from '@aztec/sequencer-client/config';
|
|
29
|
-
import { type
|
|
30
|
-
|
|
31
|
-
export type ProverNodeConfig =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
WorldStateConfig &
|
|
35
|
-
PublisherConfig &
|
|
36
|
-
TxSenderConfig &
|
|
26
|
+
import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store';
|
|
27
|
+
|
|
28
|
+
export type ProverNodeConfig = ProverClientUserConfig &
|
|
29
|
+
ProverPublisherConfig &
|
|
30
|
+
ProverTxSenderConfig &
|
|
37
31
|
DataStoreConfig &
|
|
38
32
|
KeyStoreConfig &
|
|
39
|
-
SharedNodeConfig &
|
|
40
33
|
SpecificProverNodeConfig &
|
|
41
|
-
|
|
34
|
+
Pick<SharedNodeConfig, 'web3SignerUrl'>;
|
|
42
35
|
|
|
43
36
|
export type SpecificProverNodeConfig = {
|
|
44
37
|
proverNodeMaxPendingJobs: number;
|
|
@@ -53,7 +46,7 @@ export type SpecificProverNodeConfig = {
|
|
|
53
46
|
txGatheringMaxParallelRequestsPerNode: number;
|
|
54
47
|
};
|
|
55
48
|
|
|
56
|
-
const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeConfig> = {
|
|
49
|
+
export const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeConfig> = {
|
|
57
50
|
proverNodeMaxPendingJobs: {
|
|
58
51
|
env: 'PROVER_NODE_MAX_PENDING_JOBS',
|
|
59
52
|
description: 'The maximum number of pending jobs for the prover node',
|
|
@@ -67,7 +60,7 @@ const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeCon
|
|
|
67
60
|
proverNodeMaxParallelBlocksPerEpoch: {
|
|
68
61
|
env: 'PROVER_NODE_MAX_PARALLEL_BLOCKS_PER_EPOCH',
|
|
69
62
|
description: 'The Maximum number of blocks to process in parallel while proving an epoch',
|
|
70
|
-
...numberConfigHelper(
|
|
63
|
+
...numberConfigHelper(0),
|
|
71
64
|
},
|
|
72
65
|
proverNodeFailedEpochStore: {
|
|
73
66
|
env: 'PROVER_NODE_FAILED_EPOCH_STORE',
|
|
@@ -75,7 +68,8 @@ const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeCon
|
|
|
75
68
|
defaultValue: undefined,
|
|
76
69
|
},
|
|
77
70
|
proverNodeEpochProvingDelayMs: {
|
|
78
|
-
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',
|
|
79
73
|
defaultValue: undefined,
|
|
80
74
|
},
|
|
81
75
|
txGatheringIntervalMs: {
|
|
@@ -108,15 +102,11 @@ const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeCon
|
|
|
108
102
|
export const proverNodeConfigMappings: ConfigMappingsType<ProverNodeConfig> = {
|
|
109
103
|
...dataConfigMappings,
|
|
110
104
|
...keyStoreConfigMappings,
|
|
111
|
-
...archiverConfigMappings,
|
|
112
105
|
...proverClientConfigMappings,
|
|
113
|
-
...
|
|
114
|
-
...
|
|
115
|
-
...getPublisherConfigMappings('PROVER'),
|
|
116
|
-
...getTxSenderConfigMappings('PROVER'),
|
|
106
|
+
...proverPublisherConfigMappings,
|
|
107
|
+
...proverTxSenderConfigMappings,
|
|
117
108
|
...specificProverNodeConfigMappings,
|
|
118
|
-
...
|
|
119
|
-
...sharedNodeConfigMappings,
|
|
109
|
+
...pickConfigMappings(sharedNodeConfigMappings, ['web3SignerUrl']),
|
|
120
110
|
};
|
|
121
111
|
|
|
122
112
|
export function getProverNodeConfigFromEnv(): ProverNodeConfig {
|
|
@@ -143,7 +133,7 @@ function createKeyStoreFromWeb3Signer(config: ProverNodeConfig): KeyStore | unde
|
|
|
143
133
|
}
|
|
144
134
|
|
|
145
135
|
// Also, we need at least one publisher address.
|
|
146
|
-
const publishers = config.
|
|
136
|
+
const publishers = config.proverPublisherAddresses ?? [];
|
|
147
137
|
|
|
148
138
|
if (publishers.length === 0) {
|
|
149
139
|
return undefined;
|
|
@@ -164,8 +154,8 @@ function createKeyStoreFromWeb3Signer(config: ProverNodeConfig): KeyStore | unde
|
|
|
164
154
|
|
|
165
155
|
function createKeyStoreFromPublisherKeys(config: ProverNodeConfig): KeyStore | undefined {
|
|
166
156
|
// Extract the publisher keys from the provided config.
|
|
167
|
-
const publisherKeys = config.
|
|
168
|
-
? config.
|
|
157
|
+
const publisherKeys = config.proverPublisherPrivateKeys
|
|
158
|
+
? config.proverPublisherPrivateKeys.map((k: { getValue: () => string }) => ethPrivateKeySchema.parse(k.getValue()))
|
|
169
159
|
: [];
|
|
170
160
|
|
|
171
161
|
// There must be at least 1.
|
|
@@ -194,7 +184,10 @@ function createKeyStoreFromPublisherKeys(config: ProverNodeConfig): KeyStore | u
|
|
|
194
184
|
|
|
195
185
|
export function createKeyStoreForProver(config: ProverNodeConfig): KeyStore | undefined {
|
|
196
186
|
if (config.web3SignerUrl !== undefined && config.web3SignerUrl.length > 0) {
|
|
197
|
-
|
|
187
|
+
const keyStore = createKeyStoreFromWeb3Signer(config);
|
|
188
|
+
if (keyStore) {
|
|
189
|
+
return keyStore;
|
|
190
|
+
}
|
|
198
191
|
}
|
|
199
192
|
|
|
200
193
|
return createKeyStoreFromPublisherKeys(config);
|