Skip to content

Commit 72c8ae2

Browse files
spalladinoclaude
andcommitted
feat(fast-inbox): drop bundle num_real_msgs and pad in the prover path (A-1384)
Align the node message-bundle representation and prover orchestrator with the circuit flip (AZIP-22 Fast Inbox): - stdlib L1ToL2MessageBundle drops numRealMsgs (single numMsgs real count); makeL1ToL2MessageBundle and the noir conversion follow. - Prover orchestrator: compact (unpadded) L1-to-L2 tree insertion, checkpoint InboxParity in_hash hint fed zero, getPaddedL1ToL2Messages/getNumRealL1ToL2Messages retired. Known follow-up (flagged in-code): the prover still assigns the whole checkpoint's messages to its first block; post-flip a block carries at most MAX_L1_TO_L2_MSGS_PER_BLOCK and a checkpoint drains across up to four blocks, so the per-block message split (with per-block snapshots and compact frontier hints) still needs the proving-path rework, verified by epoch-proving e2e on CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9e80502 commit 72c8ae2

6 files changed

Lines changed: 28 additions & 51 deletions

File tree

yarn-project/noir-protocol-circuits-types/src/conversion/server.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,6 @@ function mapL1ToL2MessageBundleToNoir(bundle: L1ToL2MessageBundle) {
836836
// get the fixed-length `FixedLengthArray` the generated `L1ToL2MessageBundle.messages` type requires.
837837
messages: mapFieldArrayToNoir(bundle.messages, MAX_L1_TO_L2_MSGS_PER_BLOCK),
838838
num_msgs: mapNumberToNoir(bundle.numMsgs),
839-
num_real_msgs: mapNumberToNoir(bundle.numRealMsgs),
840839
};
841840
}
842841

yarn-project/prover-client/src/orchestrator/block-proving-state.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
1212
import type { Tuple } from '@aztec/foundation/serialize';
1313
import { type TreeNodeLocation, UnbalancedTreeStore } from '@aztec/foundation/trees';
1414
import type { PublicInputsAndRecursiveProof } from '@aztec/stdlib/interfaces/server';
15-
import { L1ToL2MessageBundle } from '@aztec/stdlib/messaging';
15+
import { L1ToL2MessageBundle, makeL1ToL2MessageBundle } from '@aztec/stdlib/messaging';
1616
import type { RollupHonkProofData } from '@aztec/stdlib/proofs';
1717
import {
1818
BlockRollupPublicInputs,
@@ -368,18 +368,17 @@ export class BlockProvingState {
368368
}
369369

370370
/**
371-
* The message bundle this block appends. Transitionally the first block carries the whole checkpoint's messages
372-
* padded to `MAX_L1_TO_L2_MSGS_PER_BLOCK` — inserted as a full aligned subtree into the L1-to-L2 tree (`numMsgs` =
373-
* the cap), while only the real messages are absorbed into the message sponge (`numRealMsgs`, matching the
374-
* checkpoint's InboxParity proof). Non-first blocks carry an empty bundle.
371+
* The real-count message bundle this block appends (AZIP-22 Fast Inbox): the real leaves inserted at compact indices
372+
* and absorbed into the message sponge.
373+
*
374+
* TODO(fast-inbox): the prover still assigns the whole checkpoint's messages to the first block. Post-flip a block
375+
* carries at most `MAX_L1_TO_L2_MSGS_PER_BLOCK` and a checkpoint drains its consumption across up to four blocks, so
376+
* this must split the checkpoint's messages per block (with per-block start/end snapshots and full-height frontier
377+
* hints at compact indices). Requires the proving-path rework; verified by epoch-proving e2e on CI.
375378
*/
376379
#getMessageBundle(): L1ToL2MessageBundle {
377380
if (this.isFirstBlock) {
378-
return new L1ToL2MessageBundle(
379-
this.parentCheckpoint.getPaddedL1ToL2Messages(),
380-
MAX_L1_TO_L2_MSGS_PER_BLOCK,
381-
this.parentCheckpoint.getNumRealL1ToL2Messages(),
382-
);
381+
return makeL1ToL2MessageBundle(this.parentCheckpoint.getL1ToL2Messages());
383382
}
384383
return L1ToL2MessageBundle.empty();
385384
}

yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
1010
import type { Tuple } from '@aztec/foundation/serialize';
1111
import { type TreeNodeLocation, UnbalancedTreeStore } from '@aztec/foundation/trees';
1212
import type { PublicInputsAndRecursiveProof } from '@aztec/stdlib/interfaces/server';
13-
import { L1ToL2MessageSponge, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
13+
import { L1ToL2MessageSponge } from '@aztec/stdlib/messaging';
1414
import { InboxParityPrivateInputs, type ParityPublicInputs } from '@aztec/stdlib/parity';
1515
import { BlockMergeRollupPrivateInputs, BlockRollupPublicInputs, CheckpointConstantData } from '@aztec/stdlib/rollup';
1616
import type { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
@@ -54,9 +54,6 @@ export class CheckpointProvingState {
5454
Fr,
5555
typeof L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH
5656
>,
57-
// The checkpoint's messages padded to `MAX_L1_TO_L2_MSGS_PER_CHECKPOINT` (the first block's transitional bundle,
58-
// inserted as a full subtree into the L1-to-L2 tree).
59-
private readonly paddedL1ToL2Messages: Fr[],
6057
// Message-bundle sponge over the checkpoint's real messages (real-count absorb). Equals the InboxParity proof's
6158
// end sponge and the sponge the block roots accumulate, so it is threaded into non-first block roots as their
6259
// inherited `startMsgSponge`.
@@ -71,14 +68,9 @@ export class CheckpointProvingState {
7168
this.firstBlockNumber = BlockNumber(headerOfLastBlockInPreviousCheckpoint.globalVariables.blockNumber + 1);
7269
}
7370

74-
/** The checkpoint's messages padded to the per-checkpoint cap (the first block's transitional bundle). */
75-
public getPaddedL1ToL2Messages(): Fr[] {
76-
return this.paddedL1ToL2Messages;
77-
}
78-
79-
/** Number of real (non-padding) L1-to-L2 messages in the checkpoint — the sponge/InboxParity real-count. */
80-
public getNumRealL1ToL2Messages(): number {
81-
return this.l1ToL2Messages.length;
71+
/** The checkpoint's real L1-to-L2 messages (unpadded), consumed across its blocks (AZIP-22 Fast Inbox). */
72+
public getL1ToL2Messages(): Fr[] {
73+
return this.l1ToL2Messages;
8274
}
8375

8476
/** The message-bundle sponge over the checkpoint's real messages (real-count absorb) — inherited by non-first block roots. */
@@ -179,7 +171,8 @@ export class CheckpointProvingState {
179171
this.l1ToL2Messages,
180172
this.startInboxRollingHash,
181173
L1ToL2MessageSponge.empty(),
182-
computeInHashFromL1ToL2Messages(this.l1ToL2Messages),
174+
// Legacy in_hash is dead post-flip; the InboxParity pass-through hint carries zero (AZIP-22 Fast Inbox).
175+
Fr.ZERO,
183176
this.constants.vkTreeRoot,
184177
this.constants.proverId,
185178
);

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import {
33
type ARCHIVE_HEIGHT,
44
L1_TO_L2_MSG_SUBTREE_HEIGHT,
55
L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
6-
MAX_L1_TO_L2_MSGS_PER_CHECKPOINT,
76
NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
87
} from '@aztec/constants';
98
import { BlockNumber, type EpochNumber } from '@aztec/foundation/branded-types';
10-
import { padArrayEnd } from '@aztec/foundation/collection';
119
import { Fr } from '@aztec/foundation/curves/bn254';
1210
import { AbortError } from '@aztec/foundation/error';
1311
import type { LoggerBindings } from '@aztec/foundation/log';
@@ -528,10 +526,8 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler {
528526
newL1ToL2MessageSubtreeRootSiblingPath,
529527
} = await this.updateL1ToL2MessageTree(l1ToL2Messages, db);
530528

531-
// The first block inserts the whole checkpoint's messages as a full padded subtree into the L1-to-L2 tree, so keep
532-
// the padded array for the block-root bundle. The message sponge, however, absorbs only the real messages
533-
// (real-count), so it matches the checkpoint's single InboxParity proof; non-first block roots inherit this sponge.
534-
const paddedL1ToL2Messages = padArrayEnd<Fr, number>(l1ToL2Messages, Fr.ZERO, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT);
529+
// The message sponge absorbs the checkpoint's real messages (real-count), matching the checkpoint's single
530+
// InboxParity proof; non-first block roots inherit this sponge (AZIP-22 Fast Inbox).
535531
const checkpointMsgSponge = L1ToL2MessageSponge.empty();
536532
await checkpointMsgSponge.absorb(l1ToL2Messages);
537533

@@ -547,7 +543,6 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler {
547543
lastL1ToL2MessageSubtreeRootSiblingPath,
548544
newL1ToL2MessageTreeSnapshot,
549545
newL1ToL2MessageSubtreeRootSiblingPath,
550-
paddedL1ToL2Messages,
551546
checkpointMsgSponge,
552547
Number(this.epochNumber),
553548
/* isAlive */ () => !this.cancelled,

yarn-project/stdlib/src/messaging/l1_to_l2_message_bundle.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,29 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
66
import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
77

88
/**
9-
* A block's L1-to-L2 message bundle: the leaves it inserts into the L1-to-L2 message tree plus the two counts a block
10-
* root needs — one for the (transitionally padded) tree insert, one for the real-count sponge absorb. See the Noir
11-
* `L1ToL2MessageBundle`. Once the tree insert becomes real-count too, `numMsgs === numRealMsgs` and `numRealMsgs` is
12-
* dropped.
9+
* A block's L1-to-L2 message bundle: the real message leaves it inserts into the L1-to-L2 message tree and the count
10+
* that drives both the compact (unpadded) tree append and the message-sponge absorb (AZIP-22 Fast Inbox). See the Noir
11+
* `L1ToL2MessageBundle`.
1312
*/
1413
export class L1ToL2MessageBundle {
1514
constructor(
16-
/** The message leaves, padded with zeros to `MAX_L1_TO_L2_MSGS_PER_BLOCK`. Kept as a plain array (not a tuple)
17-
* to avoid TS's deep-instantiation limit on the 1024-lane type. */
15+
/** The real message leaves in the leading lanes, padded with zeros to `MAX_L1_TO_L2_MSGS_PER_BLOCK`. Kept as a
16+
* plain array (not a tuple) to avoid TS's deep-instantiation limit on the wide type. */
1817
public readonly messages: Fr[],
19-
/** Number of leaves inserted into the L1-to-L2 message tree (the padded subtree size, or 0 for an empty block). */
18+
/** Number of real messages: drives both the compact tree append and the sponge absorb. */
2019
public readonly numMsgs: number,
21-
/** Number of real (non-padding) messages absorbed into the message sponge. */
22-
public readonly numRealMsgs: number,
2320
) {}
2421

2522
/** An empty bundle: no leaves inserted, nothing absorbed. */
2623
static empty(): L1ToL2MessageBundle {
2724
return new L1ToL2MessageBundle(
2825
Array.from({ length: MAX_L1_TO_L2_MSGS_PER_BLOCK }, () => Fr.ZERO),
2926
0,
30-
0,
3127
);
3228
}
3329

3430
toBuffer() {
35-
return serializeToBuffer(this.messages, this.numMsgs, this.numRealMsgs);
31+
return serializeToBuffer(this.messages, this.numMsgs);
3632
}
3733

3834
toString() {
@@ -43,7 +39,7 @@ export class L1ToL2MessageBundle {
4339
const reader = BufferReader.asReader(buffer);
4440
// Array.from (not readArray with the literal cap) keeps the type `Fr[]` and avoids TS's deep-tuple instantiation.
4541
const messages = Array.from({ length: MAX_L1_TO_L2_MSGS_PER_BLOCK }, () => Fr.fromBuffer(reader));
46-
return new L1ToL2MessageBundle(messages, reader.readNumber(), reader.readNumber());
42+
return new L1ToL2MessageBundle(messages, reader.readNumber());
4743
}
4844

4945
static fromString(str: string) {
@@ -59,12 +55,11 @@ export class L1ToL2MessageBundle {
5955
}
6056
}
6157

62-
/** Pads `messages` to a full subtree and wraps it into a bundle whose tree-insert count is the padded subtree size. */
58+
/** Wraps `messages` (the real leaves) into a bundle: the leaves fill the leading lanes, the count is the real count. */
6359
export function makeL1ToL2MessageBundle(messages: Fr[]): L1ToL2MessageBundle {
6460
// Explicit `<Fr, number>` keeps the result `Fr[]`; padding to the literal cap would otherwise infer a deep tuple.
6561
return new L1ToL2MessageBundle(
6662
padArrayEnd<Fr, number>(messages, Fr.ZERO, MAX_L1_TO_L2_MSGS_PER_BLOCK),
67-
MAX_L1_TO_L2_MSGS_PER_BLOCK,
6863
messages.length,
6964
);
7065
}

yarn-project/stdlib/src/tests/factories.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -966,11 +966,7 @@ export function makeTxMergeRollupPrivateInputs(seed = 0): TxMergeRollupPrivateIn
966966
export function makeBlockRootFirstRollupPrivateInputs(seed = 0) {
967967
return new BlockRootFirstRollupPrivateInputs(
968968
[makeProofData(seed + 0x1000, makeTxRollupPublicInputs), makeProofData(seed + 0x2000, makeTxRollupPublicInputs)],
969-
new L1ToL2MessageBundle(
970-
makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500),
971-
MAX_L1_TO_L2_MSGS_PER_BLOCK,
972-
MAX_L1_TO_L2_MSGS_PER_BLOCK,
973-
),
969+
new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), MAX_L1_TO_L2_MSGS_PER_BLOCK),
974970
makeAppendOnlyTreeSnapshot(seed + 0x3000),
975971
makeSiblingPath(seed + 0x4000, L1_TO_L2_MSG_TREE_HEIGHT),
976972
makeSiblingPath(seed + 0x5000, ARCHIVE_HEIGHT),
@@ -980,7 +976,7 @@ export function makeBlockRootFirstRollupPrivateInputs(seed = 0) {
980976
export function makeBlockRootSingleTxRollupPrivateInputs(seed = 0) {
981977
return new BlockRootSingleTxRollupPrivateInputs(
982978
makeProofData(seed + 0x1000, makeTxRollupPublicInputs),
983-
new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), 0, 0),
979+
new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), 0),
984980
makeAppendOnlyTreeSnapshot(seed + 0x2800),
985981
makeL1ToL2MessageSponge(seed + 0x3000),
986982
makeSiblingPath(seed + 0x4000, L1_TO_L2_MSG_TREE_HEIGHT),

0 commit comments

Comments
 (0)