@aztec/prover-client 0.0.1-commit.e3c1de76 → 0.0.1-commit.e57c76e

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 (108) hide show
  1. package/dest/config.d.ts +1 -1
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +16 -2
  4. package/dest/light/lightweight_checkpoint_builder.d.ts +10 -5
  5. package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -1
  6. package/dest/light/lightweight_checkpoint_builder.js +53 -22
  7. package/dest/mocks/test_context.d.ts +3 -1
  8. package/dest/mocks/test_context.d.ts.map +1 -1
  9. package/dest/mocks/test_context.js +18 -9
  10. package/dest/orchestrator/block-building-helpers.d.ts +4 -4
  11. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  12. package/dest/orchestrator/block-building-helpers.js +2 -2
  13. package/dest/orchestrator/block-proving-state.d.ts +4 -1
  14. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  15. package/dest/orchestrator/block-proving-state.js +7 -0
  16. package/dest/orchestrator/checkpoint-proving-state.d.ts +10 -3
  17. package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -1
  18. package/dest/orchestrator/checkpoint-proving-state.js +13 -4
  19. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts +107 -0
  20. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts.map +1 -0
  21. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.js +151 -0
  22. package/dest/orchestrator/epoch-proving-context.d.ts +51 -0
  23. package/dest/orchestrator/epoch-proving-context.d.ts.map +1 -0
  24. package/dest/orchestrator/epoch-proving-context.js +81 -0
  25. package/dest/orchestrator/epoch-proving-state.d.ts +3 -3
  26. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  27. package/dest/orchestrator/epoch-proving-state.js +5 -3
  28. package/dest/orchestrator/index.d.ts +4 -1
  29. package/dest/orchestrator/index.d.ts.map +1 -1
  30. package/dest/orchestrator/index.js +3 -0
  31. package/dest/orchestrator/orchestrator.d.ts +16 -26
  32. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  33. package/dest/orchestrator/orchestrator.js +76 -206
  34. package/dest/orchestrator/proving-scheduler.d.ts +72 -0
  35. package/dest/orchestrator/proving-scheduler.d.ts.map +1 -0
  36. package/dest/orchestrator/proving-scheduler.js +117 -0
  37. package/dest/orchestrator/top-tree-orchestrator.d.ts +83 -0
  38. package/dest/orchestrator/top-tree-orchestrator.d.ts.map +1 -0
  39. package/dest/orchestrator/top-tree-orchestrator.js +182 -0
  40. package/dest/orchestrator/top-tree-proving-scheduler.d.ts +62 -0
  41. package/dest/orchestrator/top-tree-proving-scheduler.d.ts.map +1 -0
  42. package/dest/orchestrator/top-tree-proving-scheduler.js +73 -0
  43. package/dest/orchestrator/top-tree-proving-state.d.ts +61 -0
  44. package/dest/orchestrator/top-tree-proving-state.d.ts.map +1 -0
  45. package/dest/orchestrator/top-tree-proving-state.js +185 -0
  46. package/dest/prover-client/prover-client.d.ts +64 -5
  47. package/dest/prover-client/prover-client.d.ts.map +1 -1
  48. package/dest/prover-client/prover-client.js +58 -8
  49. package/dest/proving_broker/broker_prover_facade.d.ts +1 -1
  50. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
  51. package/dest/proving_broker/broker_prover_facade.js +13 -19
  52. package/dest/proving_broker/config.d.ts +11 -67
  53. package/dest/proving_broker/config.d.ts.map +1 -1
  54. package/dest/proving_broker/config.js +16 -5
  55. package/dest/proving_broker/index.d.ts +2 -1
  56. package/dest/proving_broker/index.d.ts.map +1 -1
  57. package/dest/proving_broker/index.js +1 -0
  58. package/dest/proving_broker/proof_store/factory.d.ts +2 -5
  59. package/dest/proving_broker/proof_store/factory.d.ts.map +1 -1
  60. package/dest/proving_broker/proof_store/factory.js +7 -30
  61. package/dest/proving_broker/proof_store/file_store_proof_store.d.ts +18 -0
  62. package/dest/proving_broker/proof_store/file_store_proof_store.d.ts.map +1 -0
  63. package/dest/proving_broker/proof_store/file_store_proof_store.js +60 -0
  64. package/dest/proving_broker/proof_store/index.d.ts +2 -2
  65. package/dest/proving_broker/proof_store/index.d.ts.map +1 -1
  66. package/dest/proving_broker/proof_store/index.js +1 -1
  67. package/dest/proving_broker/proving_broker.d.ts +8 -5
  68. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  69. package/dest/proving_broker/proving_broker.js +68 -11
  70. package/dest/proving_broker/proving_broker_database/persisted.js +2 -2
  71. package/dest/proving_broker/proving_broker_instrumentation.d.ts +3 -1
  72. package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -1
  73. package/dest/proving_broker/proving_broker_instrumentation.js +7 -0
  74. package/dest/proving_broker/rpc.d.ts +6 -2
  75. package/dest/proving_broker/rpc.d.ts.map +1 -1
  76. package/dest/proving_broker/rpc.js +87 -23
  77. package/dest/test/mock_prover.d.ts +4 -4
  78. package/package.json +18 -19
  79. package/src/config.ts +18 -2
  80. package/src/light/lightweight_checkpoint_builder.ts +56 -25
  81. package/src/mocks/test_context.ts +13 -10
  82. package/src/orchestrator/block-building-helpers.ts +2 -2
  83. package/src/orchestrator/block-proving-state.ts +9 -0
  84. package/src/orchestrator/checkpoint-proving-state.ts +18 -5
  85. package/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +271 -0
  86. package/src/orchestrator/epoch-proving-context.ts +101 -0
  87. package/src/orchestrator/epoch-proving-state.ts +6 -4
  88. package/src/orchestrator/index.ts +8 -0
  89. package/src/orchestrator/orchestrator.ts +98 -268
  90. package/src/orchestrator/proving-scheduler.ts +156 -0
  91. package/src/orchestrator/top-tree-orchestrator.ts +314 -0
  92. package/src/orchestrator/top-tree-proving-scheduler.ts +154 -0
  93. package/src/orchestrator/top-tree-proving-state.ts +220 -0
  94. package/src/prover-client/prover-client.ts +132 -9
  95. package/src/proving_broker/broker_prover_facade.ts +17 -20
  96. package/src/proving_broker/config.ts +16 -2
  97. package/src/proving_broker/index.ts +1 -0
  98. package/src/proving_broker/proof_store/factory.ts +10 -32
  99. package/src/proving_broker/proof_store/file_store_proof_store.ts +78 -0
  100. package/src/proving_broker/proof_store/index.ts +1 -1
  101. package/src/proving_broker/proving_broker.ts +64 -8
  102. package/src/proving_broker/proving_broker_database/persisted.ts +2 -2
  103. package/src/proving_broker/proving_broker_instrumentation.ts +9 -0
  104. package/src/proving_broker/rpc.ts +46 -20
  105. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts +0 -14
  106. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts.map +0 -1
  107. package/dest/proving_broker/proof_store/gcs_proof_store.js +0 -52
  108. package/src/proving_broker/proof_store/gcs_proof_store.ts +0 -76
@@ -24,6 +24,7 @@ import {
24
24
  StateReference,
25
25
  TreeSnapshots,
26
26
  } from '@aztec/stdlib/tx';
27
+ import type { GenesisData } from '@aztec/stdlib/world-state';
27
28
  import type { MerkleTreeAdminDatabase } from '@aztec/world-state';
28
29
  import { NativeWorldStateService } from '@aztec/world-state/native';
29
30
 
@@ -84,14 +85,13 @@ export class TestContext {
84
85
  const feePayer = AztecAddress.fromNumber(42222);
85
86
  const initialFeePayerBalance = new Fr(10n ** 20n);
86
87
  const feePayerSlot = await computeFeePayerBalanceLeafSlot(feePayer);
87
- const prefilledPublicData = [new PublicDataTreeLeaf(feePayerSlot, initialFeePayerBalance)];
88
+ const genesis: GenesisData = {
89
+ prefilledPublicData: [new PublicDataTreeLeaf(feePayerSlot, initialFeePayerBalance)],
90
+ genesisTimestamp: 0n,
91
+ };
88
92
 
89
93
  // Separated dbs for public processor and prover - see public_processor for context
90
- const ws = await NativeWorldStateService.tmp(
91
- /*rollupAddress=*/ undefined,
92
- /*cleanupTmpDir=*/ true,
93
- prefilledPublicData,
94
- );
94
+ const ws = await NativeWorldStateService.tmp(/*rollupAddress=*/ undefined, /*cleanupTmpDir=*/ true, genesis);
95
95
 
96
96
  let localProver: ServerCircuitProver;
97
97
  const config = await getEnvironmentConfig(logger);
@@ -104,8 +104,10 @@ export class TestContext {
104
104
  bbBinaryPath: config.expectedBBPath,
105
105
  bbWorkingDirectory: config.bbWorkingDirectory,
106
106
  bbSkipCleanup: config.bbSkipCleanup,
107
- numConcurrentIVCVerifiers: 2,
107
+ numConcurrentIVCVerifiers: 8,
108
108
  bbIVCConcurrency: 1,
109
+ bbChonkVerifyMaxBatch: 16,
110
+ bbChonkVerifyConcurrency: 6,
109
111
  };
110
112
  localProver = await createProver(bbConfig);
111
113
  }
@@ -116,7 +118,7 @@ export class TestContext {
116
118
 
117
119
  const broker = new TestBroker(proverCount, localProver);
118
120
  const facade = new BrokerCircuitProverFacade(broker);
119
- const orchestrator = new TestProvingOrchestrator(ws, facade, EthAddress.ZERO);
121
+ const orchestrator = new TestProvingOrchestrator(ws, facade, EthAddress.ZERO, false, 10);
120
122
 
121
123
  await broker.start();
122
124
  facade.start();
@@ -250,7 +252,7 @@ export class TestContext {
250
252
  const previousCheckpointOutHashes = this.checkpointOutHashes;
251
253
  const builder = await LightweightCheckpointBuilder.startNewCheckpoint(
252
254
  checkpointNumber,
253
- constants,
255
+ { ...constants, timestamp },
254
256
  l1ToL2Messages,
255
257
  previousCheckpointOutHashes,
256
258
  cleanFork,
@@ -262,7 +264,7 @@ export class TestContext {
262
264
  const txs = blockTxs[i];
263
265
  const state = blockEndStates[i];
264
266
 
265
- const block = await builder.addBlock(blockGlobalVariables[i], txs, {
267
+ const { block } = await builder.addBlock(blockGlobalVariables[i], txs, {
266
268
  expectedEndState: state,
267
269
  insertTxsEffects: true,
268
270
  });
@@ -282,6 +284,7 @@ export class TestContext {
282
284
 
283
285
  return {
284
286
  constants,
287
+ checkpoint,
285
288
  header: checkpoint.header,
286
289
  blocks,
287
290
  l1ToL2Messages,
@@ -253,8 +253,8 @@ export function getPublicChonkVerifierPrivateInputsFromTx(tx: Tx | ProcessedTx,
253
253
  // Build "hints" as the private inputs for the checkpoint root rollup circuit.
254
254
  // The `blobCommitments` will be accumulated and checked in the root rollup against the `finalBlobChallenges`.
255
255
  // The `blobsHash` will be validated on L1 against the submitted blob data.
256
- export const buildBlobHints = (blobFields: Fr[]) => {
257
- const blobs = getBlobsPerL1Block(blobFields);
256
+ export const buildBlobHints = async (blobFields: Fr[]) => {
257
+ const blobs = await getBlobsPerL1Block(blobFields);
258
258
  const blobCommitments = getBlobCommitmentsFromBlobs(blobs);
259
259
  const blobsHash = computeBlobsHashFromBlobs(blobs);
260
260
  return { blobCommitments, blobs, blobsHash };
@@ -55,6 +55,7 @@ export class BlockProvingState {
55
55
  | ProofState<BlockRollupPublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
56
56
  | undefined;
57
57
  private builtBlockHeader: BlockHeader | undefined;
58
+ private builtArchive: AppendOnlyTreeSnapshot | undefined;
58
59
  private endState: StateReference | undefined;
59
60
  private endSpongeBlob: SpongeBlob | undefined;
60
61
  private txs: TxProvingState[] = [];
@@ -232,6 +233,14 @@ export class BlockProvingState {
232
233
  return this.builtBlockHeader;
233
234
  }
234
235
 
236
+ public setBuiltArchive(archive: AppendOnlyTreeSnapshot) {
237
+ this.builtArchive = archive;
238
+ }
239
+
240
+ public getBuiltArchive() {
241
+ return this.builtArchive;
242
+ }
243
+
235
244
  public getStartSpongeBlob() {
236
245
  return this.startSpongeBlob;
237
246
  }
@@ -85,7 +85,7 @@ export class CheckpointProvingState {
85
85
  typeof L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH
86
86
  >,
87
87
  public parentEpoch: EpochProvingState,
88
- private onBlobAccumulatorSet: (checkpoint: CheckpointProvingState) => void,
88
+ private onBlobAccumulatorSet: (checkpoint: CheckpointProvingState) => Promise<void>,
89
89
  ) {
90
90
  this.blockProofs = new UnbalancedTreeStore(totalNumBlocks);
91
91
  this.firstBlockNumber = BlockNumber(headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber + 1);
@@ -204,7 +204,7 @@ export class CheckpointProvingState {
204
204
  Fr.ZERO,
205
205
  NUM_MSGS_PER_BASE_PARITY,
206
206
  );
207
- return new ParityBasePrivateInputs(messages, this.constants.vkTreeRoot);
207
+ return new ParityBasePrivateInputs(messages, this.constants.vkTreeRoot, this.constants.proverId);
208
208
  }
209
209
 
210
210
  public setOutHashHint(hint: OutHashHint) {
@@ -245,7 +245,7 @@ export class CheckpointProvingState {
245
245
  this.endBlobAccumulator = await accumulateBlobs(this.blobFields!, startBlobAccumulator);
246
246
  this.startBlobAccumulator = startBlobAccumulator;
247
247
 
248
- this.onBlobAccumulatorSet(this);
248
+ await this.onBlobAccumulatorSet(this);
249
249
 
250
250
  return this.endBlobAccumulator;
251
251
  }
@@ -271,7 +271,7 @@ export class CheckpointProvingState {
271
271
  return this.totalNumBlocks === 1 ? 'rollup-checkpoint-root-single-block' : 'rollup-checkpoint-root';
272
272
  }
273
273
 
274
- public getCheckpointRootRollupInputs() {
274
+ public async getCheckpointRootRollupInputs() {
275
275
  const proofs = this.#getChildProofsForRoot();
276
276
  const nonEmptyProofs = proofs.filter(p => !!p);
277
277
  if (proofs.length !== nonEmptyProofs.length) {
@@ -287,7 +287,7 @@ export class CheckpointProvingState {
287
287
  // `blobFields` must've been set if `startBlobAccumulator` is set (in `accumulateBlobs`).
288
288
  const blobFields = this.blobFields!;
289
289
 
290
- const { blobCommitments, blobsHash } = buildBlobHints(blobFields);
290
+ const { blobCommitments, blobsHash } = await buildBlobHints(blobFields);
291
291
 
292
292
  const hints = CheckpointRootRollupHints.from({
293
293
  previousBlockHeader: this.headerOfLastBlockInPreviousCheckpoint,
@@ -346,4 +346,17 @@ export class CheckpointProvingState {
346
346
  ? [this.blockProofs.getNode(rootLocation)?.provingOutput] // If there's only 1 block, its proof will be stored at the root.
347
347
  : this.blockProofs.getChildren(rootLocation).map(c => c?.provingOutput);
348
348
  }
349
+
350
+ /**
351
+ * Returns the block-level proof outputs that feed into the checkpoint root rollup.
352
+ * Used by `CheckpointSubTreeOrchestrator` to surface its sub-tree result.
353
+ */
354
+ public getSubTreeOutputProofs() {
355
+ return this.#getChildProofsForRoot();
356
+ }
357
+
358
+ /** Sibling path of the archive tree captured before any block in this checkpoint landed. */
359
+ public getLastArchiveSiblingPath() {
360
+ return this.lastArchiveSiblingPath;
361
+ }
349
362
  }
@@ -0,0 +1,271 @@
1
+ import { FinalBlobBatchingChallenges } from '@aztec/blob-lib';
2
+ import type { ARCHIVE_HEIGHT, NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH } from '@aztec/constants';
3
+ import type { EpochNumber } from '@aztec/foundation/branded-types';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
+ import type { LoggerBindings } from '@aztec/foundation/log';
6
+ import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
7
+ import type { Tuple } from '@aztec/foundation/serialize';
8
+ import type { EthAddress } from '@aztec/stdlib/block';
9
+ import type {
10
+ ForkMerkleTreeOperations,
11
+ PublicInputsAndRecursiveProof,
12
+ ReadonlyWorldStateAccess,
13
+ ServerCircuitProver,
14
+ } from '@aztec/stdlib/interfaces/server';
15
+ import type {
16
+ BlockRollupPublicInputs,
17
+ CheckpointConstantData,
18
+ PublicChonkVerifierPublicInputs,
19
+ } from '@aztec/stdlib/rollup';
20
+ import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
21
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
22
+
23
+ import { getPublicChonkVerifierPrivateInputsFromTx } from './block-building-helpers.js';
24
+ import type { BlockProvingState } from './block-proving-state.js';
25
+ import type { CheckpointProvingState } from './checkpoint-proving-state.js';
26
+ import type { EpochProvingContext } from './epoch-proving-context.js';
27
+ import { ProvingOrchestrator } from './orchestrator.js';
28
+
29
+ /**
30
+ * Result of proving a single checkpoint's block-level sub-tree.
31
+ *
32
+ * Contains the final block-rollup proof outputs that feed the checkpoint root rollup,
33
+ * plus the archive sibling path captured before any block in the checkpoint landed
34
+ * (the top-tree needs this to assemble the checkpoint root rollup hints).
35
+ */
36
+ export type SubTreeResult = {
37
+ blockProofOutputs: PublicInputsAndRecursiveProof<
38
+ BlockRollupPublicInputs,
39
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
40
+ >[];
41
+ previousArchiveSiblingPath: Tuple<Fr, typeof ARCHIVE_HEIGHT>;
42
+ };
43
+
44
+ /**
45
+ * Orchestrates block-level proving for a single checkpoint, stopping at the boundary
46
+ * where checkpoint root rollup would otherwise begin.
47
+ *
48
+ * Reuses every circuit driver in `ProvingOrchestrator` (chonk verifier, base, merge,
49
+ * block-root, parity, block-merge) but overrides the gating method that escalates to
50
+ * checkpoint root rollup. Instead of escalating, the orchestrator resolves
51
+ * `getSubTreeResult()` once every block-level proof in the checkpoint's tree is ready.
52
+ *
53
+ * Wiring: a single-checkpoint epoch is created in the constructor (epoch number sourced
54
+ * from the supplied `EpochProvingContext`). The canonical way to obtain a fully-started
55
+ * sub-tree is the `start` static factory, which also drives the single internal
56
+ * `startNewCheckpoint(0, ...)` call. The sub-tree never calls `finalizeEpochStructure`;
57
+ * the override of `checkAndEnqueueCheckpointRootRollup` resolves `getSubTreeResult` once
58
+ * block-level proving completes.
59
+ */
60
+ export class CheckpointSubTreeOrchestrator extends ProvingOrchestrator {
61
+ private readonly subTreeResult: PromiseWithResolvers<SubTreeResult>;
62
+
63
+ constructor(
64
+ dbProvider: ReadonlyWorldStateAccess & ForkMerkleTreeOperations,
65
+ prover: ServerCircuitProver,
66
+ proverId: EthAddress,
67
+ /**
68
+ * Per-epoch shared chonk-verifier proof cache. Every chonk-verifier proof started
69
+ * by this sub-tree lives on the context and survives the sub-tree's cancellation,
70
+ * so a tx whose original checkpoint is reorged out and re-appears in a replacement
71
+ * checkpoint reuses the cached proof. The context's `epochNumber` is the epoch
72
+ * this sub-tree proves into.
73
+ */
74
+ private readonly epochContext: EpochProvingContext,
75
+ cancelJobsOnStop: boolean = false,
76
+ enqueueConcurrency: number,
77
+ telemetryClient: TelemetryClient = getTelemetryClient(),
78
+ bindings?: LoggerBindings,
79
+ ) {
80
+ super(dbProvider, prover, proverId, cancelJobsOnStop, enqueueConcurrency, telemetryClient, bindings);
81
+
82
+ // Single-checkpoint mini-epoch by construction. The total/challenges supplied to
83
+ // `super.startNewEpoch` are never read, because the sub-tree overrides
84
+ // `checkAndEnqueueCheckpointRootRollup` to short-circuit before the parent's
85
+ // checkpoint-root / finalize machinery would consume them.
86
+ super.startNewEpoch(epochContext.epochNumber, 1, FinalBlobBatchingChallenges.empty());
87
+
88
+ this.subTreeResult = promiseWithResolvers<SubTreeResult>();
89
+ // Mark the rejection branch as observed so a `cancel()` or proving failure does not
90
+ // surface an unhandled rejection when no consumer awaits getSubTreeResult().
91
+ this.subTreeResult.promise.catch(() => {});
92
+
93
+ // If the parent's proving state ever rejects, surface the failure on the sub-tree promise.
94
+ void this.provingPromise!.then(result => {
95
+ if (result.status === 'failure') {
96
+ this.subTreeResult.reject(new Error(result.reason));
97
+ }
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Constructs and starts a sub-tree for a single checkpoint. The returned sub-tree
103
+ * has had its single internal `startNewCheckpoint(0, ...)` driven; callers proceed
104
+ * directly to per-block `startNewBlock` / `addTxs` / `setBlockCompleted`.
105
+ *
106
+ * If the internal `startNewCheckpoint` rejects, the partially-constructed sub-tree
107
+ * is stopped before the error propagates, so no broker resources leak.
108
+ */
109
+ public static async start(
110
+ dbProvider: ReadonlyWorldStateAccess & ForkMerkleTreeOperations,
111
+ prover: ServerCircuitProver,
112
+ proverId: EthAddress,
113
+ epochContext: EpochProvingContext,
114
+ cancelJobsOnStop: boolean,
115
+ enqueueConcurrency: number,
116
+ checkpointConstants: CheckpointConstantData,
117
+ l1ToL2Messages: Fr[],
118
+ totalNumBlocks: number,
119
+ headerOfLastBlockInPreviousCheckpoint: BlockHeader,
120
+ telemetryClient: TelemetryClient = getTelemetryClient(),
121
+ bindings?: LoggerBindings,
122
+ ): Promise<CheckpointSubTreeOrchestrator> {
123
+ const subTree = new CheckpointSubTreeOrchestrator(
124
+ dbProvider,
125
+ prover,
126
+ proverId,
127
+ epochContext,
128
+ cancelJobsOnStop,
129
+ enqueueConcurrency,
130
+ telemetryClient,
131
+ bindings,
132
+ );
133
+ try {
134
+ await ProvingOrchestrator.prototype.startNewCheckpoint.call(
135
+ subTree,
136
+ 0,
137
+ checkpointConstants,
138
+ l1ToL2Messages,
139
+ totalNumBlocks,
140
+ headerOfLastBlockInPreviousCheckpoint,
141
+ );
142
+ return subTree;
143
+ } catch (err) {
144
+ await subTree.stop().catch(() => {});
145
+ throw err;
146
+ }
147
+ }
148
+
149
+ /** Returns a promise that resolves when block-level proving completes for the checkpoint. */
150
+ public getSubTreeResult(): Promise<SubTreeResult> {
151
+ return this.subTreeResult.promise;
152
+ }
153
+
154
+ /**
155
+ * The epoch is started in the constructor.
156
+ */
157
+ public override startNewEpoch(
158
+ _epochNumber: EpochNumber,
159
+ _totalNumCheckpoints: number,
160
+ _finalBlobBatchingChallenges: FinalBlobBatchingChallenges,
161
+ ): void {
162
+ throw new Error('CheckpointSubTreeOrchestrator starts its epoch in the constructor; do not call startNewEpoch.');
163
+ }
164
+
165
+ /**
166
+ * The single internal checkpoint is started by the `start` factory
167
+ */
168
+ public override startNewCheckpoint(
169
+ _checkpointIndex: number,
170
+ _constants: CheckpointConstantData,
171
+ _l1ToL2Messages: Fr[],
172
+ _totalNumBlocks: number,
173
+ _headerOfLastBlockInPreviousCheckpoint: BlockHeader,
174
+ ): Promise<void> {
175
+ return Promise.reject(
176
+ new Error(
177
+ 'CheckpointSubTreeOrchestrator drives its single checkpoint in `start`; do not call startNewCheckpoint.',
178
+ ),
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Returns the archive sibling path captured at the internal `startNewCheckpoint`.
184
+ * Available synchronously once `start` has resolved, before block-level proving
185
+ * completes. The top-tree consumer uses this to assemble checkpoint root rollup hints
186
+ * up-front so checkpoint root proofs can pipeline against in-flight sub-tree proving.
187
+ */
188
+ public getPreviousArchiveSiblingPath(): Tuple<Fr, typeof ARCHIVE_HEIGHT> {
189
+ const checkpoint = this.provingState!.getCheckpointProvingState(0);
190
+ if (!checkpoint) {
191
+ throw new Error('Checkpoint not started; call CheckpointSubTreeOrchestrator.start first.');
192
+ }
193
+ return checkpoint.getLastArchiveSiblingPath();
194
+ }
195
+
196
+ /**
197
+ * Override the checkpoint-root boundary: instead of escalating to checkpoint root,
198
+ * resolve the sub-tree promise with the block-level proof outputs once they're all ready.
199
+ */
200
+ // eslint-disable-next-line require-await
201
+ protected override async checkAndEnqueueCheckpointRootRollup(provingState: CheckpointProvingState): Promise<void> {
202
+ const proofs = provingState.getSubTreeOutputProofs();
203
+ const nonEmpty = proofs.filter((p): p is NonNullable<typeof p> => !!p);
204
+ if (proofs.length !== nonEmpty.length) {
205
+ // Block merge tree not fully resolved yet — will be retried as more block proofs land.
206
+ return;
207
+ }
208
+
209
+ this.subTreeResult.resolve({
210
+ blockProofOutputs: nonEmpty,
211
+ previousArchiveSiblingPath: provingState.getLastArchiveSiblingPath(),
212
+ });
213
+ }
214
+
215
+ /**
216
+ * Kickstart chonk-verifier circuits via the shared `EpochProvingContext`. The context
217
+ * owns the broker job lifecycle, so the proof survives this sub-tree's `cancel()` —
218
+ * a tx that ends up in a replacement checkpoint after a reorg can pick the cached
219
+ * promise up and skip re-proving.
220
+ */
221
+ public override startChonkVerifierCircuits(txs: Tx[]): Promise<void> {
222
+ if (!this.provingState?.verifyState()) {
223
+ return Promise.reject(new Error('Sub-tree proving state is not active.'));
224
+ }
225
+ const publicTxs = txs.filter(tx => tx.data.forPublic);
226
+ for (const tx of publicTxs) {
227
+ const txHash = tx.getTxHash().toString();
228
+ const inputs = getPublicChonkVerifierPrivateInputsFromTx(tx, this.getProverId().toField());
229
+ // Fire and forget — getOrEnqueueChonkVerifier later picks up the cached promise
230
+ // when the tx is processed inside its block.
231
+ void this.epochContext.enqueue(txHash, inputs);
232
+ }
233
+ return Promise.resolve();
234
+ }
235
+
236
+ /**
237
+ * Route the tx's chonk-verifier dependency through the per-epoch context: read the
238
+ * cached promise (or enqueue if missing), then `.then(handleResult)` to progress to
239
+ * the base rollup once the proof lands.
240
+ */
241
+ protected override getOrEnqueueChonkVerifier(provingState: BlockProvingState, txIndex: number) {
242
+ if (!provingState.verifyState()) {
243
+ return;
244
+ }
245
+
246
+ const txProvingState = provingState.getTxProvingState(txIndex);
247
+ const txHash = txProvingState.processedTx.hash.toString();
248
+
249
+ const handleResult = (
250
+ result: PublicInputsAndRecursiveProof<
251
+ PublicChonkVerifierPublicInputs,
252
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
253
+ >,
254
+ ) => {
255
+ if (!provingState.verifyState()) {
256
+ return;
257
+ }
258
+ txProvingState.setPublicChonkVerifierProof(result);
259
+ this.checkAndEnqueueBaseRollup(provingState, txIndex);
260
+ };
261
+
262
+ let promise = this.epochContext.getCached(txHash);
263
+ if (!promise) {
264
+ promise = this.epochContext.enqueue(txHash, txProvingState.getPublicChonkVerifierPrivateInputs());
265
+ }
266
+ void promise.then(handleResult).catch(() => {
267
+ // The context self-cleans on rejection; a future call (replacement sub-tree
268
+ // for this tx) will see the miss and re-enqueue. No action needed here.
269
+ });
270
+ }
271
+ }
@@ -0,0 +1,101 @@
1
+ import type { NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH } from '@aztec/constants';
2
+ import type { EpochNumber } from '@aztec/foundation/branded-types';
3
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
4
+ import type { PublicInputsAndRecursiveProof, ServerCircuitProver } from '@aztec/stdlib/interfaces/server';
5
+ import type { PublicChonkVerifierPrivateInputs, PublicChonkVerifierPublicInputs } from '@aztec/stdlib/rollup';
6
+
7
+ /**
8
+ * Result of a chonk-verifier proof, cached per tx hash on `EpochProvingContext`.
9
+ */
10
+ export type ChonkVerifierProofResult = PublicInputsAndRecursiveProof<
11
+ PublicChonkVerifierPublicInputs,
12
+ typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
13
+ >;
14
+
15
+ /**
16
+ * Per-epoch state shared across every `CheckpointSubTreeOrchestrator` constructed for
17
+ * the same epoch. Owns the chonk-verifier proof cache so a tx whose checkpoint is
18
+ * reorged out and re-appears in a replacement checkpoint does not have to re-prove
19
+ * its chonk circuit.
20
+ *
21
+ * The context's chonk-verifier broker jobs are deliberately submitted **outside** the
22
+ * sub-tree's deferred-proving queue. The sub-tree's `cancel()` therefore does not abort
23
+ * them — by design, because their result is tx-scoped, not sub-tree-scoped, and a
24
+ * replacement sub-tree should be able to consume the cached proof.
25
+ *
26
+ * Callers (`EpochProvingJob`, or unit tests) construct one context per epoch and pass
27
+ * it into every sub-tree they create. `stop()` aborts every in-flight chonk job.
28
+ */
29
+ export class EpochProvingContext {
30
+ private readonly cache = new Map<string, Promise<ChonkVerifierProofResult>>();
31
+ /** Abort controllers for in-flight chonk jobs, keyed by tx hash. */
32
+ private readonly pending = new Map<string, AbortController>();
33
+ private readonly log: Logger;
34
+ private stopped = false;
35
+
36
+ constructor(
37
+ private readonly prover: ServerCircuitProver,
38
+ public readonly epochNumber: EpochNumber,
39
+ bindings?: LoggerBindings,
40
+ ) {
41
+ this.log = createLogger('prover-client:epoch-proving-context', bindings);
42
+ }
43
+
44
+ /**
45
+ * Returns the cached chonk-verifier proof promise for the given tx hash, or
46
+ * `undefined` if none has been enqueued yet. Non-mutating.
47
+ */
48
+ public getCached(txHash: string): Promise<ChonkVerifierProofResult> | undefined {
49
+ return this.cache.get(txHash);
50
+ }
51
+
52
+ /**
53
+ * Enqueues a chonk-verifier proof for the given tx hash, returning the promise (or
54
+ * the already-cached one if already enqueued). The promise resolves when the broker
55
+ * delivers the result; on rejection (including `stop()`), the cache entry is removed
56
+ * so a subsequent caller can re-enqueue.
57
+ */
58
+ public enqueue(txHash: string, inputs: PublicChonkVerifierPrivateInputs): Promise<ChonkVerifierProofResult> {
59
+ if (this.stopped) {
60
+ return Promise.reject(new Error('EpochProvingContext is stopped'));
61
+ }
62
+
63
+ const cached = this.cache.get(txHash);
64
+ if (cached) {
65
+ return cached;
66
+ }
67
+
68
+ const controller = new AbortController();
69
+ this.pending.set(txHash, controller);
70
+ this.log.debug(`Enqueueing chonk-verifier circuit`, { txHash, epochNumber: this.epochNumber });
71
+
72
+ const promise = this.prover
73
+ .getPublicChonkVerifierProof(inputs, controller.signal, this.epochNumber)
74
+ .finally(() => this.pending.delete(txHash));
75
+
76
+ // Self-clean on rejection so a future caller can re-enqueue. Mark the rejection
77
+ // path as observed to silence unhandled-rejection warnings when no consumer
78
+ // awaits the promise (e.g. when the only `.then` chain belonged to a cancelled
79
+ // sub-tree's tx-proving state).
80
+ promise.catch(err => {
81
+ this.cache.delete(txHash);
82
+ this.log.debug(`Chonk-verifier proof failed; evicted from cache`, { txHash, error: `${err}` });
83
+ });
84
+
85
+ this.cache.set(txHash, promise);
86
+ return promise;
87
+ }
88
+
89
+ /**
90
+ * Aborts every in-flight chonk-verifier broker job and clears the cache. Called by
91
+ * the owning `EpochProvingJob` when the job stops.
92
+ */
93
+ public stop() {
94
+ this.stopped = true;
95
+ for (const controller of this.pending.values()) {
96
+ controller.abort();
97
+ }
98
+ this.pending.clear();
99
+ this.cache.clear();
100
+ }
101
+ }
@@ -76,7 +76,7 @@ export class EpochProvingState {
76
76
  public readonly epochNumber: EpochNumber,
77
77
  public readonly totalNumCheckpoints: number,
78
78
  private readonly finalBlobBatchingChallenges: FinalBlobBatchingChallenges,
79
- private onCheckpointBlobAccumulatorSet: (checkpoint: CheckpointProvingState) => void,
79
+ private onCheckpointBlobAccumulatorSet: (checkpoint: CheckpointProvingState) => Promise<void>,
80
80
  private completionCallback: (result: ProvingResult) => void,
81
81
  private rejectionCallback: (reason: string) => void,
82
82
  ) {
@@ -254,9 +254,11 @@ export class EpochProvingState {
254
254
  }
255
255
  outHashes.push(outHash);
256
256
 
257
- // Get or create hints for the next checkpoint.
258
- hint = checkpoint.getOutHashHintForNextCheckpoint() ?? (await computeOutHashHint(outHashes));
259
- checkpoint.setOutHashHintForNextCheckpoint(hint);
257
+ // If this is NOT the last checkpoint, get or create the hint for the next checkpoint.
258
+ if (i !== this.totalNumCheckpoints - 1) {
259
+ hint = checkpoint.getOutHashHintForNextCheckpoint() ?? (await computeOutHashHint(outHashes));
260
+ checkpoint.setOutHashHintForNextCheckpoint(hint);
261
+ }
260
262
  }
261
263
  }
262
264
 
@@ -1 +1,9 @@
1
1
  export { ProvingOrchestrator } from './orchestrator.js';
2
+ export { CheckpointSubTreeOrchestrator, type SubTreeResult } from './checkpoint-sub-tree-orchestrator.js';
3
+ export { EpochProvingContext, type ChonkVerifierProofResult } from './epoch-proving-context.js';
4
+ export {
5
+ TopTreeOrchestrator,
6
+ TopTreeCancelledError,
7
+ type CheckpointTopTreeData,
8
+ type TopTreeResult,
9
+ } from './top-tree-orchestrator.js';