Context
Summit's finalizer prepares checkpoint data around epoch boundaries. At the penultimate block it stores a pending_checkpoint, and the last block carries the checkpoint hash that commits the boundary artifact into the finalized chain.
The intended invariant is that the advertised SSZ state root for checkpoint or proof consumers authenticates the consensus-state fields needed to verify the next boundary transition. pending_checkpoint is one of those fields because it determines the checkpoint digest that the boundary header is expected to carry.
Claim
pending_checkpoint is serialized state used to derive the next boundary checkpoint_hash, but it is omitted from the SSZ state root used for parent_beacon_block_root.
A malicious checkpoint producer or state artifact supplier can persist or serve consensus state with a serialized pending_checkpoint that differs from the value expected by a consumer while advertising the same SSZ state root, because pending_checkpoint is omitted from the root tree and the root alone cannot bind the next boundary checkpoint digest.
Flow
Reachable for SSZ roots and proofs captured after the finalizer sets pending_checkpoint at the penultimate block, because set_pending_checkpoint does not update the tree and the full rebuild omits the field. The validated start condition is a state/proof artifact whose serialized pending_checkpoint differs from the value a consumer expects; live boundary block verification still checks the header checkpoint_hash separately.
Impact
The finalized/proved state root can omit the checkpoint artifact the chain is about to finalize. Restarted, checkpoint-derived, or proof-consuming nodes can accept a state root that does not commit to the pending checkpoint digest used at the epoch boundary, weakening the root-binds-state invariant.
Root Cause
pending_checkpoint is part of serialized consensus state but is excluded from the SSZ state tree leaves and is not reflected by set_pending_checkpoint.
Code
Related Issues/PRs
Related issues cover adjacent pending-checkpoint validation and SSZ root omissions across dynamic epoch, pending execution, and proof response state.
Fix
- Add
pending_checkpoint to the SSZ state tree or remove it from consensus state and derive it deterministically from committed fields.
- Ensure state-root tests change when only
pending_checkpoint changes.
- Version roots/checkpoints if the committed state layout changes.
Context
Summit's finalizer prepares checkpoint data around epoch boundaries. At the penultimate block it stores a
pending_checkpoint, and the last block carries the checkpoint hash that commits the boundary artifact into the finalized chain.The intended invariant is that the advertised SSZ state root for checkpoint or proof consumers authenticates the consensus-state fields needed to verify the next boundary transition.
pending_checkpointis one of those fields because it determines the checkpoint digest that the boundary header is expected to carry.Claim
pending_checkpointis serialized state used to derive the next boundarycheckpoint_hash, but it is omitted from the SSZ state root used forparent_beacon_block_root.A malicious checkpoint producer or state artifact supplier can persist or serve consensus state with a serialized
pending_checkpointthat differs from the value expected by a consumer while advertising the same SSZ state root, becausepending_checkpointis omitted from the root tree and the root alone cannot bind the next boundary checkpoint digest.Flow
Reachable for SSZ roots and proofs captured after the finalizer sets
pending_checkpointat the penultimate block, becauseset_pending_checkpointdoes not update the tree and the full rebuild omits the field. The validated start condition is a state/proof artifact whose serializedpending_checkpointdiffers from the value a consumer expects; live boundary block verification still checks the headercheckpoint_hashseparately.Impact
The finalized/proved state root can omit the checkpoint artifact the chain is about to finalize. Restarted, checkpoint-derived, or proof-consuming nodes can accept a state root that does not commit to the pending checkpoint digest used at the epoch boundary, weakening the root-binds-state invariant.
Root Cause
pending_checkpointis part of serialized consensus state but is excluded from the SSZ state tree leaves and is not reflected byset_pending_checkpoint.Code
ConsensusStatestorespending_checkpoint: https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/consensus_state.rs#L30.set_pending_checkpointmutates it without updating the SSZ tree: https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/consensus_state.rs#L268.pending_checkpoint: https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/consensus_state.rs#L760, https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/consensus_state.rs#L780.SszStateTreetop-level leaves do not include a pending-checkpoint leaf: https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/ssz_state_tree.rs#L30, https://github.com/SeismicSystems/summit/blob/ed2c5c8/types/src/ssz_state_tree.rs#L56.checkpoint_hash: https://github.com/SeismicSystems/summit/blob/ed2c5c8/finalizer/src/actor.rs#L932, https://github.com/SeismicSystems/summit/blob/ed2c5c8/finalizer/src/actor.rs#L965.parent_beacon_block_rootandcheckpoint_hashseparately, so the root does not itself bind the pending checkpoint: https://github.com/SeismicSystems/summit/blob/ed2c5c8/application/src/actor.rs#L648, https://github.com/SeismicSystems/summit/blob/ed2c5c8/application/src/actor.rs#L665.Related Issues/PRs
Related issues cover adjacent pending-checkpoint validation and SSZ root omissions across dynamic epoch, pending execution, and proof response state.
Fix
pending_checkpointto the SSZ state tree or remove it from consensus state and derive it deterministically from committed fields.pending_checkpointchanges.