Skip to content

feat(fast-inbox): streaming inbox propose validation, unwired (A-1378)#24773

Open
spalladino wants to merge 3 commits into
spl/a-1377-inbox-bucketsfrom
spl/a-1378-propose-validation
Open

feat(fast-inbox): streaming inbox propose validation, unwired (A-1378)#24773
spalladino wants to merge 3 commits into
spl/a-1377-inbox-bucketsfrom
spl/a-1378-propose-validation

Conversation

@spalladino

@spalladino spalladino commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Stacked on #24771 (A-1377). AZIP-22 Fast Inbox, FI-08.

What

Implements the AZIP's censorship assert as ProposeLib.validateInboxConsumption — a new library function that is not yet called: today's inbox.consume() / inHash check in propose is untouched, so this is dead code until the flip wires it in.

Given the checkpoint header's inboxRollingHash, an unsigned calldata bucket hint, the proposed slot, and the parent checkpoint's cumulative consumed total (which the flip will source from the temp checkpoint log), the function enforces:

  1. End anchoring: the header's rolling hash must equal the snapshot in inbox.getBucket(hint) (Rollup__InvalidInboxRollingHash). The hint is a lookup aid only — a wrong hint reverts, it cannot change what gets accepted — and getBucket itself rejects hints beyond the current bucket or already overwritten in the ring. A checkpoint consuming nothing references the same bucket as its parent (the genesis bucket for the first checkpoint), so there is no base case.
  2. Mandatory consumption: the first unconsumed bucket (hint + 1) must be absent, past the cutoff, or cap-escaped (Rollup__UnconsumedInboxMessages). The cutoff is the build-frame start minus INBOX_LAG_SECONDS: a checkpoint proposed in slot S is built during slot S-1 (proposer pipelining), and validators are not required to act on buckets younger than one L1 slot at build start, so cutoff = toTimestamp(S-1) - 12. Cap escape allows stopping when consuming through the next bucket would exceed MAX_L1_TO_L2_MSGS_PER_CHECKPOINT (1024) messages since the parent's total. Both comparisons are exact-boundary tested.

The function performs no Inbox write. It is a pure view that returns the consumed cumulative total (bucket.totalMsgCount). The flip (FI-14) stores that consumed position as part of the per-checkpoint temp-log record, extended to {inboxRollingHash, inboxMsgTotal, inboxConsumedBucket} and written by propose; that record is the authoritative consumed position and is prune-consistent, since temp logs rewind with the pending chain. Two new checks guard the returned value: consumption must move forward (Rollup__InboxConsumptionBehindParent, equal allowed — a proposal cannot consume behind its parent, and the check precedes the delta subtractions so a backwards proposal reverts descriptively rather than underflowing), and the consumed delta cannot exceed MAX_L1_TO_L2_MSGS_PER_CHECKPOINT in one checkpoint (Rollup__TooManyInboxMessagesConsumed). The Inbox-side proven-consumed cache that ring overwrite protection needs moved to FI-20, anchored to the proven tip rather than the pending chain (decided 2026-07-17): a pointer advanced with the pending chain is not prune-safe, since after a prune it would sit ahead of what the replacement chain consumed.

INBOX_LAG_SECONDS = 12 and MAX_L1_TO_L2_MSGS_PER_CHECKPOINT = 1024 are defined at file level in ProposeLib for now; they mirror the protocol constants and should move into the generated Constants library once the Solidity emitter allow-list includes them.

Testing

New ProposeInboxConsumption.t.sol (15 tests) drives the library function through a harness that owns the Inbox and TimeLib storage, covering the roadmap done-when plus boundaries:

  • empty Inbox (hint 0 / hash 0 passes and returns 0; non-zero hash rejected),
  • exact-cutoff bucket (timestamp == cutoff must be consumed; cutoff + 1 need not),
  • cap escape (1025 messages spill into buckets of 256+1: stopping at bucket 4 = exactly 1024 escapes and returns the full cap; stopping at bucket 3 = 1024-through-next does not; parent total shifts the arithmetic and kills the escape),
  • cap upper bound (consuming more than 1024 in one checkpoint from a fresh parent reverts Rollup__TooManyInboxMessagesConsumed),
  • moves forward (a proposal whose referenced bucket total sits behind the parent reverts Rollup__InboxConsumptionBehindParent; an equal reference consumes nothing and returns the unchanged total),
  • stale hash (previous bucket's hash against the current bucket) and unknown hash rejection, plus out-of-window hints (future bucket, ring-overwritten bucket),
  • return value: the successful paths assert the consumed cumulative total the flip will record (0, 3, cap).

The three former consumed-pointer tests are removed along with the pointer. Full forge test at the current rebased tip: 890 passed, 0 failed, 3 skipped (the previously noted testGetEpochProofPublicInputsVerifiesHeaders failure inherited from the base has since been fixed).

@spalladino
spalladino requested a review from just-mitch as a code owner July 17, 2026 18:24
@spalladino
spalladino force-pushed the spl/a-1377-inbox-buckets branch from 630c509 to 20656f8 Compare July 17, 2026 19:38
@spalladino
spalladino force-pushed the spl/a-1378-propose-validation branch from aa85a03 to 531e71a Compare July 17, 2026 19:38
@spalladino
spalladino force-pushed the spl/a-1377-inbox-buckets branch from 20656f8 to d12875f Compare July 17, 2026 20:04
@spalladino
spalladino force-pushed the spl/a-1378-propose-validation branch from 531e71a to b011406 Compare July 17, 2026 20:04
@spalladino
spalladino force-pushed the spl/a-1377-inbox-buckets branch from d12875f to 68714ab Compare July 17, 2026 23:29
@spalladino
spalladino force-pushed the spl/a-1378-propose-validation branch 2 times, most recently from 57ce718 to b853b87 Compare July 18, 2026 04:52
@spalladino
spalladino force-pushed the spl/a-1377-inbox-buckets branch from ec1326a to db14374 Compare July 18, 2026 05:22
@spalladino
spalladino force-pushed the spl/a-1378-propose-validation branch from b853b87 to 6e642ec Compare July 18, 2026 05:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant