Repository navigation
consensus/parlia: validate BlockAccessListHash and SlotNumber in header verification - #3808
Merged
zlacfzy merged 1 commit intoSep 8, 2026
Conversation
…e Amsterdam BlockAccessListHash (EIP-7928) and SlotNumber (EIP-7843) are trailing rlp:"optional" header fields that are covered by the block hash but not by the Parlia seal preimage (EncodeSigHeader stops after RequestsHash). Parlia's header verification never validated them, so before Amsterdam an unprivileged peer could append a crafted BlockAccessListHash to any validator-signed block: RLP-valid, seal preimage and signature unchanged, but a different block hash. Every node accepted the mutated block as validly sealed, letting a peer with no stake or key grind the block hash of an honest validator's block (e.g. to bias BLOCKHASH-based consumers or win a same-height lower-hash fork-choice tie). Validate these fields in VerifyUnsealedHeader, which is on the peer-import path (VerifyHeader -> verifyHeader -> VerifyUnsealedHeader, before verifySeal) as well as the bid pre-seal path, mirroring the existing RequestsHash/ParentBeaconRoot handling: require them absent before Amsterdam and present from Amsterdam on. This closes the live pre-Amsterdam malleability; from Amsterdam the fields are legitimately present and their content is bound by state validation and the slot rules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull Request ReviewThis PR strengthens Parlia header verification by enforcing fork-gated presence rules for the optional Sensitive ContentNo sensitive content detected. Security IssuesNo serious security issues detected. Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
allformless
approved these changes
Aug 31, 2026
will-2012
approved these changes
Sep 3, 2026
flywukong
approved these changes
Sep 3, 2026
joey0612
approved these changes
Sep 4, 2026
chee-chyuan
added a commit
to bnb-chain/reth-bsc
that referenced
this pull request
Sep 10, 2026
…fication (#499) Ports bnb-chain/bsc#3808. `BlockAccessListHash` (EIP-7928) and `SlotNumber` (EIP-7843) are trailing `rlp:"optional"` fields on alloy's `Header`: they decode off the wire and are covered by the block hash, but the Parlia seal preimage stops after `requests_hash` (`encode_header_with_chain_id`, mirroring go-bsc's `EncodeSigHeader`), so the seal does not commit to them. Header verification did not check them at all. Honest BSC blocks never set either field -- every header reth-bsc assembles hard-codes both to `None`, and every `NextBlockEnvAttributes` construction site passes `slot_number: None`. So the gap was pure malleability: an unprivileged peer could append a crafted value to a validator-signed header, changing its block hash while the signature still recovers the same signer, and reth-bsc would accept the result as a validly-sealed block. `validate_unsealed_header_fields` -- go-bsc's `VerifyUnsealedHeader` scope, shared by the peer-import path and the BidBlock pre-seal path -- now requires both fields to be absent, alongside the presence rules it already applies to `withdrawals_root`, the 4844 fields, `parent_beacon_block_root` and `requests_hash`. Error strings match go-bsc's ("invalid BlockAccessListHash, have %#x, expected nil") so cross-client log comparison stays mechanical. go-bsc gates the rule on Amsterdam -- absent before, required from. BSC has not scheduled Amsterdam and reth-bsc has no such fork, so only the "must be absent" half is expressible here; the presence half lands with Amsterdam itself. This is recorded on the helper so the second half is not forgotten. Tests cover the helper's two rejections and the both-absent baseline, that the check is actually reached from `validate_unsealed_header_fields` (an otherwise-valid pre-Cancun header passes, the same header with either field does not), and the malleability itself: appending either field leaves `hash_with_chain_id` unchanged while `hash_slow` differs, which is precisely why the seal cannot catch it. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Description
Validate the two trailing
rlp:"optional"header fieldsBlockAccessListHash(EIP-7928) andSlotNumber(EIP-7843) in Parlia's header verification, mirroring the existing fork-gated handling ofRequestsHashandParentBeaconRoot: they must be absent before Amsterdam and present from Amsterdam on.Rationale
These optional fields are covered by the block hash but not by the Parlia seal preimage (
EncodeSigHeaderstops afterRequestsHash), and header verification previously did not check them at all. Because they are absent from honest blocks before Amsterdam, the missing check left a validator-signed block's hash malleable: the fields could be added without invalidating the seal, yielding a different block hash on an otherwise validly-sealed block. Adding the same nil/presence validation Parlia already applies to the other post-Prague optional fields closes that gap. From Amsterdam the fields are legitimately present, and their content is bound by state validation (BlockAccessListHashis recomputed from execution) and the slot rules rather than by the seal.Example
A pre-Amsterdam header carrying a non-nil
BlockAccessListHashorSlotNumberis now rejected byVerifyHeaderbefore seal verification, instead of being accepted.Changes
consensus/parlia: inVerifyUnsealedHeader(on theVerifyHeaderpeer-import path and the bid pre-seal path), requireBlockAccessListHashandSlotNumberto be nil before Amsterdam and non-nil from Amsterdam on.