chore(fast-inbox): umbrella stack for circuits and L1 - #24774
Closed
spalladino wants to merge 63 commits into
Closed
chore(fast-inbox): umbrella stack for circuits and L1#24774spalladino wants to merge 63 commits into
spalladino wants to merge 63 commits into
Conversation
spalladino
requested review from
LeilaWang,
Thunkar and
just-mitch
as code owners
July 17, 2026 18:41
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
2 times, most recently
from
July 17, 2026 20:04
531e71a to
b011406
Compare
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
3 times, most recently
from
July 18, 2026 05:23
b853b87 to
6e642ec
Compare
This was referenced Jul 18, 2026
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
3 times, most recently
from
July 19, 2026 20:48
e1600a5 to
5088258
Compare
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
5 times, most recently
from
July 21, 2026 03:39
5a9c4e0 to
7f41bee
Compare
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
from
July 27, 2026 20:53
7f41bee to
df088ff
Compare
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.
… of append_leaves_to_snapshot (A-1372)
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.
Removing the value constants (NUM_BASE_PARITY_PER_ROOT_PARITY, NUM_MSGS_PER_BASE_PARITY) left an @aztec/constants import whose members are all inline-type, which @typescript-eslint/no-import-type-side-effects rejects. Convert to import type.
ParityPublicInputs requires inHash to be a 31-byte (top-byte-zero) sha frontier root; the InboxParity circuit passes in_hash through unconstrained, so a raw Fr.random() input reappears in the output and fails the constructor check. Compute inHash from the messages instead.
…e_bundle input shape (A-1427) The inbox-parity branch reshapes the block-root private inputs around message_bundle; carry the sample inputs generated at this state so nargo execute passes for the five block-root crates.
…hat 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.
…ker and restart tests (A-1427) The base parity and root parity request types collapsed into a single INBOX_PARITY, so the broker abort/revive tests and the prover-restart parity predicate reference it.
Destructuring the sub-tree proof bundle left the helper's inferred return type unnameable outside the package, so declare it explicitly.
…st blocks (A-1432) The non-first tx-carrying block-root variants (block_root, block_root_single_tx) now take their start L1-to-L2 tree snapshot as a witness input, pinned by block-merge continuity (right.start_state == left.end_state) to the previous block's end state, and assert the tx constants carry their own post-bundle root via validate_l1_to_l2_tree_snapshot_in_constants, exactly like the first-block variants. This lets a public/AVM tx in block N read the messages block N inserts (same-block consumption) instead of only from block N+1. Previously the non-first variants read their start snapshot from constants.l1_to_l2_tree_snapshot and never asserted the post-bundle root, so mid-checkpoint insertions were next-block-visible. Bit-identical pre-flip: non-first bundles are empty, so post-bundle == start == today's constants value, and the new assert passes with unchanged values. The tx-less variants (empty-first, msgs-only) carry no tx constants and already witness their start snapshot, so they are unchanged. Adds negative nargo tests (a non-first block whose constants snapshot differs from its post-bundle root must fail) and mirrors the new witness field through stdlib serialization, the Noir ABI conversion, and the prover-client block-root input builders. VK/artifact regen, checkpoint-root/block-root Prover.toml sample-input regen, and the generated noir-protocol-circuits-types (index.ts) regen ride the mainframe/CI flow; they are not regenerated locally (bb write_vk OOMs on large circuits on dev boxes).
…in the TS BlockConstantData (A-1432)
…minant (A-1432) The same-block input reshape made the private-inputs classes structurally assignable, so the orchestrator's instanceof chain collapsed the union to never (TS2358). Type the block-root selection as a discriminated union and switch on rollupType instead.
… consumption (A-1432) The same-block reshape adds previous_l1_to_l2 to the non-first block-root inputs; regenerate the two affected crates' sample inputs at this state.
…wn terms (A-1432)
…amp-key note (A-1377)
…1377) Raise the constructor guard from `_bucketRingSize > 1` to a floor of 512. The ring must cover the longest stall the chain recovers from on its own: the prune-and-repropose window of 64 checkpoints (2 epochs = 384 L1 blocks) at the natural one-bucket-per-L1-block cadence, so buckets re-consumed after a prune are not overwritten first. 384 rounded up to the next power of two, kept at or below the production ring of 1024. Rework testRingWraparound to exercise a real wraparound against a 512-slot ring and add a negative test asserting construction below the floor reverts.
Move the internal _absorbIntoBucket helper below the external functions so solhint's ordering rule (external before internal) passes. Pure move, no behavior change.
…box ABIs (A-1377) The Inbox MessageSent event gains inboxRollingHash and bucketSeq fields, changing its topic. The examples decode deposit receipts against a hardcoded Inbox ABI, so the stale signature made the event filter come up empty.
…itten-hint test (A-1378)
Place the IInbox import after IRollup so solhint's imports-order rule passes.
… (A-1378) Remove the transitional skip added at the per-block-bundle branch. multi_proof was observed passing in CI at this content (2-CPU, 65s on 2026-07-19) and passes on next/merge-train, so the constrained-CPU slowdown introduced earlier in the stack is resolved by here. The skip stays protective over the intervening branches where we have no passing observation; from this branch upward the test runs normally.
… spec reference (A-1378)
…(A-1378) A bucket keeps absorbing messages for the whole L1 block that opened it, so a reference to the current bucket is not a reference to any lasting snapshot. A lone designated proposer could bundle send(A) -> propose(hash-after-A) -> send(B) into one L1 transaction: L1 accepts the checkpoint, then the trailing send overwrites the bucket in place, leaving the checkpoint committed to a rolling hash that exists neither on L1 nor in any node, since nodes only ever observe a bucket's end-of-block state. No honest node can resolve the consumed position, so the pending chain stalls until the checkpoint is pruned. validateInboxConsumption now requires the referenced bucket to be settled, which is the negation of the Inbox's own rollover condition: the genesis bucket (which never absorbs), a bucket whose L1 block has passed, or a full bucket whose successor takes the next message. The per-bucket cap moves to IInbox.sol so the Inbox and the propose path share one definition without the rollup libraries depending on the Inbox implementation. Honest proposers never reference a bucket younger than INBOX_LAG_SECONDS, so nothing legitimate is excluded.
spalladino
force-pushed
the
spl/a-1378-propose-validation
branch
from
July 28, 2026 12:37
df088ff to
e5284d7
Compare
Contributor
Author
|
Superseded by #25036. The Fast Inbox stack has been regrouped from 20 per-issue PRs (plus 2 umbrellas) down to 3 area PRs plus an umbrella, and rebased onto New structure: #25036 (circuits + L1) → #25037 (node + flip) → #25038 (cleanup), with #25039 as the full-stack umbrella. The original branch for this PR is left on the remote as a recovery point. Closing here to cut the rebase and review overhead of maintaining 22 PRs; the work is not abandoned. |
spalladino
added a commit
that referenced
this pull request
Jul 28, 2026
…387) FI-17 of the Fast Inbox (AZIP-22) stack. Stacked on #24791 (A-1386); it removes the legacy `inHash` from the checkpoint header now that the consensus flip (#24789, A-1384) has switched validation to the streaming rolling hash. After this PR, `inboxRollingHash` is the checkpoint's only inbox commitment. ## Consensus-format change - The checkpoint header loses its `inHash` field across all three preimage layers in lockstep: noir `CheckpointHeader` (`checkpoint_header.nr`), TS `CheckpointHeader` (`stdlib/src/rollup/checkpoint_header.ts`), and Solidity `ProposedHeaderLib` (struct + hash packing). The field ordering is otherwise unchanged; the header shrinks by one field (32 bytes: `CHECKPOINT_HEADER_SIZE_IN_BYTES` 380 to 348, `CHECKPOINT_HEADER_LENGTH` 14 to 13). - The header-hash fixtures were regenerated from `checkpoint_header.test.ts` (the established workflow) and pasted into the noir tests; the p2p golden-byte fixture (`wire_compat_fixtures.ts`) was refreshed for the checkpoint-proposal serialization, which shrinks by exactly the one removed field. The block-proposal wire fixture is unchanged (block-level `inHash` is out of scope, see A-1388). ## Circuits and constants - Strips the dead unconstrained `in_hash` pass-through from the sized `InboxParity<S>` circuit and `ParityPublicInputs` (noir + TS + the hand-written `noir-protocol-circuits-types` conversion wrappers). The circuit is kept (sized-parity, per FI-06's topology decision), so no VK-tree indices move. - Retires `NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP` (replaced everywhere by `MAX_L1_TO_L2_MSGS_PER_CHECKPOINT`, same value 1024) and the now-dead `NUM_MSGS_PER_BASE_PARITY`, `NUM_BASE_PARITY_PER_ROOT_PARITY`, and `L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH`. Generated constants (`constants.gen.ts`, `ConstantsGen.sol`) were regenerated via `remake-constants`. - Deletes `FrontierLib.sol` and its tests (`Parity.t.sol`, the `Frontier` harness and merkle test) now that their last users are gone. ## Verification - Noir: `types::checkpoint_header` (5/5), `rollup_lib::parity` and `checkpoint_root::parity_tests` (15/15), and the checkpoint-root header-assembly path pass locally. The full `checkpoint_root` suite (68 tests) is too slow to run in full locally and rides CI. - Solidity: `forge test` green across `Rollup`, `RollupFieldRange`, `Inbox`, `InboxBuckets`, `RollupGetters` (76/76), covering header hashing and the regenerated checkpoint fixtures. - Jest: `checkpoint_header`, the p2p wire-compat suites, and the parity/messaging suites pass (43 tests), confirming the fixture regeneration is byte-consistent. - VK/artifact/Prover.toml regeneration and the generated Noir ABI types ride CI (local `yarn build` in `noir-protocol-circuits-types` / `simulator` / `prover-client` is expected to fail on stale circuit artifacts until then); e2e and the broken-zone typecheck are the CI-of-record for those. ## Stack Part of the Fast Inbox stack #24784 through #24791 (umbrella #24774): A-1379 (#24784), A-1380 (#24785), A-1381 (#24786), A-1382 (#24787), A-1383 (#24788), A-1384 flip (#24789), A-1385 (#24790), A-1386 (#24791, base of this PR). Resolves A-1387. ## l1 publisher inbox consumption via the streaming selector (CI fix, pass-8) The A-1387 inHash-removal rewrite left `l1_publisher.integration.test.ts` hardcoding `previousInboxRollingHash = Fr.ZERO` and `bucketHint = 0n` while consuming real L1→L2 messages, so message-consuming checkpoints reverted on L1 with `Rollup__InvalidInboxRollingHash` / `UnconsumedInboxMessages` (masked every prior CI round behind earlier failures). `INBOX_LAG_SECONDS` is an L1-time censorship cutoff, not whole checkpoints, so no checkpoint-depth shift register reproduces the aged bucket set. The test now mirrors the real Inbox buckets into its `MockL1ToL2MessageSource` and reuses 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. Box: 13/13, verified at a-1387 and a-1388. Replaces #24792.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Umbrella / consolidated PR for the Fast Inbox (AZIP-22) work.
This PR spans the entire Fast Inbox 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 of everything.What it contains, at a high level
The stack lands the circuit and L1 (Solidity + archiver/TS) changes for Fast Inbox:
rollup-lib; the sha256inboxRollingHashchain threaded end-to-end (dual with the legacyinHash); per-block L1-to-L2 message bundles with parity moved to the checkpoint root; ablock_root_msgs_only_rollupcircuit for no-tx blocks that still carry messages; a single variable-sizeInboxParity<S>proof per checkpoint (replacing the parity base ×4 + parity root fan-in); and same-block L1-to-L2 message consumption for non-first blocks (every block'sBlockConstantData.l1_to_l2_tree_snapshotis its own post-bundle root).Inboxalongside the existing frontier trees, and the (still-unwired) streaming propose-time consumption validation (validateInboxConsumption).Contained PRs (stack order, bottom → top)
Review and land the individual PRs above; this umbrella exists only to consolidate CI and provide a full-stack diff.