Files
lighthouse/consensus/state_processing/src/per_block_processing/verify_attestation.rs
Michael Sproul 7e275f8dc2 Gloas envelope consensus and more operations tests (#8781)
- Implement new `process_execution_payload` (as `process_execution_payload_envelope`).
- Implement new processing for deposit requests, including logic for adding new builders to the registry with index reuse.
- Enable a bunch more operations EF tests (most of them except bid processing/payload attestations/etc which we don't have code for yet).


Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
2026-02-10 03:59:25 +00:00

131 lines
4.5 KiB
Rust

use super::VerifySignatures;
use super::errors::{AttestationInvalid as Invalid, BlockOperationError};
use crate::ConsensusContext;
use crate::per_block_processing::is_valid_indexed_attestation;
use safe_arith::SafeArith;
use types::*;
type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
fn error(reason: Invalid) -> BlockOperationError<Invalid> {
BlockOperationError::invalid(reason)
}
/// Returns `Ok(())` if the given `attestation` is valid to be included in a block that is applied
/// to `state`. Otherwise, returns a descriptive `Err`.
///
/// Optionally verifies the aggregate signature, depending on `verify_signatures`.
pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>(
state: &BeaconState<E>,
attestation: AttestationRef<'ctxt, E>,
ctxt: &'ctxt mut ConsensusContext<E>,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<IndexedAttestationRef<'ctxt, E>> {
let data = attestation.data();
verify!(
data.slot.safe_add(spec.min_attestation_inclusion_delay)? <= state.slot(),
Invalid::IncludedTooEarly {
state: state.slot(),
delay: spec.min_attestation_inclusion_delay,
attestation: data.slot,
}
);
if state.fork_name_unchecked().deneb_enabled() {
// [Modified in Deneb:EIP7045]
} else {
verify!(
state.slot() <= data.slot.safe_add(E::slots_per_epoch())?,
Invalid::IncludedTooLate {
state: state.slot(),
attestation: data.slot,
}
);
}
verify_attestation_for_state(state, attestation, ctxt, verify_signatures, spec)
}
/// Returns `Ok(())` if `attestation` is a valid attestation to the chain that precedes the given
/// `state`.
///
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
/// prior blocks in `state`.
pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
state: &BeaconState<E>,
attestation: AttestationRef<'ctxt, E>,
ctxt: &'ctxt mut ConsensusContext<E>,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<IndexedAttestationRef<'ctxt, E>> {
let data = attestation.data();
// NOTE: choosing a validation based on the attestation's fork
// rather than the state's fork makes this simple, but technically the spec
// defines this verification based on the state's fork.
match attestation {
AttestationRef::Base(_) => {
verify!(
data.index < state.get_committee_count_at_slot(data.slot)?,
Invalid::BadCommitteeIndex
);
}
AttestationRef::Electra(_) => {
let fork_at_attestation_slot = spec.fork_name_at_slot::<E>(data.slot);
if fork_at_attestation_slot.gloas_enabled() {
verify!(data.index < 2, Invalid::BadOverloadedDataIndex);
} else {
verify!(data.index == 0, Invalid::BadCommitteeIndex);
}
}
}
// Verify the Casper FFG vote.
verify_casper_ffg_vote(attestation, state)?;
// Check signature and bitfields
let indexed_attestation = ctxt.get_indexed_attestation(state, attestation)?;
is_valid_indexed_attestation(state, indexed_attestation, verify_signatures, spec)?;
Ok(indexed_attestation)
}
/// Check target epoch and source checkpoint.
fn verify_casper_ffg_vote<E: EthSpec>(
attestation: AttestationRef<E>,
state: &BeaconState<E>,
) -> Result<()> {
let data = attestation.data();
verify!(
data.target.epoch == data.slot.epoch(E::slots_per_epoch()),
Invalid::TargetEpochSlotMismatch {
target_epoch: data.target.epoch,
slot_epoch: data.slot.epoch(E::slots_per_epoch()),
}
);
if data.target.epoch == state.current_epoch() {
verify!(
data.source == state.current_justified_checkpoint(),
Invalid::WrongJustifiedCheckpoint {
state: Box::new(state.current_justified_checkpoint()),
attestation: Box::new(data.source),
is_current: true,
}
);
Ok(())
} else if data.target.epoch == state.previous_epoch() {
verify!(
data.source == state.previous_justified_checkpoint(),
Invalid::WrongJustifiedCheckpoint {
state: Box::new(state.previous_justified_checkpoint()),
attestation: Box::new(data.source),
is_current: false,
}
);
Ok(())
} else {
Err(error(Invalid::BadTargetEpoch))
}
}