fix(beacon): verify fetched beacon state hashes to the block state root - #259
Open
qu0b wants to merge 1 commit into
Open
fix(beacon): verify fetched beacon state hashes to the block state root#259qu0b wants to merge 1 commit into
qu0b wants to merge 1 commit into
Conversation
downloadAndStoreBeaconState fetches the state by slot from a single upstream and stored whatever came back under the block's state root without verification. A node that is behind can answer a by-slot state request with a state from a different chain (e.g. its stale head dialed forward through empty slots, as lodestar and lighthouse do), which checkpointz would then serve at /eth/v2/debug/beacon/states/finalized. Checkpoint-syncing clients fail with a state root mismatch against block.state_root. Compute the hash tree root of the fetched state and reject the bundle if it does not match the state root committed to in the block. Observed on glamsterdam-devnet-7 with a lodestar upstream whose head was ~15k slots behind: it returned a fabricated state for the finalized checkpoint slot instead of erroring. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017K6pyDaSgX2FcLtQ1qQ8Vu
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.
Problem
downloadAndStoreBeaconStatefetches the finalized state by slot from a single upstream and stores whatever bytes come back under the block'sstate_root, without ever verifying them. The state is then served verbatim at/eth/v2/debug/beacon/states/finalized.A beacon node that has fallen behind can answer a by-slot state request with a state from a different chain view instead of erroring — lodestar (and lighthouse, in a different way) serve their stale head state dialed forward through empty slots for slots beyond their head. Checkpointz caches that fabricated state under the real
block.state_rootand serves it; checkpoint-syncing clients then fail theirhash_tree_root(state) == block.state_rootanchor check.Observed on glamsterdam-devnet-7
lodestar-geth-1's head froze at slot 56351 while checkpointz still listed ithealthy: true.latest_block_header.slot = 56351dialed forward ~15k empty slots, HTR0xcb39fc23…— matching nothing on the canonical chain (expected0x2d414744…).PastFinalizedCheckpointnode filter usually keeps such nodes out, but it filters on claimed finality, not head — finality stalls, optimistic finality claims, or a node freezing near a serving flip let a fabricating upstream through, and nothing downstream catches it.Fix
Compute the hash tree root of the fetched state (existing
sszEncoder.GetStateRoot) and reject the bundle if it doesn't match the state root committed to in the block. The bad upstream's bundle fails, and the serving loop retries with another node.Cost: one state HTR per bundle download (once per serving-checkpoint flip / genesis fetch) — ~1s for a 73 MB gloas state.
Testing
go build ./.../go test ./...green.The same code exists on
master; happy to open a port there too.🤖 Generated with Claude Code
https://claude.ai/code/session_017K6pyDaSgX2FcLtQ1qQ8Vu