Skip to content

Commit 2b08840

Browse files
committed
refactor(fast-inbox): name the checkpoint sub-tree proof bundle for what it holds (A-1427)
The top tree's per-checkpoint `blockProofs` promise stopped holding just block proofs when parity moved to the checkpoint root: it now carries the block-rollup proof outputs plus the checkpoint's InboxParity proof. Rename it to `subTreeProofs`, typed by a single exported `CheckpointSubTreeProofs` in prover-client (dropping the duplicate alias in prover-node), and rename the block-proof-array parameters in the orchestrator so `blockProofOutputs` means the same thing everywhere. `CheckpointProver.whenBlockProofsReady` follows suit.
1 parent 7a8a603 commit 2b08840

11 files changed

Lines changed: 113 additions & 104 deletions

yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ export type SubTreeResult = {
9090
previousArchiveSiblingPath: Tuple<Fr, typeof ARCHIVE_HEIGHT>;
9191
};
9292

93+
/**
94+
* The proofs a checkpoint's sub-tree hands to the top tree: the per-block rollup proofs plus the checkpoint's single
95+
* variable-size InboxParity proof (parity moved from the first block root to the checkpoint root in AZIP-22 Fast Inbox).
96+
*/
97+
export type CheckpointSubTreeProofs = Pick<SubTreeResult, 'blockProofOutputs' | 'inboxParityProof'>;
98+
9399
type TreeSnapshots = Map<MerkleTreeId, AppendOnlyTreeSnapshot>;
94100

95101
/**

yarn-project/prover-client/src/orchestrator/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export { CheckpointSubTreeOrchestrator, type SubTreeResult } from './checkpoint-sub-tree-orchestrator.js';
1+
export {
2+
CheckpointSubTreeOrchestrator,
3+
type CheckpointSubTreeProofs,
4+
type SubTreeResult,
5+
} from './checkpoint-sub-tree-orchestrator.js';
26
export { ChonkCache, type ChonkVerifierProofResult } from './chonk-cache.js';
37
export {
48
TopTreeOrchestrator,

yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('prover/orchestrator/top-tree', () => {
8585
await subTree.stop();
8686

8787
const topTreeData: CheckpointTopTreeData = {
88-
blockProofs: Promise.resolve({
88+
subTreeProofs: Promise.resolve({
8989
blockProofOutputs: result.blockProofOutputs,
9090
inboxParityProof: result.inboxParityProof,
9191
}),
@@ -170,9 +170,9 @@ describe('prover/orchestrator/top-tree', () => {
170170
const b = await driveSubTree(1, 1);
171171
const challenges = await context.getFinalBlobChallenges();
172172

173-
// Replace ckpt1's blockProofs with a deferred promise that resolves later.
174-
const deferred = promiseWithResolvers<typeof b.topTreeData.blockProofs extends Promise<infer T> ? T : never>();
175-
const ckpt1 = { ...b.topTreeData, blockProofs: deferred.promise } as CheckpointTopTreeData;
173+
// Replace ckpt1's subTreeProofs with a deferred promise that resolves later.
174+
const deferred = promiseWithResolvers<typeof b.topTreeData.subTreeProofs extends Promise<infer T> ? T : never>();
175+
const ckpt1 = { ...b.topTreeData, subTreeProofs: deferred.promise } as CheckpointTopTreeData;
176176

177177
const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue());
178178
try {
@@ -184,7 +184,7 @@ describe('prover/orchestrator/top-tree', () => {
184184
await new Promise(resolve => setTimeout(resolve, 50));
185185

186186
// Now resolve ckpt1 — the orchestrator should pick it up and continue.
187-
deferred.resolve((await b.topTreeData.blockProofs) as any);
187+
deferred.resolve((await b.topTreeData.subTreeProofs) as any);
188188

189189
const result = await provePromise;
190190
expect(result.proof).toBeDefined();
@@ -197,9 +197,9 @@ describe('prover/orchestrator/top-tree', () => {
197197
const { topTreeData } = await driveSubTree(1, 1);
198198
const challenges = await context.getFinalBlobChallenges();
199199

200-
// Block ckpt0's blockProofs forever so prove() can't finish.
201-
const stuck = new Promise<typeof topTreeData.blockProofs extends Promise<infer T> ? T : never>(() => {});
202-
const stuckData = { ...topTreeData, blockProofs: stuck } as CheckpointTopTreeData;
200+
// Block ckpt0's subTreeProofs forever so prove() can't finish.
201+
const stuck = new Promise<typeof topTreeData.subTreeProofs extends Promise<infer T> ? T : never>(() => {});
202+
const stuckData = { ...topTreeData, subTreeProofs: stuck } as CheckpointTopTreeData;
203203

204204
const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue());
205205
const provePromise = topTree.prove(EpochNumber(1), 1, challenges, [stuckData]);
@@ -258,7 +258,7 @@ describe('prover/orchestrator/top-tree', () => {
258258
// A malformed block proof makes toProofData (inside buildCheckpointRootInputs) throw.
259259
const badData = {
260260
...topTreeData,
261-
blockProofs: Promise.resolve({ blockProofOutputs: [{} as any], inboxParityProof: {} as any }),
261+
subTreeProofs: Promise.resolve({ blockProofOutputs: [{} as any], inboxParityProof: {} as any }),
262262
} as CheckpointTopTreeData;
263263

264264
const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue());
@@ -288,23 +288,23 @@ describe('prover/orchestrator/top-tree', () => {
288288
const { topTreeData } = await driveSubTree(1, 1);
289289
const challenges = await context.getFinalBlobChallenges();
290290

291-
const deferred = promiseWithResolvers<typeof topTreeData.blockProofs extends Promise<infer T> ? T : never>();
292-
// Observe exactly when prove() attaches its blockProofs handler, so we can sequence the
291+
const deferred = promiseWithResolvers<typeof topTreeData.subTreeProofs extends Promise<infer T> ? T : never>();
292+
// Observe exactly when prove() attaches its subTreeProofs handler, so we can sequence the
293293
// genuine rejection and the cancel deterministically rather than racing a fixed timeout.
294294
let handlerAttached = false;
295-
const observableBlockProofs = {
295+
const observableSubTreeProofs = {
296296
then: (onF: any, onR: any) => {
297297
handlerAttached = true;
298298
return deferred.promise.then(onF, onR);
299299
},
300300
};
301-
const failingData = { ...topTreeData, blockProofs: observableBlockProofs as any } as CheckpointTopTreeData;
301+
const failingData = { ...topTreeData, subTreeProofs: observableSubTreeProofs as any } as CheckpointTopTreeData;
302302

303303
const topTree = new TopTreeOrchestrator(context.prover, EthAddress.ZERO, makeTestDeferredJobQueue());
304304
const provePromise = topTree.prove(EpochNumber(1), 1, challenges, [failingData]);
305305

306-
// Wait until prove() has finished its pre-loop setup and registered the blockProofs handler.
307-
await retryUntil(() => handlerAttached, 'prove() attaches blockProofs handler', 5, 0.005);
306+
// Wait until prove() has finished its pre-loop setup and registered the subTreeProofs handler.
307+
await retryUntil(() => handlerAttached, 'prove() attaches subTreeProofs handler', 5, 0.005);
308308

309309
// Register a cancel reaction on the rejection, after prove()'s own handler (registered
310310
// first, so it runs first). On rejection the ordering is: prove's handler rejects the

yarn-project/prover-client/src/orchestrator/top-tree-orchestrator.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,19 @@ import type { BlockHeader } from '@aztec/stdlib/tx';
3333
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
3434

3535
import { buildBlobHints, toProofData } from './block-building-helpers.js';
36+
import type { CheckpointSubTreeProofs } from './checkpoint-sub-tree-orchestrator.js';
3637
import { ProvingScheduler } from './proving-scheduler.js';
3738
import { TopTreeProvingState } from './top-tree-proving-state.js';
3839

3940
/** Per-checkpoint data fed into the top tree. */
4041
export type CheckpointTopTreeData = {
4142
/**
42-
* Block-rollup proof outputs and the parity root proof from the checkpoint's sub-tree. Passed as a Promise so the
43-
* top tree can start (compute hints, pipeline merges) while sub-trees are still proving. `blockProofOutputs`
44-
* resolves to 1 entry for a single-block checkpoint, 2 for multi-block. The parity root proof feeds the checkpoint
45-
* root rollup (parity moved there from the first block root in AZIP-22 Fast Inbox).
43+
* The checkpoint sub-tree's proofs. Passed as a Promise so the top tree can start (compute hints, pipeline merges)
44+
* while sub-trees are still proving. `blockProofOutputs` resolves to 1 entry for a single-block checkpoint, 2 for
45+
* multi-block; `inboxParityProof` feeds the checkpoint root rollup (parity moved there from the first block root in
46+
* AZIP-22 Fast Inbox).
4647
*/
47-
blockProofs: Promise<{
48-
blockProofOutputs: PublicInputsAndRecursiveProof<
49-
BlockRollupPublicInputs,
50-
typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
51-
>[];
52-
inboxParityProof: PublicInputsAndRecursiveProof<ParityPublicInputs>;
53-
}>;
48+
subTreeProofs: Promise<CheckpointSubTreeProofs>;
5449
/** L2-to-L1 messages per block in the checkpoint, used to compute the out hash. */
5550
l2ToL1MsgsPerBlock: Fr[][][];
5651
/** Blob fields encoding the checkpoint's tx effects, used to compute the blob accumulator. */
@@ -90,7 +85,7 @@ type OutHashHint = {
9085
*
9186
* Pipelined start: `prove()` does not wait for block-level proving. It pre-computes the
9287
* out-hash and blob-accumulator hint chains immediately from archiver-derivable data,
93-
* and each checkpoint's root rollup fires the moment its sub-tree's `blockProofs`
88+
* and each checkpoint's root rollup fires the moment its sub-tree's `subTreeProofs`
9489
* promise resolves. Later checkpoints can still be block-level proving in parallel.
9590
*/
9691
export class TopTreeOrchestrator extends ProvingScheduler {
@@ -172,7 +167,7 @@ export class TopTreeOrchestrator extends ProvingScheduler {
172167
for (let i = 0; i < checkpointData.length; i++) {
173168
const cd = checkpointData[i];
174169
const checkpointIndex = i;
175-
void cd.blockProofs.then(
170+
void cd.subTreeProofs.then(
176171
subTreeProofs => {
177172
if (this.cancelled || !this.state?.verifyState()) {
178173
return;
@@ -227,7 +222,7 @@ export class TopTreeOrchestrator extends ProvingScheduler {
227222
private enqueueCheckpointRoot(
228223
state: TopTreeProvingState,
229224
checkpointIndex: number,
230-
blockProofs: PublicInputsAndRecursiveProof<
225+
blockProofOutputs: PublicInputsAndRecursiveProof<
231226
BlockRollupPublicInputs,
232227
typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
233228
>[],
@@ -236,7 +231,13 @@ export class TopTreeOrchestrator extends ProvingScheduler {
236231
outHashHint: OutHashHint,
237232
startBlobAccumulator: BatchedBlobAccumulator,
238233
) {
239-
void this.buildCheckpointRootInputs(blockProofs, inboxParityProof, cd, outHashHint, startBlobAccumulator).then(
234+
void this.buildCheckpointRootInputs(
235+
blockProofOutputs,
236+
inboxParityProof,
237+
cd,
238+
outHashHint,
239+
startBlobAccumulator,
240+
).then(
240241
inputs => {
241242
this.deferredProving(
242243
state,
@@ -269,7 +270,7 @@ export class TopTreeOrchestrator extends ProvingScheduler {
269270
}
270271

271272
private async buildCheckpointRootInputs(
272-
blockProofs: PublicInputsAndRecursiveProof<
273+
blockProofOutputs: PublicInputsAndRecursiveProof<
273274
BlockRollupPublicInputs,
274275
typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
275276
>[],
@@ -293,7 +294,7 @@ export class TopTreeOrchestrator extends ProvingScheduler {
293294
});
294295

295296
const inboxParity = toProofData(inboxParityProof);
296-
const proofDatas = blockProofs.map(p => toProofData(p));
297+
const proofDatas = blockProofOutputs.map(p => toProofData(p));
297298
return proofDatas.length === 1
298299
? new CheckpointRootSingleBlockRollupPrivateInputs(proofDatas[0], inboxParity, hints)
299300
: new CheckpointRootRollupPrivateInputs([proofDatas[0], proofDatas[1]], inboxParity, hints);

yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ describe('prover/bb_prover/full-rollup', () => {
103103
}
104104

105105
topTreeData.push({
106-
blockProofs: subTree
106+
subTreeProofs: subTree
107107
.getSubTreeResult()
108108
.then(r => ({ blockProofOutputs: r.blockProofOutputs, inboxParityProof: r.inboxParityProof })),
109109
l2ToL1MsgsPerBlock: blocks.map(b => b.txs.map(tx => tx.txEffect.l2ToL1Msgs)),

yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => {
154154
}
155155

156156
topTreeData.push({
157-
blockProofs: subTree
157+
subTreeProofs: subTree
158158
.getSubTreeResult()
159159
.then(r => ({ blockProofOutputs: r.blockProofOutputs, inboxParityProof: r.inboxParityProof })),
160160
l2ToL1MsgsPerBlock: blocks.map(b => b.txs.map(tx => tx.txEffect.l2ToL1Msgs)),

yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ export async function rerunCheckpointProvingJob(
9595

9696
const prover = await buildCheckpointProver(ctx, 0, log);
9797
try {
98-
const blockProofs = await prover.whenBlockProofsReady();
99-
log.info(`Completed proving for checkpoint ${checkpointNumber} with ${blockProofs.length} block proof(s)`);
100-
return blockProofs;
98+
const { blockProofOutputs } = await prover.whenSubTreeProofsReady();
99+
log.info(`Completed proving for checkpoint ${checkpointNumber} with ${blockProofOutputs.length} block proof(s)`);
100+
return blockProofOutputs;
101101
} finally {
102102
prover.cancel({ routine: true });
103103
await prover.whenDone();

yarn-project/prover-node/src/job/checkpoint-prover.test.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ describe('CheckpointProver', () => {
134134
await prover.whenDone();
135135
});
136136

137-
it('rejects whenBlockProofsReady() but does not mark the prover failed or fire onFailed', async () => {
137+
it('rejects whenSubTreeProofsReady() but does not mark the prover failed or fire onFailed', async () => {
138138
// A cancel (reorg/prune/shutdown) is not a proving failure: isFailed() must stay false and the
139139
// onFailed callback must not fire (no post-mortem upload for a cancelled prover).
140140
const prover = makeProver();
141-
const blockProofs = prover.whenBlockProofsReady();
141+
const subTreeProofs = prover.whenSubTreeProofsReady();
142142
prover.cancel();
143-
await expect(blockProofs).rejects.toThrow(/cancelled/);
143+
await expect(subTreeProofs).rejects.toThrow(/cancelled/);
144144
expect(prover.isFailed()).toBe(false);
145145
expect(onFailed).not.toHaveBeenCalled();
146146
await prover.whenDone();
@@ -161,11 +161,11 @@ describe('CheckpointProver', () => {
161161

162162
it('routine cancel still aborts and rejects block proofs (only log level differs)', async () => {
163163
const prover = makeProver();
164-
const blockProofs = prover.whenBlockProofsReady();
164+
const subTreeProofs = prover.whenSubTreeProofsReady();
165165
prover.cancel({ routine: true });
166166
expect(prover.isCancelled()).toBe(true);
167167
expect(prover.getAbortSignal().aborted).toBe(true);
168-
await expect(blockProofs).rejects.toThrow(/cancelled/);
168+
await expect(subTreeProofs).rejects.toThrow(/cancelled/);
169169
await prover.whenDone();
170170
});
171171
});
@@ -221,7 +221,7 @@ describe('CheckpointProver', () => {
221221
// ---------------- gather failure ----------------
222222

223223
describe('gather failures', () => {
224-
it('rejects whenBlockProofsReady when txProvider returns missing txs', async () => {
224+
it('rejects whenSubTreeProofsReady when txProvider returns missing txs', async () => {
225225
const missingHash = checkpoint.blocks[0].body.txEffects[0]?.txHash;
226226
// Without a real missing hash the per-block payload would be empty and the prover
227227
// would happily proceed; only checkpoints with txs can exercise this branch.
@@ -232,7 +232,7 @@ describe('CheckpointProver', () => {
232232
txProvider.getTxsForBlock.mockResolvedValue({ txs: [], missingTxs: [missingHash] });
233233

234234
const prover = makeProver();
235-
await expect(prover.whenBlockProofsReady()).rejects.toThrow(/Txs not found/);
235+
await expect(prover.whenSubTreeProofsReady()).rejects.toThrow(/Txs not found/);
236236
await prover.whenDone();
237237
});
238238

@@ -245,19 +245,19 @@ describe('CheckpointProver', () => {
245245
txProvider.getTxsForBlock.mockReturnValue(gate.promise);
246246

247247
const prover = makeProver();
248-
const blockProofs = prover.whenBlockProofsReady();
248+
const subTreeProofs = prover.whenSubTreeProofsReady();
249249
prover.cancel();
250250
gate.reject(new Error('gather aborted by test'));
251-
await expect(blockProofs).rejects.toThrow(/cancelled/);
251+
await expect(subTreeProofs).rejects.toThrow(/cancelled/);
252252
await expect(prover.whenDone()).resolves.toBeUndefined();
253253
});
254254

255-
it('lets a second whenBlockProofsReady caller observe the same rejection', async () => {
255+
it('lets a second whenSubTreeProofsReady caller observe the same rejection', async () => {
256256
// Two callers awaiting the same promise both see the rejection — neither leaks an
257257
// unhandled rejection (the constructor pre-attaches a noop catch handler).
258258
const prover = makeProver();
259-
const a = prover.whenBlockProofsReady();
260-
const b = prover.whenBlockProofsReady();
259+
const a = prover.whenSubTreeProofsReady();
260+
const b = prover.whenSubTreeProofsReady();
261261
prover.cancel();
262262
await Promise.all([expect(a).rejects.toThrow(/cancelled/), expect(b).rejects.toThrow(/cancelled/)]);
263263
await prover.whenDone();
@@ -274,7 +274,7 @@ describe('CheckpointProver', () => {
274274

275275
const prover = makeProver();
276276
failure.resolve({ txs: [], missingTxs: [missingHash] });
277-
await expect(prover.whenBlockProofsReady()).rejects.toThrow(/Txs not found/);
277+
await expect(prover.whenSubTreeProofsReady()).rejects.toThrow(/Txs not found/);
278278
// Subsequent cancel is a no-op; no throws.
279279
prover.cancel();
280280
expect(prover.isCancelled()).toBe(true);
@@ -285,10 +285,10 @@ describe('CheckpointProver', () => {
285285
// ---------------- data-plane reorg fork fault ----------------
286286

287287
describe('data-plane reorg fault', () => {
288-
it('rejects whenBlockProofsReady when a world-state fork faults mid-proof', async () => {
288+
it('rejects whenSubTreeProofsReady when a world-state fork faults mid-proof', async () => {
289289
// Models the data-plane prune race: gather succeeds and the sub-tree starts, but the
290290
// world-state synchronizer has already unwound the base block, so forking it faults inside
291-
// executeCheckpoint. The fault must reject whenBlockProofsReady() AND mark the prover failed, so
291+
// executeCheckpoint. The fault must reject whenSubTreeProofsReady() AND mark the prover failed, so
292292
// the SessionManager won't build (or rebuild) an EpochSession over it until a re-add replaces it.
293293
txProvider.getTxsForBlock.mockReset();
294294
txProvider.getTxsForBlock.mockResolvedValue({ txs: [], missingTxs: [] });
@@ -310,9 +310,9 @@ describe('CheckpointProver', () => {
310310

311311
const prover = makeProver();
312312

313-
// blockProofs rejects: the fork error aborts the block loop before completion, so the sub-tree
313+
// subTreeProofs rejects: the fork error aborts the block loop before completion, so the sub-tree
314314
// never yields proofs. (The raw fork error is logged; the promise settles as not-completed.)
315-
await expect(prover.whenBlockProofsReady()).rejects.toThrow(/did not complete block processing/);
315+
await expect(prover.whenSubTreeProofsReady()).rejects.toThrow(/did not complete block processing/);
316316
expect(dbProvider.fork).toHaveBeenCalled();
317317
expect(prover.isFailed()).toBe(true);
318318
// The owner is notified exactly once, with this prover, so it can upload a checkpoint post-mortem.

0 commit comments

Comments
 (0)