feat(fast-inbox): streaming inbox node integration, flip, and cleanup (umbrella)#24796
feat(fast-inbox): streaming inbox node integration, flip, and cleanup (umbrella)#24796spalladino wants to merge 137 commits into
Conversation
Pure library components for the Fast Inbox (AZIP-22), consumed by nothing yet: new MAX_L1_TO_L2_MSGS_PER_BLOCK / MAX_L1_TO_L2_MSGS_PER_CHECKPOINT constants, a variable-length frontier-based append to an AppendOnlyTreeSnapshot at arbitrary (non-aligned) indices, an absorb-only poseidon message-bundle sponge (L1ToL2MessageSponge), and the rolling sha256 chain helper (accumulate_inbox_rolling_hash) matching the L1 truncated-to-field policy. No circuit interface changes.
Replaces the per-leaf frontier walk (MaxLeaves x TreeHeight poseidon hashes) with a level-by-level batched merge: the batch is prepended with the pending left sibling when it starts as a right child, dangling odd nodes become the new frontier entries, and remaining nodes are paired into the next level with lane bounds halving per level. Total cost drops to ~MaxLeaves + 3 x TreeHeight hashes (1,166 for 1024 leaves in the height-36 tree vs ~36,900 before). Same signature, semantics, and error messages. Adds an exhaustive small-tree sweep over every (start, num) combination and a fail-closed test for appending to a completely full tree.
Adds the Fast Inbox (AZIP-22) rolling-hash commitment as a dual of the
legacy inHash, flowing through parity circuits, block/checkpoint rollup
public inputs, the checkpoint header, L1, and the node/prover.
Circuits (noir):
- Parity base absorbs only real leaves into a truncated-to-field sha256
chain (accumulate_inbox_rolling_hash, guarded by num_msgs +
assert_trailing_zeros); merkle roots keep absorbing padding. Parity
public inputs gain start_rolling_hash / end_rolling_hash / num_msgs;
parity root threads the four base segments sequentially and sums counts.
- BlockRollupPublicInputs and CheckpointRollupPublicInputs carry a
{start,end} pair propagated exactly like in_hash (first block root sets
it, merges take left's, validate asserts right's is zero). Checkpoint
merge asserts right.start == left.end (decision 11). CheckpointHeader
gains inbox_rolling_hash (the end value) immediately after in_hash.
- Root rollup public inputs are unchanged: exposing the epoch pair is
inseparable from the L1 epoch-proof anchoring that is out of scope here
(FI-08/FI-14), so it is deferred with the rest of L1 anchoring.
TS: stdlib inbox_rolling_hash mirror (unit-tested against the FI-02
vectors), CheckpointHeader / parity / block / checkpoint PI classes,
noir-protocol-circuits-types conversions, orchestrator threading of
per-base start hashes + counts, and node header population at the
sequencer / prover / validator sites (sourced from the previous
checkpoint header via getPreviousCheckpointInboxRollingHash).
L1: ProposedHeader + ProposedHeaderLib.hash pack inboxRollingHash after
inHash (no new propose checks); test fixtures + headerHash regenerated.
Add the {previous, end} inbox rolling-hash pair to RootRollupPublicInputs so
the epoch's consumed chain segment is carried through to proof verification.
The root rollup sources the pair from the merged checkpoint public inputs;
continuity within the range is already enforced by the checkpoint merges.
On L1, PublicInputArgs gains previousInboxRollingHash/endInboxRollingHash and
EpochProofLib places them at public-input positions 3 and 4 (header hashes,
fees, constants and blob inputs shift by two). Both values are deliberately
unvalidated until the Fast Inbox flip, when they will be checked against the
per-checkpoint records written at propose; for now they are pass-through only.
Also fixes two ivc-integration benchmark generators whose hand-built parity
base inputs were missing start_rolling_hash/num_msgs.
…1373) Exposing the inbox rolling hash on the parity-root and rollup public inputs changed the ABI of the block-root-first variants, block-merge, checkpoint-merge and root circuits, but their committed crates/rollup-*/Prover.toml sample inputs were not refreshed. CI runs `nargo execute` against these fixtures, so all six failed to deserialize (e.g. missing parity_root.public_inputs.start_rolling_hash and previous_rollups[].public_inputs.start_inbox_rolling_hash). Regenerate the six stale fixtures. The circuits push their serialized inputs via pushTestData when run through the prover, so a new prover-client test drives representative epochs through the simulated orchestrator and dumps the captured inputs, replacing the dead orchestrator_single_checkpoint.test.ts regeneration path. The suite is skipped unless AZTEC_GENERATE_TEST_DATA=1 is set.
A-1373 changes the rollup public-input ABIs, so the base branch's committed pinned-build.tar.gz no longer matches the compiled circuits. Remove it rather than carry a stale archive: bootstrap recompiles from source whenever the pin is absent, and the pin is regenerated once the ABI settles.
getEpochProofPublicInputs sources the fee recipient/value from the supplied checkpoint headers but never validated that they hash to the stored headers, so a mismatch only reverted on the on-chain submit path instead of when an off-chain caller assembles the public inputs. Call verifyHeaders in the getter as well, and import ProposedHeaderLib in Rollup.t.sol so the test that exercises this path compiles.
7b88209 to
de28e00
Compare
…h header (A-1373) The checkpoint header now carries inboxRollingHash, changing the checkpoint hash preimage. The stored format is unchanged (read-defaultable), so only the snapshot is regenerated; no PXE_DATA_SCHEMA_VERSION bump.
…ng hash (A-1373) The proposal handler, sequencer job, and prover node now source the parent checkpoint's inboxRollingHash. Serve zeroed parent headers from the mocks so scenarios beyond the genesis checkpoint resolve their chain start: the validator suite stubs the proposed-checkpoint fallback (getCheckpointData doubles as the already-published existence check), the sequencer suite stubs getCheckpointData, and the prover-node suite serves synthetic ancestors below the mined window.
a6dbe2a to
bd8896e
Compare
f4a62b3 to
147b7ff
Compare
…f types (A-1427) The legacy L1-to-L2 tree gated block roots on parity outputs, so the broker's type-major priority still progressed one epoch at a time. The streaming inbox parity only gates the checkpoint root: under sustained block production the type-major order kept serving younger epochs' block roots and never scheduled INBOX_PARITY, stalling every epoch at awaiting-root. Select the oldest-epoch job across the allowed queues and tie-break by proof-type priority.
…uits (A-1387) Remove the dead unconstrained in_hash pass-through from InboxParity and ParityPublicInputs, and remove in_hash from the CheckpointHeader consensus format (struct, to_be_bytes, serialization length, Empty, hash fixtures). The Inbox rolling hash is now the checkpoint's only inbox commitment. Retire NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP in favor of MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, and drop the now-unused L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH and the NUM_MSGS_PER_BASE_PARITY / NUM_BASE_PARITY_PER_ROOT_PARITY frontier fan-in constants. Regenerated constants.gen.ts and ConstantsGen.sol via remake-constants.
…te FrontierLib (A-1387) Remove inHash from the ProposedHeader struct and its hash preimage in ProposedHeaderLib, keeping the packing byte-for-byte in lockstep with the noir and stdlib checkpoint headers. Update the decoder base struct and the propose test helpers, and regenerate the checkpoint fixtures (drop header.inHash, recompute headerHash). Delete FrontierLib and its only remaining users: the parity cross-check test (Parity.t.sol, which verified the deleted parity circuits) and the frontier merkle test/harness. The frontier tree is gone from both circuits and L1.
…h stdlib and node (A-1387) Mirror the consensus-format change in the stdlib CheckpointHeader (struct, serialization, viem, hash fixtures) and the circuit-ABI stdlib types (ParityPublicInputs, InboxParityPrivateInputs) and their noir conversion mappings. Sweep the header-inHash populate/read sites: the lightweight checkpoint builder, the checkpoint-proving orchestrator, the sequencer fixture writer, and the checkpoint proposal (its block-level inHash now sources zero rather than the removed header field). Regenerate the checkpoint p2p wire fixture for the shorter header. Replace NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP with MAX_L1_TO_L2_MSGS_PER_CHECKPOINT across the TS consumers. The block-level inHash struct fields (BlockProposal, L2Block) and stdlib/src/messaging/in_hash.ts remain for the node cleanup issue.
…oval (A-1387) The propose-failure test asserted the deleted Rollup__InvalidInHash selector (0xcd6f4233). With the legacy inHash L1 check gone, a checkpoint carrying messages that do not match the Inbox now reverts at validateInboxConsumption with Rollup__InvalidInboxRollingHash (0xed1f7bb5).
… checkpoint header (A-1387) Dropping inHash from the checkpoint header changes the checkpoint hash preimage, so the checkpoint ids the tips store persists change value. The stored format is unchanged (read-defaultable), so only the snapshot is regenerated; no PXE_DATA_SCHEMA_VERSION bump.
…reaming bucket selector (A-1387) The A-1387 rewrite that removed inHash left this test's inbox handling half-adapted: it hardcoded previousInboxRollingHash to Fr.ZERO and proposed with bucketHint 0n while consuming real L1->L2 messages, so message-consuming checkpoints reverted on L1 with Rollup__InvalidInboxRollingHash / UnconsumedInboxMessages. INBOX_LAG_SECONDS is an L1-time censorship cutoff (not whole checkpoints), so no checkpoint-depth shift register can reproduce the aged bucket set. Mirror the real Inbox buckets into the test's MockL1ToL2MessageSource, then reuse the production selectInboxBucketForBlock (which mirrors ProposeLib.validateInboxConsumption) to pick exactly the buckets each checkpoint must consume, deriving the consumed bundle, the propose bucket hint, and the header rolling hash from that one selection so header, world state, and L1 agree. Matches the A-1388 rewrite of this file so the segment rebase dissolves to zero.
Removes the legacy inboxLag / AZTEC_INBOX_LAG config field and env var from ethereum config, foundation env vars, the network-consensus-config list, the l1-contracts and spartan network defaults, and the e2e test option plumbing. Also drops the removed inHash argument from the e2e web3signer createBlockProposal wrapper.
…ers from stdlib/p2p (A-1388) Deletes computeInHashFromL1ToL2Messages and the inHash field from BlockProposal (including its signed-payload slot), the CheckpointProposal getBlockProposal pass-through, and the createBlockProposal inHash argument on the validator interface. Removes the padded per-checkpoint InboxLeaf helpers (smallestIndexForCheckpoint / indexRangeForCheckpoint / checkpointNumberFromIndex) and the legacy getL1ToL2Messages(checkpointNumber) member from the L1ToL2MessageSource and archiver RPC interfaces. Regenerates the block-proposal golden wire fixtures for the shrunk payload; the p2p wire format changes at a release boundary (fresh networks), matching the A-1381 optional-tail precedent.
…aths in the archiver (A-1388) Removes the legacy getL1ToL2Messages(checkpointNumber) flow, the padded per-checkpoint index invariants, the inboxTreeInProgress readiness gate, and the L1ToL2MessagesNotReadyError. InboxMessage drops the 128-bit keccak rollingHash and the vacuous derived checkpointNumber, so messages carry only the compact global index and the full-width consensus rolling hash; the store serialization changes and ARCHIVER_DB_VERSION bumps to 9 (nodes resync, no migration). Reorg detection now compares the local consensus rolling hash and total against the Inbox's current rolling-hash bucket via new getBucket/getCurrentBucketSeq/getCurrentBucket wrappers, replacing the 128-bit getState comparison. Test fakes/mocks move to compact indexing.
…lidator (A-1388) Removes the dead inHash=Fr.ZERO plumbing through the checkpoint proposal job and the validator/validation-service createBlockProposal path, and deletes the dead in_hash_mismatch validation-failure reason from proposal_handler, validator, and metrics. Updates the sequencer/validator test mocks and callers for the dropped inHash argument and the removed getL1ToL2Messages message source member.
…ator message fetch (A-1388) Drops the no-op first-in-checkpoint padding alias and its stale docs in world-state, and removes the obsolete non-first-block-empty-bundle transitional test. The node public-calls simulator no longer fetches next-checkpoint messages via the removed per-checkpoint API and drops its now-unused l1ToL2MessageSource dependency, simulating against the fork's current tree (streaming Inbox consumes per block). Rewrites the THREAT_MODEL inHash/consume passages to the consensus rolling-hash / bucket model.
…ts (A-1388) The optional bucket-reference tail keeps proposals that omit it round-tripping cleanly; it does not make the wire byte-identical to the pre-inHash-removal format. Reword the toBuffer/fromBuffer comments to describe only the tail's unset case.
… tests (A-1388) The env sweep removed AZTEC_INBOX_LAG from scripts/network-defaults.json but both deploy-script test setUps still readUint the key, which reverts (vm.parseJsonUint on a missing path), failing the suites before any test runs. The deploy configuration no longer consumes the env var.
…proposal formats (A-1388) The attestation store persists raw BlockProposal and CheckpointAttestation buffers. Both changed shape in this stack (the checkpoint header lost inHash and the block-proposal wire format dropped its zeroed inHash), and stored checkpoint attestations are decoded without a tolerant fallback, so a store written by a pre-Fast-Inbox node would throw on read. Bump 2 -> 3 to wipe stale pools, matching the archiver's no-migration bump.
…388) Removes the dead AZTEC_INBOX_LAG entries from the spartan environment profiles and the governance-upgrade tutorial, rewrites the validator and sequencer README sections that still described the legacy inHash flow, and drops e2e comments that still explained fixture settings in terms of the deleted inboxLag / L1ToL2MessagesNotReadyError behavior. Versioned docs snapshots are left untouched.
c6ea5bc to
7d1d7d3
Compare
…mple inbox ABIs (A-1386)
…ts generator (A-1434) Add INBOX_LAG_SECONDS and MAX_L1_TO_L2_MSGS_PER_CHECKPOINT to the Solidity constants allowlist so the generator emits them into ConstantsGen.sol, matching the values already present in constants.gen.ts. ProposeLib and its consumption test now read the generated Constants library instead of the hand-declared file-scope copies, giving L1 and TS a single source of truth.
…neration (A-1435) Make the prover-client regenerate_rollup_sample_inputs suite the sole owner of every block-root, block-merge, checkpoint, tx-merge, and root rollup Prover.toml, and stop the e2e full.test dump from writing the block-root and checkpoint samples it overlapped on. The two paths previously both wrote rollup-block-root-first, rollup-block-root-first-single-tx, rollup-checkpoint-root-single-block, rollup-checkpoint-merge, and rollup-root, so the committed fixtures drifted depending on which ran last. The e2e dump now regenerates only what needs real client-proved transactions the simulated orchestrator cannot produce: the private-kernel circuits and the transaction-base rollups. A dedicated three-tx scenario restores rollup-tx-merge coverage (dropped when orchestrator_single_checkpoint.test.ts was replaced), so the suite covers every rollup circuit at or above the transaction merge. Update the regen docs (barretenberg cpp CLAUDE.md, update-prover-toml and gate-counts skills) to the two-command split and drop the dead orchestrator_single_checkpoint references.
…en and fix stale regen command patterns (A-1435) Post-flip a zero-tx non-first block carrying a message bundle is a live block shape routed through the msgs-only block root, so the regen suite gains a scenario with a per-block message distribution that dumps rollup-block-root-msgs-only, and the circuit joins the CI nargo-execute list. The first regen run creates crates/rollup-block-root-msgs-only/Prover.toml, which must be committed alongside the other regenerated tomls. Also fixes the documented e2e regen command: the prover full test lives at single-node/prover/server/full.test.ts, so the old e2e_prover/full.test jest pattern matches nothing.
…-1435) makeCheckpoint concentrates every scenario message into the first block, whose bundle pads to MAX_L1_TO_L2_MSGS_PER_BLOCK (256), so seeding scenarios with the per-checkpoint cap (1024) threw on regeneration. Also distribute messages across both blocks of the rollup-block-root scenario so the committed non-first block-root sample carries a non-empty bundle.
7d1d7d3 to
9e12351
Compare
spalladino
left a comment
There was a problem hiding this comment.
Got as far as sequencer-client (excluded)
| // The fee recipient/value below are sourced from the supplied headers, so the header hashes must be validated here | ||
| // as well as on the submit path - otherwise an off-chain caller of this getter would assemble public inputs from | ||
| // unverified fee fields and only discover the mismatch when the on-chain proof reverts. | ||
| verifyHeaders(_start, _end, _headers); |
There was a problem hiding this comment.
Why wasn't this needed before?
| bytes32 expectedPreviousInboxRollingHash = STFLib.getInboxRollingHash(_start - 1); | ||
| require( | ||
| expectedPreviousInboxRollingHash == _args.previousInboxRollingHash, | ||
| Errors.Rollup__InvalidPreviousInboxRollingHash( | ||
| expectedPreviousInboxRollingHash, _args.previousInboxRollingHash | ||
| ) | ||
| ); |
There was a problem hiding this comment.
Why don't we need to do the same for the endInboxRollingHash?
| v.header.inboxRollingHash, | ||
| _args.bucketHint, | ||
| v.header.slotNumber, | ||
| STFLib.getInboxMsgTotal(checkpointNumber - 1) |
There was a problem hiding this comment.
This is requiring yet another SLOAD, right? Any way we can optimize that?
| bytes32 inboxRollingHash; | ||
| uint64 inboxMsgTotal; | ||
| uint64 inboxConsumedBucket; |
There was a problem hiding this comment.
These are two extra SSTOREs. How do these compare vs what we were storing on the Inbox?
| function testFuzzSendAndConsume( | ||
| DataStructures.L1ToL2Msg[] memory _messagesFirstBatch, | ||
| DataStructures.L1ToL2Msg[] memory _messagesSecondBatch, | ||
| uint256 _numTreesToConsumeFirstBatch, | ||
| uint256 _numTreesToConsumeSecondBatch | ||
| ) public { |
There was a problem hiding this comment.
Do we have an equivalent fuzzing test in the new suite? If not, let's add it.
| // Streaming Inbox (AZIP-22 Fast Inbox): messages are inserted per block via `addBlock`, so `l1ToL2Messages` here | ||
| // is empty and the up-front checkpoint-wide insertion is skipped. | ||
| insertMessagesPerBlock: boolean = false, |
There was a problem hiding this comment.
We're post-flip now. No need for a flag to control pre vs post flip behaviour. Just write the code for post-flip.
| @@ -165,14 +177,15 @@ export class LightweightCheckpointBuilder { | |||
| public async addBlock( | |||
| globalVariables: GlobalVariables, | |||
| txs: ProcessedTx[], | |||
| opts: { insertTxsEffects?: boolean; expectedEndState?: StateReference } = {}, | |||
| opts: { insertTxsEffects?: boolean; expectedEndState?: StateReference; l1ToL2Messages?: Fr[] } = {}, | |||
There was a problem hiding this comment.
l1ToL2Messages should not be an "opt", it's now a first-class thing we add to each block. Promote it to a proper argument.
| case 'rollup-block-root-first': | ||
| return this.prover.getBlockRootFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); | ||
| case 'rollup-block-root-first-single-tx': | ||
| return this.prover.getBlockRootSingleTxFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); | ||
| case 'rollup-block-root-first-empty-tx': | ||
| return this.prover.getBlockRootEmptyTxFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); |
There was a problem hiding this comment.
I should've surfaced this during the circuits review (probably should funnel this comment to the circuits agent), but why do we still need the first flavor of block root circuit? Can't we merge it with the others? Isn't the only difference whether it's allowed to be fully empty?
| blockProofs: Promise<{ | ||
| blockProofOutputs: PublicInputsAndRecursiveProof< | ||
| BlockRollupPublicInputs, | ||
| typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH | ||
| >[]; | ||
| inboxParityProof: PublicInputsAndRecursiveProof<ParityPublicInputs>; | ||
| }>; |
There was a problem hiding this comment.
Should either rename blockProofs, or split this into two different fields, one for blockProofs and one for inboxParityProof.
| private async deriveCheckpointConsumedMessages(previousBlockHeader: BlockHeader, lastBlock: L2Block): Promise<Fr[]> { | ||
| const startLeafCount = BigInt(previousBlockHeader.state.l1ToL2MessageTree.nextAvailableLeafIndex); | ||
| const endLeafCount = BigInt(lastBlock.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); | ||
| if (endLeafCount <= startLeafCount) { | ||
| return []; | ||
| } | ||
| const startBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(startLeafCount); | ||
| const endBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(endLeafCount); | ||
| if (startBucket === undefined || endBucket === undefined) { | ||
| throw new Error( | ||
| `Cannot resolve consumed messages for checkpoint ending at block ${lastBlock.number} from the Inbox buckets`, | ||
| ); | ||
| } | ||
| return this.l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets(startBucket.seq, endBucket.seq); | ||
| } |
There was a problem hiding this comment.
"Get all message between these indices" (ie between startLeafCount and endLeafCount) should probably be a method of the messageStore, so clients don't need to bother with explicit buckets.
Umbrella / consolidated PR for phase 2 of the Fast Inbox (AZIP-22) work.
This PR spans the phase-2 stack — from the topmost branch down to
merge-train/spartan— so CI runs once over the combined diff and reviewers get a single consolidated view. It sits on top of the phase-1 (circuits + L1) umbrella #24774: until the phase-1 PRs merge, this diff includes them too.What it contains, at a high level
Phase 2 lands the node integration, the consensus flip, the streaming e2e coverage, and the legacy-path cleanup for the Fast Inbox:
Rollup.proposevalidates streaming inbox consumption against per-checkpoint temp-log records (genesis{0,0,0}),ProposeArgsgains an unsignedbucketHint,MAX_L1_TO_L2_MSGS_PER_BLOCKdrops 1024 → 256, every block carries its own L1-to-L2 tree root in the blob, the prover slices a checkpoint's messages per block at compact indices, and thestreamingInboxflag is removed — streaming is the only path.consume(),LAG), the checkpoint header'sinHash(noir/TS/Solidity in lockstep), the node's legacy per-checkpoint message paths and the 128-bit sync hash (node reads and, via the leftover sweep, the L1 field and event arg too), theAZTEC_INBOX_LAGenv sweep, inbox constants emitted from the generator, and a single canonical path for rollup sample-input regeneration.Contained PRs (stack order, bottom → top)
inHash(A-1387)Merge notes
Prover.tomlsample inputs were regenerated on a bb-capable box and committed at the top of the stack (bbfaf57daa), including the newrollup-block-root-msgs-only/Prover.toml— the circuit joined the regen scenarios and the CInargo executelist in this stack. All regenerated inputs passnargo execute.ARCHIVER_DB_VERSION7 → 9 across the stack and the p2p attestation store 2 → 3; no migrations — nodes resync (fresh rollup instance per release line).inHash(preimage changes in noir/TS/Solidity lockstep, fixtures regenerated), the block-proposal p2p wire format drops its zeroedinHash, the per-block blob encoding always carries the L1-to-L2 root, and the per-block message cap is 256. All are release-boundary changes for fresh networks.EpochProofExtLibsplit keepingRollupOperationsExtLibdeployable post-flip, streaming mocks for the validator/prover-node unit suites, a pxe L2 tips snapshot regen for the inHash-less header (read-defaultable, no schema bump), and docs-example Inbox ABIs kept in lockstep with theMessageSentevent.ProvingJobResultZod union was missing aBLOCK_ROOT_MSGS_ONLY_ROLLUPmember (feat(fast-inbox): add block_root_msgs_only_rollup circuit for no-tx blocks with messages (A-1375) #24612 / A-1375, where the request type is introduced), which stalled epochs decoding a message-only block-root result; and the proposebucketHintwas lost when a checkpoint's final block failed to build after a message-only first block consumed a bucket (feat(fast-inbox): flip consensus to streaming inbox (A-1384) #24789 / A-1384). Both carry unit-level round-trip / RED-GREEN regression tests. The relocated proving-scheduling test (base of the stack) also swapped its parity reference forPUBLIC_VM: no parity name type-checks across the whole stack (INBOX_PARITYexists only at A-1427+,PARITY_BASEonly at A-1374/A-1375, since A-1427 collapsesPARITY_BASE/PARITY_ROOTintoINBOX_PARITY), whereasPUBLIC_VMis stable everywhere and stays lower-priority thanPRIVATE_TX_BASE_ROLLUP. Verified with realtscat A-1374, A-1384, and the top.'latest'tip while these suites anchor the PXE to'checkpointed'. Post-flip per-block inbox insertion lets the proposed chain reach a message's consume-checkpoint a checkpoint before it is published, so readiness flipped true while the PXE had not synced the message and the private consume simulated too early — a deterministic failure onl1_to_l2.test.tsfrom the flip up. The helper now gates on the configured PXE sync tip (CrossChainMessagingTest.pxeSyncChainTip, defaulting to'latest'). Test-only, unchanged A-1384→top; box-verified green on all three cross-chain suites (l1_to_l2,streaming_inbox,l1_to_l2_inbox_drift).block state does not match world state) on the first cross-chain-consuming block. Post-flip the blob carries a per-block root; reconstruction now uses each block's own. Surfaced ascross_chain_public_message+token_bridge(only followers reconstruct via the archiver, so validation never caught it). Adata_retrieval.test.tsunit pins per-block reconstruction; both e2es green at a-1384. A full box sweep of every L1→L2/inbox/cross-chain/proving e2e at a-1384 is now green.streaming_inbox.test.tstests 1–2 (mid-checkpoint inclusion, latency bound) flake on their first CI runs, add tighterror_regexentries to.test_patterns.ymlfor the specific assertion rather than broad skips. None are pre-added.Review and land the individual PRs above; this umbrella exists only to consolidate CI and provide a full-stack diff.
Pass-8 CI fixes (test-only, both surfaced by fail-fast unmasking): (1) the world-state
integration.test.tsmock archiver never registered Inbox buckets, so the post-flip synchronizer reconstructed empty bundles and diverged (block state does not match world state) — fixed at A-1384 by registering genesis + per-checkpoint buckets inMockPrefilledArchiver.setPrefilled; (2) thel1_publisher.integration.test.tsinbox-consumption modeling (hardcoded rolling hash / bucket hint, then a wrong checkpoint-depth shift register) reverted on L1 — fixed at A-1387 by mirroring the real Inbox buckets and reusing the productionselectInboxBucketForBlock(INBOX_LAG_SECONDS is an L1-time cutoff). A-1388's own rewrite of that file dissolves into the A-1387 version. Box-verified: world-state 12/12 (a-1384, top), l1_publisher 13/13 (a-1387, a-1388); full tsc green at a-1384/a-1387/a-1388/top.Leftover sweep (post-review of this umbrella diff): five flip/cleanup leftovers found while reviewing this diff were fixed in their owning branches. The legacy 128-bit keccak inbox rolling hash is now fully removed on L1 (
InboxState.rollingHash, theMessageSentbytes16arg, its keccak accumulation, the four Solidity test suites asserting it, and the three live docs-example inbox ABIs) at A-1388 — the earliest branch whose TS no longer reads it. The staleIRollup.solPublicInputArgsnatspec now describes the implemented epoch-proof anchoring (A-1386). The inertisFirstBlockblob-fixture parameter is removed (A-1384). The deadInboxLeafclass is deleted (A-1388). The checkpoint-builder comments now mark up-front whole-checkpoint message insertion as test-only (A-1388).Gas benchmarks refreshed (top of stack):
gas_report.json,gas_benchmark.md, andgas_benchmark_results.jsonregenerated via./bootstrap.sh gas_report/gas_benchmarkat the pinned foundry v1.4.1. Headlines:sendL2Messagemean gas 53,020 → 44,796 (−15.5%),Inboxbytecode −19%;propose+~4% mean from thebucketHintcalldata and the added inbox-consumption validation. Much of the raw diff is pre-existing baseline staleness (the committed report predated the block→checkpoint rename and theOutbox.getRootDatasignature change), now corrected. Known quirk for future regens: threeRollup.t.soltests (testExtraBlobs,testRevertInvalidCoinbase,testRevertInvalidTimestamp) fail only underFORGE_GAS_REPORT=true— a Foundryvm.expectRevertcall-depth interaction with theIInbox.getBucket()staticcall inProposeLib.validateInboxConsumption, invisible to normal CI; a real fix or a--no-match-testentry inbootstrap.sh gas_reportis a follow-up.