Skip to content

feat: rolling-hash buckets in the Inbox alongside frontier trees (A-1377)#24617

Draft
AztecBot wants to merge 2 commits into
merge-train/spartan-v6from
cb/a-1377-inbox-buckets
Draft

feat: rolling-hash buckets in the Inbox alongside frontier trees (A-1377)#24617
AztecBot wants to merge 2 commits into
merge-train/spartan-v6from
cb/a-1377-inbox-buckets

Conversation

@AztecBot

@AztecBot AztecBot commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

AZIP-22 Fast Inbox, FI-07. First L1 PR of the series; a follow-up stacks the propose-side streaming validation on top of this branch.

The first commit copies the small L1 diffs already carried by the circuits stack (#24587#24600#24603#24612) — the header's inboxRollingHash field, the two epoch-proof public inputs, and the regenerated checkpoint fixtures — so this branch and that stack merge cleanly into merge-train/spartan-v6 in either order.

What

sendL2Message additionally maintains the consensus rolling hash — the truncated-per-link sha256 chain the circuits recompute (h' = sha256ToField(h || leaf), genesis zero; new Hash.accumulateInboxRollingHash) — and snapshots it into a fixed-size ring of buckets. Frontier trees, LAG, the legacy consume() flow, and the legacy bytes16 keccak rolling hash are untouched (the legacy hash now carries a TODO to remove it at cleanup); buckets are completely inert for the legacy flow.

Per the pinned design decisions:

  • Bucket struct {rollingHash | totalMsgCount: uint64, timestamp: uint64, msgCount: uint32} packs into two slots; totalMsgCount is the Inbox-wide cumulative count (what the censorship cap-escape check reads), timestamp is the bucket's opening L1 block timestamp (recency checks are done in seconds), msgCount sits in slot 2's spare bits so the per-bucket cap costs no extra storage access.
  • Ring indexed by dense bucket sequence number (seq % BUCKET_RING_SIZE), 1024 entries in production, sized as an immutable constructor parameter. getBucket(seq) reverts with Inbox__BucketOutOfWindow outside the live window (seq <= current < seq + ringSize, same idiom as the Rollup's roundabout). Overwrite protection for unconsumed buckets is deliberately not enforced yet (happy path assumes the ring never wraps into live data).
  • Bucket boundaries: a bucket only holds messages from a single L1 block; the first message of a new block opens the next bucket, and the 257th message within one block (MAX_MSGS_PER_BUCKET = 256, the post-flip per-L2-block insertion cap) rolls over into the next bucket with the same timestamp. New buckets inherit the rolling hash and cumulative count, so the chain is continuous across buckets.
  • Genesis base case: bucket 0 is {0, 0, deployTime} and never absorbs (even for a message sent in the deployment block), so a checkpoint consuming nothing against an empty Inbox can always reference a bucket matching its parent's chain position — no special case at propose.
  • Event: MessageSent gains bytes32 inboxRollingHash and uint256 bucketSeq after the legacy args.

Archiver / TS

The generated InboxAbi picks up the new event signature at build time; MessageSentArgs and the decode in ethereum/src/contracts/inbox.ts carry the two new fields, and the archiver keeps relying only on the legacy args for now (InboxMessage unchanged). The archiver test fake fills the new fields with placeholders. Nothing on this branch consumes the new values yet — the node-side cross-check of its TS rolling hash against L1-emitted values comes with the archiver bucket-sync work.

Testing

  • New InboxBuckets.t.sol covers the roadmap done-when: accumulation within a block, snapshot freezing at block boundaries with chain continuity into the next bucket, 256-cap rollover within one block, ring wraparound + out-of-window reverts on a small ring, the genesis bucket (including a first message in the deploy block), and event contents.
  • Shared rolling-hash test vectors pinned in feat: message-bundle components in rollup-lib (A-1372) #24587's noir tests (chain(0,[11]), chain(0,[11,22,33]), chain(0,[1..=256]), chain(0x2a,[7]), chain(0x2a,[7,8])) are asserted against the L1 implementation, so L1, TS, and the circuits provably compute the same chain.
  • Full forge test suite: 878 passed, 0 failed (7 pre-existing skips). Existing MessageSent expectations in Inbox/TokenPortal/FeeJuicePortal tests updated for the extended event.
  • The TS change is decode-only and mechanical; yarn-project is not buildable in this workspace (no noir toolchain), so its typecheck runs in CI.

Created by claudebox · group: slackbot

@AztecBot AztecBot added ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR. labels Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant