feat(fast-inbox): commit the inbox bucket timestamp in every rolling-hash link - #25327
Open
spalladino wants to merge 2 commits into
Open
feat(fast-inbox): commit the inbox bucket timestamp in every rolling-hash link#25327spalladino wants to merge 2 commits into
spalladino wants to merge 2 commits into
Conversation
spalladino
marked this pull request as ready for review
August 27, 2026 16:13
spalladino
force-pushed
the
spl/a-1758-inbox-rolling-hash-timestamp
branch
from
August 28, 2026 22:07
b780299 to
f1cfcfe
Compare
spalladino
changed the base branch from
spl/checkpoint-builder-insert-messages-before-txs
to
spl/inbox-block-bucket-boundary
August 28, 2026 22:07
…ollup-lib instead of a types helper
spalladino
force-pushed
the
spl/a-1758-inbox-rolling-hash-timestamp
branch
from
August 29, 2026 11:12
f1cfcfe to
21f8e29
Compare
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.
Every link of the Inbox rolling hash now also absorbs the bucket's L1 timestamp, so an L1 reorg that re-times a bucket without changing its messages or packing is detected by the archiver. This is stage 4 of the Inbox Domain Separators walkthrough.
Context
The archiver decides it is in sync with the Inbox by comparing two numbers against L1: the total message count and the rolling hash. After #25319 the hash covers the messages, their order and how they are grouped into buckets — but not the bucket timestamps. So this reorg was invisible:
Same messages, same buckets, same count, same hash. The node keeps
ts 1212for bucket #8 while L1 says1224. A bucket's timestamp is what the censorship cutoff is compared against, so the stale node believes #8 is mandatory under a cutoff of 1220 while L1 says it is optional, and it is the recency key the sequencer's bucket selection walks. Nodes split their attestations for no good reason, and rolling back does not help because the rollback looks for the last message whose hash still matches, and they all do.Approach
With the timestamp in every link, a re-timed bucket changes its hash from its first message onward. The archiver's existing sync check fails, its existing rollback finds the exact last common message, and it re-downloads only what changed. No new node logic. After this change the rolling hash commits to everything in the L1
InboxBucketstruct that consensus reads: leaves, order, boundaries, timestamp.sepis unchanged (…_BUCKET_STARTon a bucket's first leaf,…_ROLLING_HASHotherwise). It stays because a full bucket spills into a second one in the same L1 block, so rollover siblings share a timestamp and the timestamp alone cannot mark that boundary; feat(fast-inbox): require every block to start consuming at an inbox bucket boundary #25322's block-alignment flags are also sound only because the separator anchors them to L1's hash.inbox_parity_{64,256,1024}are +0.24% gates (21.75 gates per lane); block roots and theL1ToL2MessageSpongeare untouched.Gas
./bootstrap.sh gas_reportand./bootstrap.sh gas_benchmarkre-run on this branch:Inbox.sendL2Messagemin/medianInbox.sendL2MessagemaxInboxbytecode sizeRollupbytecode sizepropose/submitEpochRootProofbenchmarks (gas_benchmark.md)The +59 gas is
abi.encodePackedof the extrauint64plus the wider memory copy; the sha256 precompile cost is the same for 68 and 76 bytes. The rollup side never hashes the chain on L1, so the checkpoint benchmarks do not move.Hash.accumulateInboxRollingHash,Inbox._absorbIntoBucketpassesbucket.timestamp), Noir (accumulate_sha256_with_separator_and_u64;InboxParityPrivateInputs.bucket_timestamps: [u64; S], padding lanes asserted zero) and TS (updateInboxRollingHashtakes the timestamp and rejects values outsideuint64). Shared vectors frominbox_rolling_hash_vectors.pyare pinned on all three sides, including timestamps0and2^64-1and a same-leaves/different-timestamp pair.InboxMessageBundlecarries the timestamp once per bucket ({ timestamp, leaves }[]) and expands it per lane only when building parity inputs. Rollup sample inputs and the three parity VKs are regenerated.consume_l1_to_l2_message, every portal, FeeJuice claims, theMessageSentABI and the SDK.FakeL1StategainsrecomputeMessageBuckets()andsetL1BlockTimestamp(); new sync tests for a packing reorg, a bucket merge, and a timestamp-only reorg (red before this change), plus bucket-snapshot assertions on the existing reorg tests. A validator test pins that a proposal built on a stale bucket timestamp is rejected asbucket_hash_mismatch.API changes
InboxMessageBundle(archivergetL1ToL2MessagesBetweenBucketsand friends) changes fromFr[][]to{ timestamp: bigint; leaves: Fr[] }[];EpochProvingJobDataandInboxParityPrivateInputsserialization change accordingly (both formats are unversioned, as before).Stacked on #25322.
Fixes A-1758