Skip to content

Commit 168e5ee

Browse files
committed
test(fast-inbox): streaming inbox e2e latency and mid-checkpoint inclusion (A-1385)
Test-only coverage for the Fast Inbox (AZIP-22) streaming L1→L2 message path (issue A-1385, FI-15) — the behaviours the legacy suite could not express, because pre-flip every message entered at the first block of the *next* checkpoint. Stacked on the node/flip phase: #24784 (A-1379) → #24785 (A-1380) → #24786 (A-1381) → #24787 (A-1382) → #24788 (A-1383) → #24789 (A-1384). Base is `spl/a-1384-flip-streaming-inbox`. ## e2e (`end-to-end`) New `single-node/cross-chain/streaming_inbox.test.ts`, on the proven `CrossChainMessagingTest` fixture (production pipelining sequencer) with a widened slot (36s, 6s blocks → up to ~4 blocks per checkpoint) and `minTxsPerBlock: 0`: - **Mid-checkpoint inclusion** — times a send so the message ages past `INBOX_LAG_SECONDS` partway through a checkpoint's build, then asserts the inserting block has `indexWithinCheckpoint > 0` and that the immediately preceding block did not yet carry the message. Retries with fresh messages if a message happens to age exactly at a checkpoint boundary. - **Latency bound** — asserts, slot-denominated, that `includingBlockTimestamp − messageL1Timestamp ≤ INBOX_LAG_SECONDS + 2·slotDuration` (derived from the deployed constants, not hardcoded), and that it is positive. Wall-clock latency is logged only (A-1178 style), never asserted. - **Message-only block** — on a drained pool, asserts the block that consumes the message carries zero tx effects (the FI-05 zero-tx / non-empty-bundle shape) and that the chain keeps proving past it. - **Send-then-consume on the streaming path** — consumes a streaming-inserted message with a public tx by its compact leaf index and asserts a second consume reverts (double-spend protection); the insert/consume block relationship is logged. ## prover-client New `checkpoint-sub-tree-orchestrator` test for a checkpoint whose L1→L2 messages span multiple blocks (a non-first block carries a bundle). Asserts per-block start/end L1→L2 tree-snapshot continuity and slice partitioning (no gap/overlap; block slice = `[prevBlockLeafCount, blockLeafCount)`) and the `isFirstBlock` flag. Adds `TestContext.makeCheckpointWithMessagesPerBlock` to distribute a bundle across a checkpoint's blocks (the single-block-per-checkpoint `makeCheckpoint` puts every message in the first block). ## Validation - The e2e suite and the `prover-client` orchestrator test **could not be run locally** — the local build is broken in `noir-protocol-circuits-types`/`simulator`/`prover-client` from stale circuit artifacts, and `end-to-end` sits downstream. CI validates both. Every helper, fixture, and API call is grounded on an existing green suite (`l1_to_l2.test.ts`, `cross_chain_public_message.test.ts`) and verified against the current interfaces. No packages that build locally (stdlib, blob-lib, ethereum, foundation) are touched. - Reviewed by codex (gpt-5.6-terra): it caught an incorrect message-sponge-continuity assumption in the prover test (non-first block roots inherit the checkpoint-wide sponge, not the prior block's end sponge); those sponge assertions were removed, leaving the correct L1→L2 tree-snapshot partitioning checks. ## Review follow-up (phase-2 final review) The multi-block-slice orchestrator test now also asserts per-block message-sponge continuity (start empty, each block absorbs exactly its slice, last end equals the InboxParity sponge) and turns its last block into a zero-tx message-only block, exercising the msgs-only block-root wiring fixed in #24789. A stray `async` in the e2e suite was dropped (lint). Replaces #24790.
1 parent 534647d commit 168e5ee

73 files changed

Lines changed: 739 additions & 138 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

l1-contracts/src/core/libraries/compressed-data/CheckpointLog.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct TempCheckpointLog {
3434
bytes32 attestationsHash;
3535
bytes32 payloadDigest;
3636
Slot slotNumber;
37-
// Streaming Inbox consumption counts (AZIP-22 Fast Inbox). `inboxMsgTotal` is the cumulative Inbox message count
37+
// Streaming Inbox consumption counts. `inboxMsgTotal` is the cumulative Inbox message count
3838
// consumed as of this checkpoint (the child's parent-total origin); `inboxConsumedBucket` is the bucket sequence
3939
// number the header's rolling hash corresponds to. Declared next to the slot number so the three share one storage
4040
// slot (4 + 8 + 8 of 32 bytes): propose writes and reads that slot for the slot-progression check anyway.

l1-contracts/src/core/libraries/rollup/EpochProofLib.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ library EpochProofLib {
290290
);
291291
}
292292

293-
// Boundary anchoring for the Inbox rolling-hash chain (AZIP-22 Fast Inbox), mirroring previousArchive/endArchive:
293+
// Boundary anchoring for the Inbox rolling-hash chain, mirroring previousArchive/endArchive:
294294
// both ends of the claimed chain segment must match the rolling hashes recorded at propose for checkpoints
295295
// _start - 1 and _end. The start needs this to be sound - the previous checkpoint's header is not among this
296296
// proof's public inputs, so nothing else pins where the segment begins. The end is already pinned transitively
@@ -343,7 +343,7 @@ library EpochProofLib {
343343

344344
publicInputs[2] = _args.outHash;
345345

346-
// Inbox rolling-hash chain segment consumed across the epoch (AZIP-22 Fast Inbox). The start is validated above
346+
// Inbox rolling-hash chain segment consumed across the epoch. The start is validated above
347347
// against the record written at propose for checkpoint _start - 1; the end is pinned transitively through the
348348
// stored checkpoint header hashes (see the anchoring block in assertAcceptable).
349349
publicInputs[3] = _args.previousInboxRollingHash;

l1-contracts/src/core/libraries/rollup/ProposeLib.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct ProposeArgs {
3434
bytes32 archive;
3535
OracleInput oracleInput;
3636
ProposedHeader header;
37-
// Sequence number of the Inbox bucket the header's `inboxRollingHash` corresponds to (AZIP-22 Fast Inbox).
37+
// Sequence number of the Inbox bucket the header's `inboxRollingHash` corresponds to.
3838
// Unsigned lookup aid kept out of the attested payload digest: a wrong hint can only revert, never change what is
3939
// accepted, since integrity comes from the rolling-hash equality check against the committee-signed header.
4040
uint256 bucketHint;
@@ -269,8 +269,8 @@ library ProposeLib {
269269
uint256 checkpointNumber = tips.getPending() + 1;
270270
tips = tips.updatePending(checkpointNumber);
271271

272-
// Validate the streaming Inbox consumption against the parent checkpoint's consumed position (AZIP-22 Fast
273-
// Inbox). The parent is checkpointNumber - 1, always available: checkpoint 0 carries the {0,0,0} genesis base
272+
// Validate the streaming Inbox consumption against the parent checkpoint's consumed position.
273+
// The parent is checkpointNumber - 1, always available: checkpoint 0 carries the {0,0,0} genesis base
274274
// case written at initialization. rollupStore.tips is not committed until below, so the parent read still sees
275275
// the parent as the pending tip. The returned cumulative total is stored in this checkpoint's record so its
276276
// child validates against it and, since temp-log records rewind with the pending chain on a prune, the record

l1-contracts/src/core/libraries/rollup/STFLib.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ library STFLib {
134134
excessMana: 0, manaUsed: 0, ethPerFeeAsset: _initialEthPerFeeAsset, congestionCost: 0, proverCost: 0
135135
}),
136136
// Genesis Inbox consumption base case, matching the Inbox's genesis bucket-0 sentinel {0, 0, 0}, so
137-
// checkpoint 1 validates its consumption against it (AZIP-22 Fast Inbox).
137+
// checkpoint 1 validates its consumption against it.
138138
inboxRollingHash: bytes32(0),
139139
inboxMsgTotal: 0,
140140
inboxConsumedBucket: 0
@@ -311,7 +311,7 @@ library STFLib {
311311
}
312312

313313
/**
314-
* @notice Retrieves the cumulative Inbox message count consumed as of a checkpoint (AZIP-22 Fast Inbox)
314+
* @notice Retrieves the cumulative Inbox message count consumed as of a checkpoint
315315
* @dev Gas-efficient accessor reading only the streaming-inbox consumed total. Reverts if the checkpoint is stale.
316316
* @param _checkpointNumber The checkpoint number to get the consumed total for
317317
* @return The cumulative Inbox message count consumed as of the checkpoint
@@ -321,7 +321,7 @@ library STFLib {
321321
}
322322

323323
/**
324-
* @notice Retrieves the Inbox rolling hash a checkpoint committed to (AZIP-22 Fast Inbox)
324+
* @notice Retrieves the Inbox rolling hash a checkpoint committed to
325325
* @dev Gas-efficient accessor reading only the streaming-inbox rolling hash. Reverts if the checkpoint is stale.
326326
* @param _checkpointNumber The checkpoint number to get the rolling hash for
327327
* @return The consensus Inbox rolling hash recorded for the checkpoint

l1-contracts/src/core/messagebridge/Inbox.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ contract Inbox is IInbox {
130130
currentTree = trees[inProgress];
131131
}
132132

133-
// Compact cumulative message index (AZIP-22 Fast Inbox): the zero-based position of this message in the Inbox's
133+
// Compact cumulative message index: the zero-based position of this message in the Inbox's
134134
// insertion order, equal to the number of messages inserted before it. It is embedded in the leaf preimage and
135135
// matches the streaming L1-to-L2 tree's leaf count, so consumers do not need per-checkpoint tree geometry.
136136
uint256 index = totalMessagesInserted;

l1-contracts/test/Inbox.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ contract InboxTest is Test {
9393

9494
function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public checkInvariant {
9595
Inbox.InboxState memory stateBefore = inbox.getState();
96-
// Compact cumulative index (AZIP-22 Fast Inbox): the message's index is the count inserted before it.
96+
// Compact cumulative index: the message's index is the count inserted before it.
9797
uint256 globalLeafIndex = stateBefore.totalMessagesInserted;
9898
DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex);
9999

l1-contracts/test/InboxBuckets.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ contract InboxBucketsTest is Test {
132132
recipient: recipient,
133133
content: content,
134134
secretHash: secretHash,
135-
// Compact cumulative index (AZIP-22 Fast Inbox): the first message against a fresh Inbox has index 0.
135+
// Compact cumulative index: the first message against a fresh Inbox has index 0.
136136
index: inbox.getState().totalMessagesInserted
137137
});
138138
bytes32 leaf = Hash.sha256ToField(message);

l1-contracts/test/Rollup.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ contract RollupTest is RollupBase {
470470
interim.feeAmount = interim.manaUsed * interim.minFee + interim.portalBalance;
471471
header.accumulatedFees = interim.feeAmount;
472472

473-
// Streaming Inbox (AZIP-22 Fast Inbox): nothing is seeded here, so reference the genesis bucket (hash 0).
473+
// Streaming Inbox: nothing is seeded here, so reference the genesis bucket (hash 0).
474474
header.inboxRollingHash = bytes32(0);
475475

476476
// Assert that balance have NOT been increased by proposing the checkpoint
@@ -937,7 +937,7 @@ contract RollupTest is RollupBase {
937937
}
938938

939939
// The epoch-proof anchoring pins the rolling-hash chain start to the record written at propose for checkpoint
940-
// start - 1 (AZIP-22 Fast Inbox), mirroring previousArchive. A wrong previousInboxRollingHash must be rejected.
940+
// start - 1, mirroring previousArchive. A wrong previousInboxRollingHash must be rejected.
941941
function testGetEpochProofPublicInputsRejectsWrongPreviousInboxRollingHash() public setUpFor("empty_checkpoint_1") {
942942
_proposeCheckpoint("empty_checkpoint_1", 1);
943943

@@ -965,7 +965,7 @@ contract RollupTest is RollupBase {
965965
}
966966

967967
// The end of the rolling-hash chain segment is pinned to the hash recorded at propose for the epoch's last
968-
// checkpoint (AZIP-22 Fast Inbox), mirroring endArchive. A wrong endInboxRollingHash must be rejected here rather
968+
// checkpoint, mirroring endArchive. A wrong endInboxRollingHash must be rejected here rather
969969
// than surfacing as a generic proof-verification failure.
970970
function testGetEpochProofPublicInputsRejectsWrongEndInboxRollingHash() public setUpFor("empty_checkpoint_1") {
971971
_proposeCheckpoint("empty_checkpoint_1", 1);

l1-contracts/test/RollupFieldRange.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ contract RollupFieldRangeTest is RollupBase {
175175

176176
vm.blobhashes(this.getBlobHashes(full.checkpoint.blobCommitments));
177177

178-
// Streaming Inbox (AZIP-22 Fast Inbox): nothing is seeded here, so reference the genesis bucket (hash 0).
178+
// Streaming Inbox: nothing is seeded here, so reference the genesis bucket (hash 0).
179179
header.inboxRollingHash = bytes32(0);
180180

181181
ProposeArgs memory args =

l1-contracts/test/base/RollupBase.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ contract RollupBase is DecoderBase {
7878
previousArchive: parentCheckpointLog.archive,
7979
endArchive: endFull.checkpoint.archive,
8080
outHash: endFull.checkpoint.header.outHash,
81-
// Anchor the rolling-hash chain start to the record written at propose for checkpoint start - 1 (AZIP-22 Fast
82-
// Inbox). The end value is unchecked on L1 but supplied for completeness.
81+
// Anchor the rolling-hash chain start to the record written at propose for checkpoint start - 1.
82+
// The end value is unchecked on L1 but supplied for completeness.
8383
previousInboxRollingHash: proposedHeaders[startCheckpointNumber - 1].inboxRollingHash,
8484
endInboxRollingHash: proposedHeaders[endCheckpointNumber].inboxRollingHash,
8585
proverId: _prover
@@ -174,7 +174,7 @@ contract RollupBase is DecoderBase {
174174
// Legacy frontier root for the header's inHash field. Unchecked at propose post-flip, but kept because the
175175
// fixtures were generated with it as part of the header hash.
176176
full.checkpoint.header.inHash = rollup.getInbox().getRoot(full.checkpoint.checkpointNumber);
177-
// Streaming Inbox (AZIP-22 Fast Inbox): reference the newest bucket so the checkpoint consumes all messages
177+
// Streaming Inbox: reference the newest bucket so the checkpoint consumes all messages
178178
// seeded above and the mandatory-consumption assert is trivially satisfied (a wrong ref could only revert).
179179
uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq();
180180
full.checkpoint.header.inboxRollingHash = rollup.getInbox().getBucket(bucketHint).rollingHash;

0 commit comments

Comments
 (0)