feat: validator streaming acceptance conditions, flag off (A-1383)#24788
Open
spalladino wants to merge 4 commits into
Open
feat: validator streaming acceptance conditions, flag off (A-1383)#24788spalladino wants to merge 4 commits into
spalladino wants to merge 4 commits into
Conversation
…383) Move the streaming-Inbox cutoff formula and the mandatory-consumption / cap-escape rule into a shared `stdlib/messaging/inbox_consumption` module so the sequencer's bucket selection and the validator's last-block censorship check share one source of truth for the boundary semantics. - `getInboxCutoffTimestamp(slot, l1Constants, lagSeconds)`: buildFrameStart(slot) - lagSeconds, mirroring `ProposeLib.validateInboxConsumption`. The sequencer's `selectStreamingBundle` now calls it instead of inlining the computation (behavior unchanged). - `isInboxConsumptionSufficient(...)`: the none / past-cutoff (strict >) / cap-escape predicate. - Tests pin the A-1371 section-13 cross-layer vectors (genesisTime=100000, slotDuration=36): the cutoff table and the S=10 mandatory-consumption boundary.
…REAMING_INBOX (A-1383) Behind the shared `streamingInbox` flag (default off), the validator applies the AZIP-22 Fast Inbox acceptance conditions in place of the legacy per-checkpoint inHash comparison. Flag off, behavior is byte-identical. Block proposals (`proposal_handler.handleBlockProposal`), via a new pure `streaming_inbox_checks` module: - exists: the referenced bucket resolves in the node's own Inbox view and its rolling hash matches the reference (unknown bucket is an immediate reject; the bounded wait is A-1393); - moves forward: the bucket's cumulative total is at least the parent block's; - not too new: the bucket is at least INBOX_LAG_SECONDS old at validation time (dateProvider.now, inclusive boundary); - caps: the per-block count and the running per-checkpoint total fit their caps. The derived bundle is fed into per-block re-execution (insertMessagesPerBlock threaded through openCheckpoint/reexecuteTransactions), whose state-ref comparison stays the final arbiter. Checkpoint proposals (`validateCheckpointProposal`): the last-block minimum-consumption (censorship) rule runs before attesting, via the shared `isInboxConsumptionSufficient` predicate and the corrected cutoff anchor `getTimestampForSlot(slot - 1) - INBOX_LAG_SECONDS`; a mandatory unconsumed bucket rejects the checkpoint (no attestation). Parent/last-consumed buckets are resolved from L1-to-L2 tree leaf counts via a new `getInboxBucketByTotalMsgCount` archiver lookup (compact post-flip indexing); a leaf count that does not land on a bucket boundary (a pre-flip padded parent) is rejected/deferred, the flip (A-1384) closes that gap. The flag is picked up on the validator config via pickConfigMappings of the shared `streamingInbox` key. New reasons are non-slashable while the path lands. Full tsc -b cannot run locally (pre-existing noir-protocol-circuits-types breakage); CI is the typecheck of record for validator-client. Unit tests cover each check, the caps and lag boundaries, running-total accumulation, the censorship predicate, and the handler wiring.
…lidator (A-1383) Consolidate the three `BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex)` reads into a single `blockLeafCount` helper.
The archiver only writes bucket snapshots for ingested messages, whose first bucket is sequence 1, so `getInboxBucket(0)` / `getInboxBucketByTotalMsgCount(0)` returned undefined. That broke streaming resolution of a genesis parent (first block would reject `parent_bucket_unresolved`), of an empty genesis block's own bucket reference, and let an empty checkpoint skip the last-block censorship enforcement. Synthesize the sequence-0 sentinel (rolling hash 0, total 0) on read, mirroring the on-chain Inbox's base case, with a store-level test exercising it through a populated MessageStore.
This was referenced Jul 18, 2026
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.
Implements A-1383: the validator's streaming-Inbox acceptance conditions (AZIP-22 Fast Inbox), behind the shared
streamingInboxflag (default off). Flag off ⇒ byte-identical behavior. Stacked on #24787 (A-1382).The four block-proposal checks (
validator-client/src/streaming_inbox_checks.ts, wired inproposal_handler.handleBlockProposal)bucket_unknown); the bounded-wait soft path is A-1393. The reference is trusted only as abucketSeqlookup hint — timestamp/counts are read from the locally resolved bucket.INBOX_LAG_SECONDSold at validation time (dateProvider.now(), inclusive boundary).MAX_L1_TO_L2_MSGS_PER_BLOCK/MAX_L1_TO_L2_MSGS_PER_CHECKPOINT.The derived bundle is fed into per-block re-execution (
insertMessagesPerBlockthreaded throughopenCheckpoint→reexecuteTransactions); the existing state-ref comparison after re-execution stays the final arbiter.Last-block censorship check (
validateCheckpointProposal)Before attesting, the minimum-consumption rule runs via the shared
isInboxConsumptionSufficientpredicate: the first unconsumed bucket must be absent, past the cutoff, or a cap-escape. A mandatory unconsumed bucket rejects the checkpoint (no attestation,inbox_consumption_insufficient).Shared-predicate extraction (
refactorcommit)The cutoff formula and the minimum-consumption/cap-escape rule moved to
stdlib/messaging/inbox_consumption, so the sequencer's bucket selection and the validator share one source of truth. The sequencer'sselectStreamingBundlenow callsgetInboxCutoffTimestampinstead of inlining it — behavior unchanged (selector suite still green).Corrected cutoff anchor
The cutoff is
getTimestampForSlot(slot - 1) - INBOX_LAG_SECONDS(mirroringProposeLib.validateInboxConsumption), notgetBuildFrameStart. The A-1371 §13 cross-layer vectors are pinned ininbox_consumption.test.ts.Parent-ref derivation
Parent/last-consumed buckets are resolved from L1-to-L2 tree leaf counts (compact post-flip indexing == bucket cumulative total) via a new
getInboxBucketByTotalMsgCountarchiver lookup. A leaf count that does not land on a bucket boundary (a pre-flip padded parent) is rejected/deferred; the flip (A-1384) closes that gap.CI must typecheck what can't be verified locally
tsc -bcannot complete locally due to pre-existingnoir-protocol-circuits-typesbreakage (missing artifacts / stale generated types), which cascades toarchiver/validator-client. stdlib builds clean; no errors in the changed files outside the broken zone. Runnable unit suites are green:streaming_inbox_checks(14),inbox_consumption(10),proposal_handler(34, incl. flag-off unchanged + new streaming/censorship tests),message_store(35), stdlibarchiverinterface (RPC schema). CI is the typecheck of record for the unbuildable zone.Out of scope: bounded-wait (A-1393), the flip (A-1384), reorg rejection (A-1389), the full streaming checkpoint rebuild (A-1385).