@aztec/prover-node 0.0.1-commit.b9865e97 → 0.0.1-commit.be03c316

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.
Files changed (65) hide show
  1. package/README.md +518 -0
  2. package/dest/actions/rerun-epoch-proving-job.d.ts +3 -3
  3. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  4. package/dest/actions/rerun-epoch-proving-job.js +102 -19
  5. package/dest/checkpoint-store.d.ts +87 -0
  6. package/dest/checkpoint-store.d.ts.map +1 -0
  7. package/dest/checkpoint-store.js +186 -0
  8. package/dest/config.d.ts +3 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +8 -1
  11. package/dest/factory.d.ts +4 -1
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +2 -6
  14. package/dest/index.d.ts +2 -1
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +1 -0
  17. package/dest/job/checkpoint-prover.d.ts +134 -0
  18. package/dest/job/checkpoint-prover.d.ts.map +1 -0
  19. package/dest/job/checkpoint-prover.js +353 -0
  20. package/dest/job/epoch-session.d.ts +146 -0
  21. package/dest/job/epoch-session.d.ts.map +1 -0
  22. package/dest/job/epoch-session.js +720 -0
  23. package/dest/job/top-tree-job.d.ts +82 -0
  24. package/dest/job/top-tree-job.d.ts.map +1 -0
  25. package/dest/job/top-tree-job.js +152 -0
  26. package/dest/metrics.d.ts +25 -8
  27. package/dest/metrics.d.ts.map +1 -1
  28. package/dest/metrics.js +64 -14
  29. package/dest/proof-publishing-service.d.ts +161 -0
  30. package/dest/proof-publishing-service.d.ts.map +1 -0
  31. package/dest/proof-publishing-service.js +335 -0
  32. package/dest/prover-node-publisher.d.ts +10 -19
  33. package/dest/prover-node-publisher.d.ts.map +1 -1
  34. package/dest/prover-node-publisher.js +43 -99
  35. package/dest/prover-node.d.ts +107 -67
  36. package/dest/prover-node.d.ts.map +1 -1
  37. package/dest/prover-node.js +476 -261
  38. package/dest/prover-publisher-factory.d.ts +3 -1
  39. package/dest/prover-publisher-factory.d.ts.map +1 -1
  40. package/dest/prover-publisher-factory.js +1 -0
  41. package/dest/session-manager.d.ts +158 -0
  42. package/dest/session-manager.d.ts.map +1 -0
  43. package/dest/session-manager.js +452 -0
  44. package/dest/test/index.d.ts +7 -6
  45. package/dest/test/index.d.ts.map +1 -1
  46. package/package.json +24 -23
  47. package/src/actions/rerun-epoch-proving-job.ts +104 -27
  48. package/src/checkpoint-store.ts +218 -0
  49. package/src/config.ts +12 -1
  50. package/src/factory.ts +5 -8
  51. package/src/index.ts +1 -0
  52. package/src/job/checkpoint-prover.ts +468 -0
  53. package/src/job/epoch-session.ts +436 -0
  54. package/src/job/top-tree-job.ts +227 -0
  55. package/src/metrics.ts +65 -23
  56. package/src/proof-publishing-service.ts +427 -0
  57. package/src/prover-node-publisher.ts +58 -123
  58. package/src/prover-node.ts +549 -283
  59. package/src/prover-publisher-factory.ts +3 -0
  60. package/src/session-manager.ts +552 -0
  61. package/src/test/index.ts +6 -6
  62. package/dest/job/epoch-proving-job.d.ts +0 -67
  63. package/dest/job/epoch-proving-job.d.ts.map +0 -1
  64. package/dest/job/epoch-proving-job.js +0 -912
  65. package/src/job/epoch-proving-job.ts +0 -531
@@ -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 { EpochProvingJob } from '../job/epoch-proving-job.js';
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
- publisher: ProverNodePublisher;
8
- abstract tryUploadEpochFailure(job: EpochProvingJob): Promise<string | undefined>;
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFMUUsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDbkUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN2RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFL0MsdUJBQWUsbUJBQW9CLFNBQVEsVUFBVTtJQUNwQyxNQUFNLEVBQUUsa0JBQWtCLENBQUM7SUFDM0IsU0FBUyxFQUFFLG1CQUFtQixDQUFDO0lBRTlDLFNBQXlCLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxlQUFlLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsQ0FBQztDQUNuRztBQUVELE1BQU0sTUFBTSxjQUFjLEdBQUcsbUJBQW1CLENBQUMifQ==
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDL0QsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUUxRSxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUU1RCx1QkFBZSxtQkFBb0IsU0FBUSxVQUFVO0lBQ3BDLE1BQU0sRUFBRSxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztJQUNoRCxpQkFBaUIsRUFBRSxzQkFBc0IsQ0FBQztJQUMxQyxjQUFjLEVBQUUsY0FBYyxDQUFDO0NBQy9DO0FBRUQsTUFBTSxNQUFNLGNBQWMsR0FBRyxtQkFBbUIsQ0FBQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,uBAAe,mBAAoB,SAAQ,UAAU;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,EAAE,mBAAmB,CAAC;IAE9C,SAAyB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACnG;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC"}
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": "0.0.1-commit.b9865e97",
3
+ "version": "0.0.1-commit.be03c316",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -56,28 +56,29 @@
56
56
  ]
57
57
  },
58
58
  "dependencies": {
59
- "@aztec/archiver": "0.0.1-commit.b9865e97",
60
- "@aztec/bb-prover": "0.0.1-commit.b9865e97",
61
- "@aztec/blob-client": "0.0.1-commit.b9865e97",
62
- "@aztec/blob-lib": "0.0.1-commit.b9865e97",
63
- "@aztec/constants": "0.0.1-commit.b9865e97",
64
- "@aztec/epoch-cache": "0.0.1-commit.b9865e97",
65
- "@aztec/ethereum": "0.0.1-commit.b9865e97",
66
- "@aztec/foundation": "0.0.1-commit.b9865e97",
67
- "@aztec/kv-store": "0.0.1-commit.b9865e97",
68
- "@aztec/l1-artifacts": "0.0.1-commit.b9865e97",
69
- "@aztec/native": "0.0.1-commit.b9865e97",
70
- "@aztec/node-keystore": "0.0.1-commit.b9865e97",
71
- "@aztec/node-lib": "0.0.1-commit.b9865e97",
72
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.b9865e97",
73
- "@aztec/p2p": "0.0.1-commit.b9865e97",
74
- "@aztec/protocol-contracts": "0.0.1-commit.b9865e97",
75
- "@aztec/prover-client": "0.0.1-commit.b9865e97",
76
- "@aztec/sequencer-client": "0.0.1-commit.b9865e97",
77
- "@aztec/simulator": "0.0.1-commit.b9865e97",
78
- "@aztec/stdlib": "0.0.1-commit.b9865e97",
79
- "@aztec/telemetry-client": "0.0.1-commit.b9865e97",
80
- "@aztec/world-state": "0.0.1-commit.b9865e97",
59
+ "@aztec/archiver": "0.0.1-commit.be03c316",
60
+ "@aztec/bb-prover": "0.0.1-commit.be03c316",
61
+ "@aztec/bb.js": "0.0.1-commit.be03c316",
62
+ "@aztec/blob-client": "0.0.1-commit.be03c316",
63
+ "@aztec/blob-lib": "0.0.1-commit.be03c316",
64
+ "@aztec/constants": "0.0.1-commit.be03c316",
65
+ "@aztec/epoch-cache": "0.0.1-commit.be03c316",
66
+ "@aztec/ethereum": "0.0.1-commit.be03c316",
67
+ "@aztec/foundation": "0.0.1-commit.be03c316",
68
+ "@aztec/kv-store": "0.0.1-commit.be03c316",
69
+ "@aztec/l1-artifacts": "0.0.1-commit.be03c316",
70
+ "@aztec/native": "0.0.1-commit.be03c316",
71
+ "@aztec/node-keystore": "0.0.1-commit.be03c316",
72
+ "@aztec/node-lib": "0.0.1-commit.be03c316",
73
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.be03c316",
74
+ "@aztec/p2p": "0.0.1-commit.be03c316",
75
+ "@aztec/protocol-contracts": "0.0.1-commit.be03c316",
76
+ "@aztec/prover-client": "0.0.1-commit.be03c316",
77
+ "@aztec/sequencer-client": "0.0.1-commit.be03c316",
78
+ "@aztec/simulator": "0.0.1-commit.be03c316",
79
+ "@aztec/stdlib": "0.0.1-commit.be03c316",
80
+ "@aztec/telemetry-client": "0.0.1-commit.be03c316",
81
+ "@aztec/world-state": "0.0.1-commit.be03c316",
81
82
  "source-map-support": "^0.5.21",
82
83
  "tslib": "^2.4.0",
83
84
  "viem": "npm:@aztec/viem@2.38.2"
@@ -1,23 +1,33 @@
1
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';
6
- import { PublicProcessorFactory } from '@aztec/simulator/server';
8
+ import { getLastSiblingPath } from '@aztec/prover-client/helpers';
9
+ import { ChonkCache } from '@aztec/prover-client/orchestrator';
10
+ import { AvmSimulatorPool, 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';
8
17
  import type { GenesisData } from '@aztec/stdlib/world-state';
9
18
  import { getTelemetryClient } from '@aztec/telemetry-client';
10
19
  import { createWorldState } from '@aztec/world-state';
11
20
 
12
21
  import { readFileSync } from 'fs';
13
22
 
23
+ import { CheckpointProver } from '../job/checkpoint-prover.js';
14
24
  import { deserializeEpochProvingJobData } from '../job/epoch-proving-job-data.js';
15
- import { EpochProvingJob } from '../job/epoch-proving-job.js';
25
+ import { EpochSession, type SessionSpec } from '../job/epoch-session.js';
16
26
  import { ProverNodeJobMetrics } from '../metrics.js';
17
27
 
18
28
  /**
19
29
  * Given a local folder where `downloadEpochProvingJob` was called, creates a new archiver and world state
20
- * using the state snapshots, and creates a new epoch proving job to prove the downloaded proving job.
30
+ * using the state snapshots, and creates a new epoch proving session to prove the downloaded proving job.
21
31
  * Proving is done with a local proving broker and agents as specified by the config.
22
32
  */
23
33
  export async function rerunEpochProvingJob(
@@ -34,41 +44,108 @@ export async function rerunEpochProvingJob(
34
44
  await using worldState = await createWorldState(config, genesis);
35
45
  const initialBlockHash = await worldState.getInitialHeader().hash();
36
46
  const archiver = await createArchiverStore(config, initialBlockHash);
47
+ await using avmSimulator = await AvmSimulatorPool.spawn({ wsdbIpcPath: worldState.getIpcPath() });
37
48
  const publicProcessorFactory = new PublicProcessorFactory(
38
49
  createContractDataSource(archiver),
50
+ avmSimulator,
39
51
  undefined,
40
52
  undefined,
41
53
  log.getBindings(),
42
54
  );
43
55
 
44
- const publisher = {
45
- submitEpochProof: () => Promise.resolve(true),
46
- analyzeEpochProofSubmission: () => Promise.resolve(),
56
+ // Local rerun never publishes — stub the service so submit() always resolves 'published'
57
+ // and withdraw is a no-op.
58
+ const publishingService = {
59
+ submit: () => Promise.resolve('published' as const),
60
+ withdraw: () => {},
47
61
  };
48
- const l2BlockSourceForReorgDetection = undefined;
49
- const deadline = undefined;
50
-
51
- // This starts a local proving broker that does not get exposed as a service. This should be good enough for
52
- // smallish epochs to be proven if we run on a large machine, but as epochs grow larger, we may want to switch
53
- // this out for a live proving broker with multiple agents that we can connect to.
54
62
  const broker = await createAndStartProvingBroker(config, telemetry);
55
63
  const prover = await createProverClient(config, worldState, broker, telemetry);
64
+ const chonkCache = new ChonkCache(log.getBindings());
65
+
66
+ const txProvider = makeReplayingTxProvider(jobData.txs);
67
+
68
+ log.info(`Rerunning epoch proving for epoch ${jobData.epochNumber}`);
56
69
 
57
- const provingJob = new EpochProvingJob(
58
- jobData,
59
- worldState,
60
- prover.createEpochProver(),
61
- publicProcessorFactory,
62
- publisher,
63
- l2BlockSourceForReorgDetection,
70
+ const provers: CheckpointProver[] = [];
71
+ for (let i = 0; i < jobData.checkpoints.length; i++) {
72
+ const checkpoint = jobData.checkpoints[i];
73
+ const previousBlockHeader =
74
+ i === 0 ? jobData.previousBlockHeader : jobData.checkpoints[i - 1].blocks.at(-1)!.header;
75
+ const l1ToL2Messages = jobData.l1ToL2Messages[checkpoint.number] ?? [];
76
+ const previousArchiveSiblingPath = await getLastSiblingPath(
77
+ MerkleTreeId.ARCHIVE,
78
+ worldState.getSnapshot(BlockNumber(checkpoint.blocks[0].number - 1)),
79
+ );
80
+ const attestations = checkpoint.number === jobData.checkpoints.at(-1)!.number ? jobData.attestations : [];
81
+ provers.push(
82
+ new CheckpointProver(
83
+ {
84
+ checkpoint,
85
+ epochNumber: jobData.epochNumber,
86
+ attestations,
87
+ previousBlockHeader,
88
+ l1ToL2Messages,
89
+ previousArchiveSiblingPath,
90
+ },
91
+ {
92
+ proverFactory: prover,
93
+ chonkCache,
94
+ publicProcessorFactory,
95
+ dbProvider: worldState,
96
+ txProvider,
97
+ dateProvider: new DateProvider(),
98
+ proverId: prover.getProverId(),
99
+ metrics,
100
+ txGatheringTimeoutMs: 120_000,
101
+ deadline: undefined,
102
+ log,
103
+ },
104
+ ),
105
+ );
106
+ }
107
+
108
+ const l1Constants = { epochDuration: config.aztecEpochDuration };
109
+ const [fromSlot, toSlot] = getSlotRangeForEpoch(jobData.epochNumber, l1Constants);
110
+ const spec: SessionSpec = { kind: 'full', epochNumber: jobData.epochNumber, fromSlot, toSlot };
111
+
112
+ const session = new EpochSession(spec, provers, {
113
+ proverFactory: prover,
114
+ proverId: prover.getProverId(),
115
+ publishingService,
64
116
  metrics,
65
- deadline,
66
- { skipEpochCheck: true },
67
- log.getBindings(),
68
- );
117
+ dateProvider: new DateProvider(),
118
+ deadline: undefined,
119
+ config: {},
120
+ bindings: log.getBindings(),
121
+ });
122
+
123
+ const finalState = await session.start();
124
+ log.info(`Completed proving for epoch ${jobData.epochNumber} with status ${finalState}`, {
125
+ derivedEpoch: getEpochAtSlot(provers[0].slotNumber, l1Constants),
126
+ });
127
+ return finalState;
128
+ }
69
129
 
70
- log.info(`Rerunning epoch proving job for epoch ${jobData.epochNumber}`);
71
- await provingJob.run();
72
- log.info(`Completed job for epoch ${jobData.epochNumber} with status ${provingJob.getState()}`);
73
- return provingJob.getState();
130
+ /** Build a synthetic ITxProvider that returns the supplied txs map by lookup. */
131
+ function makeReplayingTxProvider(txs: Map<string, Tx>): ITxProvider {
132
+ const lookup = (hashes: TxHash[]) => {
133
+ const found: Tx[] = [];
134
+ const missing: TxHash[] = [];
135
+ for (const hash of hashes) {
136
+ const tx = txs.get(hash.toString());
137
+ if (tx) {
138
+ found.push(tx);
139
+ } else {
140
+ missing.push(hash);
141
+ }
142
+ }
143
+ return { txs: found, missingTxs: missing };
144
+ };
145
+ return {
146
+ getAvailableTxs: hashes => Promise.resolve(lookup(hashes)),
147
+ hasTxs: hashes => Promise.resolve(hashes.map(h => txs.has(h.toString()))),
148
+ getTxsForBlockProposal: () => Promise.resolve({ txs: [], missingTxs: [] }),
149
+ getTxsForBlock: (block: L2Block) => Promise.resolve(lookup(block.body.txEffects.map(e => e.txHash))),
150
+ };
74
151
  }
@@ -0,0 +1,218 @@
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 { 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 that holds a block above the prune target as pruned. A checkpoint is orphaned by a
101
+ * prune to block `targetBlockNumber` iff its last block sits above the target — including a checkpoint whose range
102
+ * straddles the target (partially orphaned), which block-range marking catches without boundary ambiguity. Keying
103
+ * off the surviving block number (rather than a checkpoint number) is correct even when the source has already
104
+ * re-checkpointed past the divergence: the prune event reports the highest surviving block, which by construction
105
+ * survives on the source, whereas the source's current checkpointed tip can sit above the prune target.
106
+ * Sub-tree work keeps running so a re-add of the same content can pick it up. Returns the affected provers.
107
+ */
108
+ public markPrunedAboveBlock(targetBlockNumber: BlockNumber): CheckpointProver[] {
109
+ const affected: CheckpointProver[] = [];
110
+ for (const prover of this.provers.values()) {
111
+ const lastBlockNumber = prover.checkpoint.blocks.at(-1)!.number;
112
+ if (lastBlockNumber > targetBlockNumber && !prover.isPruned()) {
113
+ prover.markPruned();
114
+ affected.push(prover);
115
+ }
116
+ }
117
+ return affected;
118
+ }
119
+
120
+ /**
121
+ * Drops canonical (non-pruned) provers whose epoch is at or below the supplied expired
122
+ * epoch. Once an epoch's proof-submission window has closed, its proof can no longer be
123
+ * accepted on L1, so the prover is no longer needed.
124
+ */
125
+ public reapExpired(expiredEpoch: EpochNumber): void {
126
+ const reaped: { id: string; checkpointNumber: CheckpointNumber; epochNumber: EpochNumber }[] = [];
127
+ for (const [id, prover] of Array.from(this.provers.entries())) {
128
+ if (prover.isPruned()) {
129
+ continue;
130
+ }
131
+ if (prover.epochNumber <= expiredEpoch) {
132
+ reaped.push({ id, checkpointNumber: prover.checkpoint.number, epochNumber: prover.epochNumber });
133
+ prover.cancel({ routine: true });
134
+ void prover.whenDone();
135
+ this.provers.delete(id);
136
+ }
137
+ }
138
+ if (reaped.length > 0) {
139
+ this.log.info(`Reaped ${reaped.length} expired CheckpointProver(s) for expiredEpoch ${expiredEpoch}`, {
140
+ expiredEpoch,
141
+ reapedCount: reaped.length,
142
+ reaped,
143
+ });
144
+ }
145
+ }
146
+
147
+ /** Returns the prover with the supplied id, or undefined. */
148
+ public get(id: string): CheckpointProver | undefined {
149
+ return this.provers.get(id);
150
+ }
151
+
152
+ /** Returns the prover for the supplied checkpoint (by its content-addressed id), or undefined. */
153
+ public getByCheckpoint(checkpoint: Checkpoint): CheckpointProver | undefined {
154
+ return this.provers.get(CheckpointProver.idFor(checkpoint));
155
+ }
156
+
157
+ /** Every prover currently in the store (canonical and pruned), in insertion order. */
158
+ public listAll(): CheckpointProver[] {
159
+ return Array.from(this.provers.values());
160
+ }
161
+
162
+ /** Canonical (non-pruned) provers in the store, sorted by checkpoint number. */
163
+ public listCanonical(): CheckpointProver[] {
164
+ return Array.from(this.provers.values())
165
+ .filter(p => !p.isPruned())
166
+ .sort((a, b) => a.checkpoint.number - b.checkpoint.number);
167
+ }
168
+
169
+ /**
170
+ * Canonical provers whose slot is in the supplied epoch's slot range, sorted by
171
+ * checkpoint number.
172
+ */
173
+ public async listCanonicalForEpoch(epoch: EpochNumber): Promise<CheckpointProver[]> {
174
+ const l1Constants = await this.l2BlockSource.getL1Constants();
175
+ const [fromSlot, toSlot] = getSlotRangeForEpoch(epoch, l1Constants);
176
+ return this.listCanonicalInSlotRange(fromSlot, toSlot);
177
+ }
178
+
179
+ /** Canonical provers whose slot falls within `[fromSlot, toSlot]`, sorted by checkpoint number. */
180
+ public listCanonicalInSlotRange(fromSlot: SlotNumber, toSlot: SlotNumber): CheckpointProver[] {
181
+ return this.listCanonical().filter(p => p.slotNumber >= fromSlot && p.slotNumber <= toSlot);
182
+ }
183
+
184
+ /**
185
+ * SlotWatcher tick: reap pruned provers whose slot has passed the chain's synced
186
+ * slot. Once the chain has moved past, no re-add can revive the prover and its
187
+ * content key is unique enough that an actual re-add would create a new entry.
188
+ *
189
+ * Protected so unit tests can drive a single tick without spinning up the
190
+ * `RunningPromise` and waiting on its interval.
191
+ */
192
+ protected async reapPrunedPastSlot(): Promise<void> {
193
+ let syncedSlot: SlotNumber | undefined;
194
+ try {
195
+ syncedSlot = await this.l2BlockSource.getSyncedL2SlotNumber();
196
+ } catch (err) {
197
+ this.log.debug(`SlotWatcher could not read synced slot`, { error: `${err}` });
198
+ return;
199
+ }
200
+ if (syncedSlot === undefined) {
201
+ return;
202
+ }
203
+ for (const [id, prover] of Array.from(this.provers.entries())) {
204
+ if (prover.isPruned() && prover.slotNumber < syncedSlot) {
205
+ this.log.info(`Reaping pruned CheckpointProver ${id}: slot ${prover.slotNumber} < synced ${syncedSlot}`, {
206
+ checkpointNumber: prover.checkpoint.number,
207
+ slotNumber: prover.slotNumber,
208
+ });
209
+ prover.cancel();
210
+ void prover.whenDone();
211
+ this.provers.delete(id);
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ /** Sub-set of `L1RollupConstants` actually consumed by the store's slot helpers. */
218
+ export type CheckpointStoreL1Constants = Pick<L1RollupConstants, 'epochDuration'>;
package/src/config.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  numberConfigHelper,
7
7
  pickConfigMappings,
8
8
  } from '@aztec/foundation/config';
9
+ import { EthAddress } from '@aztec/foundation/eth-address';
9
10
  import { type KeyStoreConfig, keyStoreConfigMappings } from '@aztec/node-keystore/config';
10
11
  import { ethPrivateKeySchema } from '@aztec/node-keystore/schemas';
11
12
  import type { KeyStore } from '@aztec/node-keystore/types';
@@ -44,6 +45,7 @@ export type SpecificProverNodeConfig = {
44
45
  txGatheringIntervalMs: number;
45
46
  txGatheringBatchSize: number;
46
47
  txGatheringMaxParallelRequestsPerNode: number;
48
+ proofSubmissionTargetAddress?: EthAddress;
47
49
  };
48
50
 
49
51
  export const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProverNodeConfig> = {
@@ -68,7 +70,8 @@ export const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProver
68
70
  defaultValue: undefined,
69
71
  },
70
72
  proverNodeEpochProvingDelayMs: {
71
- description: 'Optional delay in milliseconds to wait before proving a new epoch',
73
+ description:
74
+ 'Optional delay in milliseconds to wait for late-arriving events (e.g. reorgs) to settle before starting top-tree proving for an epoch',
72
75
  defaultValue: undefined,
73
76
  },
74
77
  txGatheringIntervalMs: {
@@ -96,6 +99,14 @@ export const specificProverNodeConfigMappings: ConfigMappingsType<SpecificProver
96
99
  description: 'Whether the prover node skips publishing proofs to L1',
97
100
  ...booleanConfigHelper(false),
98
101
  },
102
+ proofSubmissionTargetAddress: {
103
+ env: 'PROVER_NODE_PROOF_SUBMISSION_TARGET_ADDRESS',
104
+ description:
105
+ 'Optional L1 address the submitEpochRootProof tx is sent to. Must expose the identical submitEpochRootProof ABI ' +
106
+ 'and forward to the rollup. Defaults to the rollup address.',
107
+ parseEnv: (val: string) => EthAddress.fromString(val),
108
+ defaultValue: undefined,
109
+ },
99
110
  };
100
111
 
101
112
  export const proverNodeConfigMappings: ConfigMappingsType<ProverNodeConfig> = {
package/src/factory.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  type ProverTxSenderConfig,
20
20
  getPublisherConfigFromProverConfig,
21
21
  } from '@aztec/sequencer-client';
22
+ import type { AvmSimulator } from '@aztec/simulator/server';
22
23
  import type {
23
24
  ITxProvider,
24
25
  ProverConfig,
@@ -31,7 +32,6 @@ import { L1Metrics, type TelemetryClient, getTelemetryClient } from '@aztec/tele
31
32
  import { createPublicClient } from 'viem';
32
33
 
33
34
  import type { SpecificProverNodeConfig } from './config.js';
34
- import { EpochMonitor } from './monitors/epoch-monitor.js';
35
35
  import { ProverNode } from './prover-node.js';
36
36
  import { ProverPublisherFactory } from './prover-publisher-factory.js';
37
37
 
@@ -48,6 +48,8 @@ export type ProverNodeDeps = {
48
48
  epochCache: EpochCacheInterface;
49
49
  blobClient: BlobClientInterface;
50
50
  keyStoreManager?: KeystoreManager;
51
+ /** AVM execution backend (simulator pool + CDB server) for public simulation. */
52
+ avmSimulator: AvmSimulator;
51
53
  };
52
54
 
53
55
  /** Creates a new prover node subsystem given a config and dependencies */
@@ -136,6 +138,7 @@ export async function createProverNode(
136
138
  deps.publisherFactory ??
137
139
  new ProverPublisherFactory(config, {
138
140
  rollupContract,
141
+ proofSubmissionTarget: config.proofSubmissionTargetAddress,
139
142
  publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config), {
140
143
  bindings: log.getBindings(),
141
144
  funder: funderL1TxUtils,
@@ -161,12 +164,6 @@ export async function createProverNode(
161
164
  ),
162
165
  };
163
166
 
164
- const epochMonitor = await EpochMonitor.create(
165
- archiver,
166
- { pollingIntervalMs: config.proverNodePollingIntervalMs, provingDelayMs: config.proverNodeEpochProvingDelayMs },
167
- telemetry,
168
- );
169
-
170
167
  const l1Metrics = new L1Metrics(
171
168
  telemetry.getMeter('ProverNodeL1Metrics'),
172
169
  publicClient,
@@ -184,9 +181,9 @@ export async function createProverNode(
184
181
  archiver,
185
182
  worldStateSynchronizer,
186
183
  p2pClient,
187
- epochMonitor,
188
184
  rollupContract,
189
185
  l1Metrics,
186
+ deps.avmSimulator,
190
187
  proverNodeConfig,
191
188
  telemetry,
192
189
  delayer,
package/src/index.ts CHANGED
@@ -2,5 +2,6 @@ export * from './actions/index.js';
2
2
  export * from './config.js';
3
3
  export * from './factory.js';
4
4
  export * from './monitors/index.js';
5
+ export * from './proof-publishing-service.js';
5
6
  export * from './prover-node-publisher.js';
6
7
  export * from './prover-node.js';