Skip to content

Commit 36db4d0

Browse files
committed
fix(fast-inbox): sub-tree slice test models post-merge block proof outputs (A-1385)
The sub-tree result surfaces at most two top-level nodes for the binary checkpoint root, so a three-block checkpoint yields [block-merge(blocks 0-1), block 2] rather than one output per block. The slice/sponge continuity walk now runs over expected per-output block ranges; merge public inputs span their range (is_first_block from the left child, start sponge/state from the left, end sponge/state from the right).
1 parent c6ed141 commit 36db4d0

1 file changed

Lines changed: 21 additions & 13 deletions

File tree

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { MAX_L2_TO_L1_MSGS_PER_TX } from '@aztec/constants';
22
import { EpochNumber } from '@aztec/foundation/branded-types';
3-
import { padArrayEnd } from '@aztec/foundation/collection';
3+
import { padArrayEnd, sum } from '@aztec/foundation/collection';
44
import { Fr } from '@aztec/foundation/curves/bn254';
55
import { EthAddress } from '@aztec/foundation/eth-address';
66
import { createLogger } from '@aztec/foundation/log';
@@ -225,7 +225,8 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => {
225225
// carries none (txs only), and the last block carries a bundle with zero txs (a message-only block, proven by
226226
// the msgs-only block root). The sub-tree must append each block's own slice at compact indices with
227227
// contiguous, non-overlapping per-block snapshots, and thread the message sponge across the blocks (AZIP-22
228-
// Fast Inbox).
228+
// Fast Inbox). The sub-tree result surfaces post-merge top-level nodes (at most two, for the binary
229+
// checkpoint root), not one output per block.
229230
const l1ToL2MessagesPerBlock = [[new Fr(1001), new Fr(1002)], [], [new Fr(1003), new Fr(1004), new Fr(1005)]];
230231
const numBlocks = l1ToL2MessagesPerBlock.length;
231232
const { constants, blocks, l1ToL2Messages, previousBlockHeader } = await context.makeCheckpointWithMessagesPerBlock(
@@ -261,15 +262,19 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => {
261262
}
262263

263264
const result = await resultPromise;
264-
expect(result.blockProofOutputs).toHaveLength(numBlocks);
265+
// Three block roots reduce to two top-level outputs: a block-merge over blocks 0-1 and block 2's msgs-only
266+
// root. Merge public inputs span their range: is_first_block propagates from the left child, the start
267+
// sponge/state come from the left child and the end sponge/state from the right.
268+
const expectedOutputBlockRanges = [[0, 1], [2]];
269+
expect(result.blockProofOutputs).toHaveLength(expectedOutputBlockRanges.length);
265270

266-
// Order the block outputs by block number (the archive tree grows by exactly one leaf per block).
271+
// Order the outputs by position in the checkpoint (the archive tree grows by one leaf per block).
267272
const ordered = [...result.blockProofOutputs].sort(
268273
(a, b) => a.inputs.previousArchive.nextAvailableLeafIndex - b.inputs.previousArchive.nextAvailableLeafIndex,
269274
);
270275

271-
// Walk the blocks in order, asserting the L1-to-L2 message tree partitions cleanly into per-block slices,
272-
// with each block's start snapshot equal to the previous block's end snapshot (the "threaded" per-block
276+
// Walk the outputs in order, asserting the L1-to-L2 message tree partitions cleanly into per-output slices,
277+
// with each output's start snapshot equal to the previous output's end snapshot (the "threaded" per-block
273278
// L1-to-L2 tree state).
274279
const baseLeaf = ordered[0].inputs.startState.l1ToL2MessageTree.nextAvailableLeafIndex;
275280
let expectedStartLeaf = baseLeaf;
@@ -279,21 +284,24 @@ describe('prover/orchestrator/checkpoint-sub-tree', () => {
279284
const expectedSponge = L1ToL2MessageSponge.empty();
280285
for (const [i, output] of ordered.entries()) {
281286
const inputs = output.inputs;
287+
const blockIndexes = expectedOutputBlockRanges[i];
282288
const startLeaf = inputs.startState.l1ToL2MessageTree.nextAvailableLeafIndex;
283289
const endLeaf = inputs.endState.l1ToL2MessageTree.nextAvailableLeafIndex;
284-
const sliceLen = l1ToL2MessagesPerBlock[i].length;
290+
const sliceLen = sum(blockIndexes.map(b => l1ToL2MessagesPerBlock[b].length));
285291

286-
// Only the checkpoint's first block flags isFirstBlock.
287-
expect(inputs.isFirstBlock).toBe(i === 0);
288-
// Contiguous, non-overlapping slices: this block starts where the previous one ended (no gap/overlap).
292+
// Only the output covering the checkpoint's first block flags isFirstBlock.
293+
expect(inputs.isFirstBlock).toBe(blockIndexes.includes(0));
294+
// Contiguous, non-overlapping slices: this output starts where the previous one ended (no gap/overlap).
289295
expect(startLeaf).toBe(expectedStartLeaf);
290-
// Block slice = [prevBlockLeafCount, blockLeafCount): the tree grows by exactly this block's bundle size.
296+
// The tree grows by exactly the covered blocks' bundle sizes.
291297
expect(endLeaf - startLeaf).toBe(sliceLen);
292298
expectedStartLeaf = endLeaf;
293299

294-
// Sponge continuity: this block starts from the previous block's end sponge and absorbs its own slice.
300+
// Sponge continuity: this output starts from the previous one's end sponge and absorbs its blocks' slices.
295301
expect(inputs.startMsgSponge.toBuffer()).toEqual(expectedSponge.toBuffer());
296-
await expectedSponge.absorb(l1ToL2MessagesPerBlock[i]);
302+
for (const blockIndex of blockIndexes) {
303+
await expectedSponge.absorb(l1ToL2MessagesPerBlock[blockIndex]);
304+
}
297305
expect(inputs.endMsgSponge.toBuffer()).toEqual(expectedSponge.toBuffer());
298306
}
299307

0 commit comments

Comments
 (0)