Skip to content

Reject attestations to blocks prior to the split #7084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
26 changes: 26 additions & 0 deletions beacon_node/beacon_chain/src/attestation_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,9 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
// Check the attestation target root is consistent with the head root.
verify_attestation_target_root::<T::EthSpec>(&head_block, attestation)?;

// Do not process an attestation that doesn't descend from the finalized root.
verify_attestation_is_finalized_checkpoint_or_descendant(attestation, chain)?;

Ok(())
}

Expand Down Expand Up @@ -1361,6 +1364,29 @@ pub fn verify_committee_index<E: EthSpec>(attestation: AttestationRef<E>) -> Res
Ok(())
}

fn verify_attestation_is_finalized_checkpoint_or_descendant<T: BeaconChainTypes>(
attestation: AttestationRef<T::EthSpec>,
chain: &BeaconChain<T>,
) -> Result<(), Error> {
// If we have a split block newer than finalization then we also ban attestations which are not
// descended from that split block.
let fork_choice = chain.canonical_head.fork_choice_read_lock();
let split = chain.store.get_split_info();
let attestation_block_root = attestation.data().beacon_block_root;
let is_descendant_from_split_block =
split.slot == 0 || fork_choice.is_descendant(split.block_root, attestation_block_root);

if fork_choice.is_finalized_checkpoint_or_descendant(attestation_block_root)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to fold these checks into verify_head_block_is_known, which is already trying to do some checks for descent from finalization. That function currently makes the (wrong) assumption that a block present in fork choice descends from finalization (this isn't true in general because fork choice is sometimes pruned on a delay).

So I think we can add these checks in the case where block_opt is Some

Copy link
Member Author

@eserilev eserilev Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the check in verify_head_block_is_known when block_opt is Some

&& is_descendant_from_split_block
{
Ok(())
} else {
Err(Error::HeadBlockFinalized {
attestation_block_root,
})
}
}

/// Assists in readability.
type CommitteesPerSlot = u64;

Expand Down
21 changes: 21 additions & 0 deletions beacon_node/network/src/network_beacon_processor/gossip_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2893,6 +2893,27 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
);
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
}
AttnError::NotFinalizedDescendant {
attestation_block_root,
attestation_slot,
} => {
error!(
self.log,
"Could not verify the attestation for gossip. Rejecting the attestation";
"attestation_block_root" => ?attestation_block_root,
"slot" => ?attestation_slot,
"type" => ?attestation_type,
"peer_id" => %peer_id,
);
// There's no reason for an honest and non-buggy client to be gossiping
// attestations that blatantly conflict with finalization.
self.gossip_penalize_peer(
peer_id,
PeerAction::LowToleranceError,
"gossip_attestation_invalid",
);
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject);
}
}

debug!(
Expand Down
Loading