@aztec/prover-node 0.0.1-commit.42ee6df9b → 0.0.1-commit.431c48d

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 (71) hide show
  1. package/README.md +572 -0
  2. package/dest/actions/download-epoch-proving-job.js +1 -1
  3. package/dest/actions/rerun-epoch-proving-job.d.ts +13 -3
  4. package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
  5. package/dest/actions/rerun-epoch-proving-job.js +146 -22
  6. package/dest/bin/run-failed-epoch.js +1 -3
  7. package/dest/checkpoint-store.d.ts +95 -0
  8. package/dest/checkpoint-store.d.ts.map +1 -0
  9. package/dest/checkpoint-store.js +178 -0
  10. package/dest/config.d.ts +3 -1
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +8 -1
  13. package/dest/factory.d.ts +1 -1
  14. package/dest/factory.d.ts.map +1 -1
  15. package/dest/factory.js +3 -7
  16. package/dest/index.d.ts +2 -1
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +1 -0
  19. package/dest/job/checkpoint-prover.d.ts +165 -0
  20. package/dest/job/checkpoint-prover.d.ts.map +1 -0
  21. package/dest/job/checkpoint-prover.js +405 -0
  22. package/dest/job/epoch-session.d.ts +160 -0
  23. package/dest/job/epoch-session.d.ts.map +1 -0
  24. package/dest/job/epoch-session.js +744 -0
  25. package/dest/job/top-tree-job.d.ts +82 -0
  26. package/dest/job/top-tree-job.d.ts.map +1 -0
  27. package/dest/job/top-tree-job.js +152 -0
  28. package/dest/metrics.d.ts +35 -8
  29. package/dest/metrics.d.ts.map +1 -1
  30. package/dest/metrics.js +86 -14
  31. package/dest/monitors/epoch-monitor.js +6 -2
  32. package/dest/proof-publishing-service.d.ts +161 -0
  33. package/dest/proof-publishing-service.d.ts.map +1 -0
  34. package/dest/proof-publishing-service.js +335 -0
  35. package/dest/prover-node-publisher.d.ts +25 -15
  36. package/dest/prover-node-publisher.d.ts.map +1 -1
  37. package/dest/prover-node-publisher.js +200 -61
  38. package/dest/prover-node.d.ts +131 -66
  39. package/dest/prover-node.d.ts.map +1 -1
  40. package/dest/prover-node.js +536 -219
  41. package/dest/prover-publisher-factory.d.ts +3 -1
  42. package/dest/prover-publisher-factory.d.ts.map +1 -1
  43. package/dest/prover-publisher-factory.js +1 -0
  44. package/dest/session-manager.d.ts +158 -0
  45. package/dest/session-manager.d.ts.map +1 -0
  46. package/dest/session-manager.js +492 -0
  47. package/dest/test/index.d.ts +7 -6
  48. package/dest/test/index.d.ts.map +1 -1
  49. package/package.json +23 -23
  50. package/src/actions/download-epoch-proving-job.ts +1 -1
  51. package/src/actions/rerun-epoch-proving-job.ts +177 -32
  52. package/src/bin/run-failed-epoch.ts +1 -2
  53. package/src/checkpoint-store.ts +212 -0
  54. package/src/config.ts +12 -1
  55. package/src/factory.ts +2 -9
  56. package/src/index.ts +1 -0
  57. package/src/job/checkpoint-prover.ts +538 -0
  58. package/src/job/epoch-session.ts +462 -0
  59. package/src/job/top-tree-job.ts +227 -0
  60. package/src/metrics.ts +102 -23
  61. package/src/monitors/epoch-monitor.ts +2 -2
  62. package/src/proof-publishing-service.ts +427 -0
  63. package/src/prover-node-publisher.ts +235 -77
  64. package/src/prover-node.ts +617 -242
  65. package/src/prover-publisher-factory.ts +3 -0
  66. package/src/session-manager.ts +592 -0
  67. package/src/test/index.ts +6 -6
  68. package/dest/job/epoch-proving-job.d.ts +0 -63
  69. package/dest/job/epoch-proving-job.d.ts.map +0 -1
  70. package/dest/job/epoch-proving-job.js +0 -762
  71. package/src/job/epoch-proving-job.ts +0 -465
@@ -0,0 +1,165 @@
1
+ import { type ARCHIVE_HEIGHT } from '@aztec/constants';
2
+ import { type EpochNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
+ import type { EthAddress } from '@aztec/foundation/eth-address';
5
+ import type { Logger } from '@aztec/foundation/log';
6
+ import type { Tuple } from '@aztec/foundation/serialize';
7
+ import { type DateProvider } from '@aztec/foundation/timer';
8
+ import type { EpochProverFactory } from '@aztec/prover-client';
9
+ import type { ChonkCache, SubTreeResult } from '@aztec/prover-client/orchestrator';
10
+ import type { PublicProcessorFactory } from '@aztec/simulator/server';
11
+ import type { CommitteeAttestation } from '@aztec/stdlib/block';
12
+ import type { Checkpoint } from '@aztec/stdlib/checkpoint';
13
+ import type { ForkMerkleTreeOperations, ITxProvider } from '@aztec/stdlib/interfaces/server';
14
+ import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
15
+ import type { ProverNodeJobMetrics } from '../metrics.js';
16
+ /** Dependencies a `CheckpointProver` needs at construction. */
17
+ export type CheckpointProverDeps = {
18
+ proverFactory: EpochProverFactory;
19
+ /** Shared chonk-verifier cache. Survives across all sessions / epochs. */
20
+ chonkCache: ChonkCache;
21
+ publicProcessorFactory: PublicProcessorFactory;
22
+ dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>;
23
+ txProvider: ITxProvider;
24
+ /** Clock the prover-node operates against — e2e fixtures inject a cheat-controlled one. */
25
+ dateProvider: DateProvider;
26
+ proverId: EthAddress;
27
+ metrics: ProverNodeJobMetrics;
28
+ /** Tx gathering deadline. */
29
+ txGatheringTimeoutMs: number;
30
+ /** Public processor deadline. */
31
+ deadline: Date | undefined;
32
+ /**
33
+ * Fired once when the prover's block proofs reject for a genuine (non-cancel) reason — a sub-tree
34
+ * fault or a prune-induced fork fault. Useful for performing post mortem on failures.
35
+ */
36
+ onFailed?: (prover: CheckpointProver) => void;
37
+ /**
38
+ * Test-only hook: if set, invoked at the start of checkpoint execution instead of proving. Lets e2e
39
+ * tests force a sub-tree failure (it should throw) to exercise the checkpoint failure/upload path.
40
+ */
41
+ checkpointProveOverride?: () => Promise<never>;
42
+ log: Logger;
43
+ };
44
+ /** Test-only hooks the store injects into every `CheckpointProver` it constructs. */
45
+ export type CheckpointProverTestHooks = {
46
+ /** If set, invoked at the start of checkpoint execution instead of proving; should throw to fail. */
47
+ checkpointProveOverride?: () => Promise<never>;
48
+ };
49
+ /** Inputs that fully describe a checkpoint at register time. */
50
+ export type CheckpointProverArgs = {
51
+ checkpoint: Checkpoint;
52
+ /** Epoch the checkpoint belongs to (derivable from slot + L1 constants; cached at register time). */
53
+ epochNumber: EpochNumber;
54
+ attestations: CommitteeAttestation[];
55
+ previousBlockHeader: BlockHeader;
56
+ l1ToL2Messages: Fr[];
57
+ previousArchiveSiblingPath: Tuple<Fr, typeof ARCHIVE_HEIGHT>;
58
+ };
59
+ /**
60
+ * Self-contained per-checkpoint prover, content-addressed by
61
+ * `(checkpoint number, slot number, checkpoint archive root)`.
62
+ *
63
+ * The store creates a CheckpointProver once per content-key. Keying on the checkpoint's
64
+ * own archive root (its post-state) means two checkpoints are "the same" iff they
65
+ * produce the same archive — so a reorg branch, or a replacement built on the same
66
+ * predecessor but with different content, keys to a distinct prover.
67
+ *
68
+ * The prover eagerly starts its own tx gather and sub-tree work in the constructor, so
69
+ * callers only need to call `whenBlockProofsReady()` to obtain the resulting block-rollup
70
+ * proofs.
71
+ *
72
+ * A CheckpointProver does not survive a prune: its sub-tree work forks world-state per
73
+ * block, and an L1 prune of a base block faults those reads. The store therefore cancels and
74
+ * discards a prover when its checkpoint is pruned, and a re-add (even of identical content)
75
+ * constructs a fresh prover.
76
+ *
77
+ * `cancel()` is idempotent. It aborts the gather + sub-tree, rejects the block-proof
78
+ * promise, and exposes a `whenDone()` that resolves once teardown has unwound.
79
+ */
80
+ export declare class CheckpointProver {
81
+ private readonly deps;
82
+ readonly id: string;
83
+ readonly checkpoint: Checkpoint;
84
+ readonly epochNumber: EpochNumber;
85
+ readonly slotNumber: SlotNumber;
86
+ readonly attestations: CommitteeAttestation[];
87
+ readonly previousBlockHeader: BlockHeader;
88
+ readonly l1ToL2Messages: Fr[];
89
+ readonly previousArchiveSiblingPath: Tuple<Fr, typeof ARCHIVE_HEIGHT>;
90
+ /** Resolved by the sub-tree on success, rejected on cancel/failure. */
91
+ private readonly blockProofs;
92
+ /** Block-level proving was fully *enqueued* (a progress marker; the sub-tree may still be proving). */
93
+ private completed;
94
+ /** Block proofs rejected for a genuine (non-cancel) reason — a sub-tree or prune-induced fork fault. */
95
+ private failed;
96
+ /** The prover was torn down (prune / reap / shutdown). */
97
+ private cancelled;
98
+ private subTree?;
99
+ private readonly abortController;
100
+ /** Tracks the eager gather+execute task so `cancel()` and `whenDone()` can await its unwind. */
101
+ private readonly runPromise;
102
+ /** Tracks the cancel-driven teardown so `whenDone()` can await it. */
103
+ private cancelPromise?;
104
+ /** Tracks the success-driven sub-tree teardown (once block proofs are captured) so `whenDone()` can await it. */
105
+ private teardownPromise?;
106
+ constructor(args: CheckpointProverArgs, deps: CheckpointProverDeps);
107
+ /**
108
+ * Stable content-addressed identifier: `${checkpoint number}:${slot}:${archive root}`.
109
+ * The archive root is the checkpoint's post-state, so it distinguishes any two
110
+ * checkpoints that differ in history or content while collapsing identical re-adds.
111
+ */
112
+ static idFor(checkpoint: Checkpoint): string;
113
+ isCancelled(): boolean;
114
+ /**
115
+ * True once this prover's block proofs have rejected for a genuine (non-cancel) reason — a sub-tree
116
+ * proving fault or a prune-induced world-state fork fault. A failed prover cannot produce its block
117
+ * proofs, so the reconciler must not build (or rebuild) an EpochSession over it; it is cleared only by
118
+ * a prune/re-add replacing it with a fresh prover, or by expiry reaping it.
119
+ */
120
+ isFailed(): boolean;
121
+ /** AbortSignal that fires on cancel — for callers that want to wire their own tasks. */
122
+ getAbortSignal(): AbortSignal;
123
+ /** Promise that resolves with the block-rollup proofs for this checkpoint (or rejects on cancel/failure). */
124
+ whenBlockProofsReady(): Promise<SubTreeResult['blockProofOutputs']>;
125
+ /** Resolves when all in-flight work for this prover has fully unwound. */
126
+ whenDone(): Promise<void>;
127
+ private gatherAndExecute;
128
+ /**
129
+ * Rejects the block-proof promise and, unless this is a cancellation, records the prover as failed so
130
+ * the reconciler won't build an EpochSession over it. First rejection wins, so a later duplicate reject
131
+ * (e.g. the executeCheckpoint `finally`) is a harmless no-op.
132
+ */
133
+ private failBlockProofs;
134
+ private fetchTxs;
135
+ private gatherTxs;
136
+ /**
137
+ * Re-fetches this checkpoint's txs from the tx pool for a post-mortem failure upload.
138
+ *
139
+ * The prover does not cache txs on the heap — they are consumed during proving and dropped — so the
140
+ * durable pool (which keeps a mined tx until L1 finality, with A-1274's retention margin covering a
141
+ * lagging prover) is the source of truth, read back by hash here. Best-effort: any tx the pool can
142
+ * no longer supply is logged and omitted rather than failing the upload, since a partial post-mortem
143
+ * snapshot is still useful for diagnosis.
144
+ */
145
+ getTxsForUpload(): Promise<Map<string, Tx>>;
146
+ private executeCheckpoint;
147
+ /**
148
+ * Mark cancelled. Idempotent. Aborts in-flight work, rejects the block-proof promise,
149
+ * and kicks off a background teardown of the sub-tree. The teardown promise is exposed
150
+ * via `whenDone()`.
151
+ *
152
+ * `routine` distinguishes a post-finalize teardown (sub-tree already proven, fires
153
+ * once at prover exit) from a real abort (reorg, prune, deadline). Behaviour is
154
+ * identical either way; the flag only adjusts log verbosity.
155
+ */
156
+ cancel({ routine }?: {
157
+ routine?: boolean;
158
+ }): void;
159
+ private runCancel;
160
+ private teardownSubTree;
161
+ private getTxsForBlock;
162
+ private processTxs;
163
+ private createFork;
164
+ }
165
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludC1wcm92ZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9qb2IvY2hlY2twb2ludC1wcm92ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssY0FBYyxFQUF1QyxNQUFNLGtCQUFrQixDQUFDO0FBQzVGLE9BQU8sRUFBZSxLQUFLLFdBQVcsRUFBRSxLQUFLLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRWpHLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxPQUFPLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsS0FBSyxZQUFZLEVBQVMsTUFBTSx5QkFBeUIsQ0FBQztBQUduRSxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQy9ELE9BQU8sS0FBSyxFQUFpQyxVQUFVLEVBQUUsYUFBYSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDbEgsT0FBTyxLQUFLLEVBQW1CLHNCQUFzQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFdkYsT0FBTyxLQUFLLEVBQUUsb0JBQW9CLEVBQVcsTUFBTSxxQkFBcUIsQ0FBQztBQUN6RSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEtBQUssRUFBRSx3QkFBd0IsRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUc3RixPQUFPLEtBQUssRUFBRSxXQUFXLEVBQWUsRUFBRSxFQUFVLE1BQU0sa0JBQWtCLENBQUM7QUFFN0UsT0FBTyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxlQUFlLENB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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-prover.d.ts","sourceRoot":"","sources":["../../src/job/checkpoint-prover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,405 @@
1
+ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
+ import { BlockNumber } from '@aztec/foundation/branded-types';
3
+ import { padArrayEnd } from '@aztec/foundation/collection';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
+ import { promiseWithResolvers } from '@aztec/foundation/promise';
6
+ import { Timer } from '@aztec/foundation/timer';
7
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
8
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
9
+ import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
10
+ import { CheckpointConstantData } from '@aztec/stdlib/rollup';
11
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
12
+ /**
13
+ * Self-contained per-checkpoint prover, content-addressed by
14
+ * `(checkpoint number, slot number, checkpoint archive root)`.
15
+ *
16
+ * The store creates a CheckpointProver once per content-key. Keying on the checkpoint's
17
+ * own archive root (its post-state) means two checkpoints are "the same" iff they
18
+ * produce the same archive — so a reorg branch, or a replacement built on the same
19
+ * predecessor but with different content, keys to a distinct prover.
20
+ *
21
+ * The prover eagerly starts its own tx gather and sub-tree work in the constructor, so
22
+ * callers only need to call `whenBlockProofsReady()` to obtain the resulting block-rollup
23
+ * proofs.
24
+ *
25
+ * A CheckpointProver does not survive a prune: its sub-tree work forks world-state per
26
+ * block, and an L1 prune of a base block faults those reads. The store therefore cancels and
27
+ * discards a prover when its checkpoint is pruned, and a re-add (even of identical content)
28
+ * constructs a fresh prover.
29
+ *
30
+ * `cancel()` is idempotent. It aborts the gather + sub-tree, rejects the block-proof
31
+ * promise, and exposes a `whenDone()` that resolves once teardown has unwound.
32
+ */ export class CheckpointProver {
33
+ deps;
34
+ id;
35
+ checkpoint;
36
+ epochNumber;
37
+ slotNumber;
38
+ attestations;
39
+ previousBlockHeader;
40
+ l1ToL2Messages;
41
+ previousArchiveSiblingPath;
42
+ /** Resolved by the sub-tree on success, rejected on cancel/failure. */ blockProofs;
43
+ // Three independent lifecycle facts — deliberately not collapsed into one status enum, because several
44
+ // combinations are legal and relied on: a prover can be `completed` and then `cancelled` (routine
45
+ // teardown of an already-proven checkpoint), or `completed` and then `failed` (block proving was
46
+ // enqueued, but the sub-tree subsequently faulted). Only `failed` + `cancelled` is excluded — a cancel
47
+ // is not a failure (enforced in `failBlockProofs`).
48
+ /** Block-level proving was fully *enqueued* (a progress marker; the sub-tree may still be proving). */ completed;
49
+ /** Block proofs rejected for a genuine (non-cancel) reason — a sub-tree or prune-induced fork fault. */ failed;
50
+ /** The prover was torn down (prune / reap / shutdown). */ cancelled;
51
+ subTree;
52
+ abortController;
53
+ /** Tracks the eager gather+execute task so `cancel()` and `whenDone()` can await its unwind. */ runPromise;
54
+ /** Tracks the cancel-driven teardown so `whenDone()` can await it. */ cancelPromise;
55
+ /** Tracks the success-driven sub-tree teardown (once block proofs are captured) so `whenDone()` can await it. */ teardownPromise;
56
+ constructor(args, deps){
57
+ this.deps = deps;
58
+ this.blockProofs = promiseWithResolvers();
59
+ this.completed = false;
60
+ this.failed = false;
61
+ this.cancelled = false;
62
+ this.abortController = new AbortController();
63
+ this.checkpoint = args.checkpoint;
64
+ this.epochNumber = args.epochNumber;
65
+ this.slotNumber = args.checkpoint.header.slotNumber;
66
+ this.attestations = args.attestations;
67
+ this.previousBlockHeader = args.previousBlockHeader;
68
+ this.l1ToL2Messages = args.l1ToL2Messages;
69
+ this.previousArchiveSiblingPath = args.previousArchiveSiblingPath;
70
+ this.id = CheckpointProver.idFor(args.checkpoint);
71
+ // Mark blockProofs as observed so a cancel that lands before any consumer awaits
72
+ // does not surface as an unhandled rejection.
73
+ this.blockProofs.promise.catch(()=>{});
74
+ deps.log.info(`Created CheckpointProver ${this.id}`, {
75
+ checkpointNumber: this.checkpoint.number,
76
+ epochNumber: this.epochNumber,
77
+ slotNumber: this.slotNumber,
78
+ blockCount: this.checkpoint.blocks.length,
79
+ l1ToL2MessageCount: this.l1ToL2Messages.length,
80
+ archiveRoot: this.checkpoint.archive.root.toString()
81
+ });
82
+ // Kick off the eager gather + sub-tree pipeline.
83
+ this.runPromise = this.gatherAndExecute();
84
+ }
85
+ /**
86
+ * Stable content-addressed identifier: `${checkpoint number}:${slot}:${archive root}`.
87
+ * The archive root is the checkpoint's post-state, so it distinguishes any two
88
+ * checkpoints that differ in history or content while collapsing identical re-adds.
89
+ */ static idFor(checkpoint) {
90
+ return `${checkpoint.number}:${checkpoint.header.slotNumber}:${checkpoint.archive.root.toString()}`;
91
+ }
92
+ isCancelled() {
93
+ return this.cancelled;
94
+ }
95
+ /**
96
+ * True once this prover's block proofs have rejected for a genuine (non-cancel) reason — a sub-tree
97
+ * proving fault or a prune-induced world-state fork fault. A failed prover cannot produce its block
98
+ * proofs, so the reconciler must not build (or rebuild) an EpochSession over it; it is cleared only by
99
+ * a prune/re-add replacing it with a fresh prover, or by expiry reaping it.
100
+ */ isFailed() {
101
+ return this.failed;
102
+ }
103
+ /** AbortSignal that fires on cancel — for callers that want to wire their own tasks. */ getAbortSignal() {
104
+ return this.abortController.signal;
105
+ }
106
+ /** Promise that resolves with the block-rollup proofs for this checkpoint (or rejects on cancel/failure). */ whenBlockProofsReady() {
107
+ return this.blockProofs.promise;
108
+ }
109
+ /** Resolves when all in-flight work for this prover has fully unwound. */ async whenDone() {
110
+ await this.runPromise.catch(()=>{});
111
+ // `runPromise` resolves once block-level proving is *enqueued*, but the sub-tree's proofs (and the
112
+ // success-driven teardown they trigger) land later, on the `getSubTreeResult()` callback. Awaiting
113
+ // `blockProofs` here bridges that gap: on success the callback resolves `blockProofs` and then
114
+ // synchronously sets `teardownPromise` before this await resumes, so the teardown is observable
115
+ // below; on failure/cancel `blockProofs` rejects and teardown is driven by the `finally`/cancel
116
+ // paths already awaited via `runPromise`/`cancelPromise`.
117
+ await this.blockProofs.promise.catch(()=>{});
118
+ if (this.cancelPromise) {
119
+ await this.cancelPromise;
120
+ }
121
+ if (this.teardownPromise) {
122
+ await this.teardownPromise;
123
+ }
124
+ }
125
+ async gatherAndExecute() {
126
+ try {
127
+ const txs = await this.gatherTxs();
128
+ if (this.cancelled) {
129
+ return;
130
+ }
131
+ await this.executeCheckpoint(txs);
132
+ } catch (err) {
133
+ if (this.cancelled) {
134
+ this.deps.log.debug(`CheckpointProver ${this.id} cancelled during gather/execute`, {
135
+ checkpointNumber: this.checkpoint.number
136
+ });
137
+ return;
138
+ }
139
+ this.deps.log.error(`Error in CheckpointProver ${this.id}`, err, {
140
+ checkpointNumber: this.checkpoint.number
141
+ });
142
+ this.failBlockProofs(err instanceof Error ? err : new Error(String(err)));
143
+ }
144
+ }
145
+ /**
146
+ * Rejects the block-proof promise and, unless this is a cancellation, records the prover as failed so
147
+ * the reconciler won't build an EpochSession over it. First rejection wins, so a later duplicate reject
148
+ * (e.g. the executeCheckpoint `finally`) is a harmless no-op.
149
+ */ failBlockProofs(err) {
150
+ if (!this.cancelled && !this.failed) {
151
+ this.failed = true;
152
+ // Notify the owner so it can upload a post-mortem for this checkpoint. Fire-and-forget: the
153
+ // callback must not block the prover's teardown, and a throw in it must not mask the rejection.
154
+ try {
155
+ this.deps.onFailed?.(this);
156
+ } catch (err) {
157
+ this.deps.log.error(`Error in CheckpointProver onFailed callback for ${this.id}`, err);
158
+ }
159
+ }
160
+ this.blockProofs.reject(err);
161
+ }
162
+ /** Fetches every tx in this checkpoint from the tx pool (by hash, via the block tx effects). */ async fetchTxs() {
163
+ const deadline = new Date(this.deps.dateProvider.now() + this.deps.txGatheringTimeoutMs);
164
+ const txsByBlock = await Promise.all(this.checkpoint.blocks.map((block)=>this.deps.txProvider.getTxsForBlock(block, {
165
+ deadline
166
+ })));
167
+ const txs = txsByBlock.flatMap(({ txs })=>txs);
168
+ const missingTxs = txsByBlock.flatMap(({ missingTxs })=>missingTxs);
169
+ return {
170
+ txs: new Map(txs.map((tx)=>[
171
+ tx.getTxHash().toString(),
172
+ tx
173
+ ])),
174
+ missingTxs
175
+ };
176
+ }
177
+ async gatherTxs() {
178
+ const { txs, missingTxs } = await this.fetchTxs();
179
+ if (missingTxs.length > 0) {
180
+ throw new Error(`Txs not found for checkpoint ${this.checkpoint.number}: ${missingTxs.map((hash)=>hash.toString()).join(', ')}`);
181
+ }
182
+ return txs;
183
+ }
184
+ /**
185
+ * Re-fetches this checkpoint's txs from the tx pool for a post-mortem failure upload.
186
+ *
187
+ * The prover does not cache txs on the heap — they are consumed during proving and dropped — so the
188
+ * durable pool (which keeps a mined tx until L1 finality, with A-1274's retention margin covering a
189
+ * lagging prover) is the source of truth, read back by hash here. Best-effort: any tx the pool can
190
+ * no longer supply is logged and omitted rather than failing the upload, since a partial post-mortem
191
+ * snapshot is still useful for diagnosis.
192
+ */ async getTxsForUpload() {
193
+ const { txs, missingTxs } = await this.fetchTxs();
194
+ if (missingTxs.length > 0) {
195
+ this.deps.log.warn(`Missing ${missingTxs.length} tx(s) re-fetching checkpoint ${this.checkpoint.number} for failure upload`, {
196
+ checkpointNumber: this.checkpoint.number,
197
+ missingTxCount: missingTxs.length
198
+ });
199
+ }
200
+ return txs;
201
+ }
202
+ async executeCheckpoint(txs) {
203
+ const signal = this.abortController.signal;
204
+ const checkpointTimer = new Timer();
205
+ let subTreeStarted = false;
206
+ try {
207
+ // Test hook: force a sub-tree failure to exercise the checkpoint failure/upload path.
208
+ if (this.deps.checkpointProveOverride) {
209
+ await this.deps.checkpointProveOverride();
210
+ }
211
+ // The gathered txs are consumed locally below (public processing + sub-tree) and then dropped.
212
+ // They are deliberately not retained on the instance: the tx pool is the durable source and
213
+ // `getTxsForUpload` re-fetches them by hash if a post-mortem upload needs them.
214
+ const { chainId, version } = this.checkpoint.blocks[0].header.globalVariables;
215
+ const checkpointConstants = CheckpointConstantData.from({
216
+ chainId,
217
+ version,
218
+ vkTreeRoot: getVKTreeRoot(),
219
+ protocolContractsHash: protocolContractsHash,
220
+ proverId: this.deps.proverId.toField(),
221
+ slotNumber: this.checkpoint.header.slotNumber,
222
+ coinbase: this.checkpoint.header.coinbase,
223
+ feeRecipient: this.checkpoint.header.feeRecipient,
224
+ gasFees: this.checkpoint.header.gasFees
225
+ });
226
+ this.deps.log.info(`Starting processing checkpoint ${this.checkpoint.number}`, {
227
+ checkpointNumber: this.checkpoint.number,
228
+ checkpointHash: this.checkpoint.hash().toString(),
229
+ blockCount: this.checkpoint.blocks.length
230
+ });
231
+ this.subTree = await this.deps.proverFactory.createCheckpointSubTreeOrchestrator(this.deps.chonkCache, this.epochNumber, checkpointConstants, this.l1ToL2Messages, this.checkpoint.blocks.length, this.previousBlockHeader);
232
+ subTreeStarted = true;
233
+ // Bridge the sub-tree's result onto blockProofs.
234
+ void this.subTree.getSubTreeResult().then((result)=>{
235
+ this.deps.log.info(`Sub-tree block proofs ready for checkpoint ${this.checkpoint.number}`, {
236
+ checkpointNumber: this.checkpoint.number,
237
+ blockProofCount: result.blockProofOutputs.length
238
+ });
239
+ // Spans processing + proving (from executeCheckpoint start, after tx gathering) to proofs ready.
240
+ this.deps.metrics.recordCheckpointProving(checkpointTimer.ms());
241
+ this.blockProofs.resolve(result.blockProofOutputs);
242
+ // Release the sub-tree orchestrator now that its output is captured. The block-proof outputs
243
+ // survive via the resolved promise; everything else the sub-tree held — per-tx AVM inputs, and
244
+ // the base/merge/parity proof trees — is dead once proving completes, yet the prover is retained
245
+ // for the whole proof-submission window. Dropping it here is what stops that retention from
246
+ // accumulating across every proven checkpoint. Post-completion consumers (the top-tree job, a
247
+ // rebuilt EpochSession, failure upload) read only `whenBlockProofsReady()` and this prover's own
248
+ // fields (`checkpoint`, `txs`, headers, sibling paths), never the sub-tree.
249
+ this.teardownPromise = this.teardownSubTree();
250
+ }, (err)=>this.failBlockProofs(err instanceof Error ? err : new Error(String(err))));
251
+ if (signal.aborted) {
252
+ return;
253
+ }
254
+ const allTxs = this.checkpoint.blocks.flatMap((block)=>block.body.txEffects.map((txEffect)=>txs.get(txEffect.txHash.toString())));
255
+ const publicTxs = allTxs.filter((tx)=>tx?.data.forPublic);
256
+ if (publicTxs.length > 0) {
257
+ await this.subTree.startChonkVerifierCircuits(publicTxs);
258
+ if (signal.aborted) {
259
+ return;
260
+ }
261
+ }
262
+ for(let blockIndex = 0; blockIndex < this.checkpoint.blocks.length; blockIndex++){
263
+ const blockTimer = new Timer();
264
+ const block = this.checkpoint.blocks[blockIndex];
265
+ const globalVariables = block.header.globalVariables;
266
+ const blockTxs = this.getTxsForBlock(block, txs);
267
+ await this.subTree.startNewBlock(block.number, globalVariables.timestamp, blockTxs.length);
268
+ if (signal.aborted) {
269
+ return;
270
+ }
271
+ const db = await this.createFork(BlockNumber(block.number - 1), blockIndex === 0 ? this.l1ToL2Messages : undefined);
272
+ try {
273
+ if (signal.aborted) {
274
+ return;
275
+ }
276
+ const config = PublicSimulatorConfig.from({
277
+ proverId: this.deps.proverId.toField(),
278
+ skipFeeEnforcement: false,
279
+ collectDebugLogs: false,
280
+ collectHints: true,
281
+ collectPublicInputs: true,
282
+ collectStatistics: false
283
+ });
284
+ const publicProcessor = this.deps.publicProcessorFactory.create(db, globalVariables, config);
285
+ const processed = await this.processTxs(publicProcessor, blockTxs);
286
+ if (signal.aborted) {
287
+ return;
288
+ }
289
+ await this.subTree.addTxs(processed);
290
+ } finally{
291
+ await db.close();
292
+ }
293
+ if (signal.aborted) {
294
+ return;
295
+ }
296
+ await this.subTree.setBlockCompleted(block.number, block.header);
297
+ this.deps.metrics.recordBlockProcessing(blockTimer.ms());
298
+ if (signal.aborted) {
299
+ return;
300
+ }
301
+ }
302
+ this.completed = true;
303
+ const numTxs = this.checkpoint.blocks.reduce((acc, block)=>acc + block.body.txEffects.length, 0);
304
+ this.deps.metrics.recordCheckpointProcessing(checkpointTimer.ms(), this.checkpoint.blocks.length, numTxs);
305
+ this.deps.log.info(`Finished enqueueing block-level proving for checkpoint ${this.checkpoint.number} in ${checkpointTimer.ms()}ms`, {
306
+ checkpointNumber: this.checkpoint.number,
307
+ blockCount: this.checkpoint.blocks.length,
308
+ durationMs: checkpointTimer.ms()
309
+ });
310
+ } finally{
311
+ if (!this.completed) {
312
+ if (subTreeStarted) {
313
+ await this.teardownSubTree();
314
+ }
315
+ this.failBlockProofs(new Error(`Checkpoint ${this.id} did not complete block processing`));
316
+ }
317
+ }
318
+ }
319
+ /**
320
+ * Mark cancelled. Idempotent. Aborts in-flight work, rejects the block-proof promise,
321
+ * and kicks off a background teardown of the sub-tree. The teardown promise is exposed
322
+ * via `whenDone()`.
323
+ *
324
+ * `routine` distinguishes a post-finalize teardown (sub-tree already proven, fires
325
+ * once at prover exit) from a real abort (reorg, prune, deadline). Behaviour is
326
+ * identical either way; the flag only adjusts log verbosity.
327
+ */ cancel({ routine = false } = {}) {
328
+ if (this.cancelled) {
329
+ return;
330
+ }
331
+ this.cancelled = true;
332
+ // A teardown of a completed prover is routine regardless of the caller's flag —
333
+ // we logged the work as done already, so don't relabel it as a mid-flight cancel.
334
+ if (routine || this.completed) {
335
+ this.deps.log.verbose(`Tearing down CheckpointProver ${this.id}`, {
336
+ checkpointNumber: this.checkpoint.number,
337
+ wasCompleted: this.completed
338
+ });
339
+ } else {
340
+ this.deps.log.info(`Cancelling in-flight CheckpointProver ${this.id}`, {
341
+ checkpointNumber: this.checkpoint.number,
342
+ wasCompleted: this.completed
343
+ });
344
+ }
345
+ this.abortController.abort();
346
+ this.blockProofs.reject(new Error(`Checkpoint ${this.id} cancelled`));
347
+ this.cancelPromise = this.runCancel().catch(()=>{});
348
+ }
349
+ async runCancel() {
350
+ if (this.subTree) {
351
+ try {
352
+ this.subTree.cancel();
353
+ } catch (err) {
354
+ this.deps.log.error('Error cancelling sub-tree', err);
355
+ }
356
+ }
357
+ await this.runPromise.catch(()=>{});
358
+ if (this.subTree) {
359
+ await this.teardownSubTree();
360
+ }
361
+ }
362
+ async teardownSubTree() {
363
+ const { subTree } = this;
364
+ this.subTree = undefined;
365
+ if (subTree) {
366
+ this.deps.log.debug(`Tearing down sub-tree for checkpoint ${this.checkpoint.number}`, {
367
+ checkpointNumber: this.checkpoint.number
368
+ });
369
+ try {
370
+ await subTree.stop();
371
+ } catch (err) {
372
+ this.deps.log.error('Error stopping sub-tree', err);
373
+ }
374
+ }
375
+ }
376
+ getTxsForBlock(block, txs) {
377
+ return block.body.txEffects.map((txEffect)=>txs.get(txEffect.txHash.toString()));
378
+ }
379
+ async processTxs(publicProcessor, txs) {
380
+ // Pass the abort signal so a prune-driven cancel stops the current block's public execution
381
+ // immediately, rather than running it to completion before the next `signal.aborted` check.
382
+ // On abort `process` returns a partial result, the length check below throws, and
383
+ // `gatherAndExecute` swallows it via its `cancelled` guard.
384
+ const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
385
+ deadline: this.deps.deadline,
386
+ signal: this.abortController.signal
387
+ });
388
+ if (failedTxs.length) {
389
+ const failedTxHashes = await Promise.all(failedTxs.map(({ tx })=>tx.getTxHash()));
390
+ throw new Error(`Txs failed processing: ${failedTxs.map(({ error }, index)=>`${failedTxHashes[index]} (${error})`).join(', ')}`);
391
+ }
392
+ if (processedTxs.length !== txs.length) {
393
+ throw new Error(`Failed to process all txs: processed ${processedTxs.length} out of ${txs.length}`);
394
+ }
395
+ return processedTxs;
396
+ }
397
+ async createFork(blockNumber, l1ToL2Messages) {
398
+ const db = await this.deps.dbProvider.fork(blockNumber);
399
+ if (l1ToL2Messages !== undefined) {
400
+ const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages');
401
+ await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
402
+ }
403
+ return db;
404
+ }
405
+ }