Skip to content

Commit

Permalink
Addressed sigp#4444
Browse files Browse the repository at this point in the history
Attestation Verification Post-Deneb
  • Loading branch information
ethDreamer committed Jul 24, 2023
1 parent 2b3adbe commit e93c2d2
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 42 deletions.
22 changes: 17 additions & 5 deletions beacon_node/beacon_chain/src/attestation_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ use std::borrow::Cow;
use strum::AsRefStr;
use tree_hash::TreeHash;
use types::{
Attestation, BeaconCommittee, CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation,
SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
Attestation, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec, ForkName, Hash256,
IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
};

pub use batch::{batch_verify_aggregated_attestations, batch_verify_unaggregated_attestations};
Expand Down Expand Up @@ -454,7 +454,7 @@ impl<'a, T: BeaconChainTypes> IndexedAggregatedAttestation<'a, T> {
// MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance).
//
// We do not queue future attestations for later processing.
verify_propagation_slot_range(&chain.slot_clock, attestation)?;
verify_propagation_slot_range(&chain.slot_clock, attestation, &chain.spec)?;

// Check the attestation's epoch matches its target.
if attestation.data.slot.epoch(T::EthSpec::slots_per_epoch())
Expand Down Expand Up @@ -722,7 +722,7 @@ impl<'a, T: BeaconChainTypes> IndexedUnaggregatedAttestation<'a, T> {
// MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance).
//
// We do not queue future attestations for later processing.
verify_propagation_slot_range(&chain.slot_clock, attestation)?;
verify_propagation_slot_range(&chain.slot_clock, attestation, &chain.spec)?;

// Check to ensure that the attestation is "unaggregated". I.e., it has exactly one
// aggregation bit set.
Expand Down Expand Up @@ -1037,6 +1037,7 @@ fn verify_head_block_is_known<T: BeaconChainTypes>(
pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
slot_clock: &S,
attestation: &Attestation<E>,
spec: &ChainSpec,
) -> Result<(), Error> {
let attestation_slot = attestation.data.slot;

Expand All @@ -1051,10 +1052,21 @@ pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
}

// Taking advantage of saturating subtraction on `Slot`.
let earliest_permissible_slot = slot_clock
let one_epoch_prior = slot_clock
.now_with_past_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
.ok_or(BeaconChainError::UnableToReadSlot)?
- E::slots_per_epoch();

let current_fork =
spec.fork_name_at_slot::<E>(slot_clock.now().ok_or(BeaconChainError::UnableToReadSlot)?);
let earliest_permissible_slot = match current_fork {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => one_epoch_prior,
// EIP-7045
ForkName::Deneb => one_epoch_prior
.epoch(E::slots_per_epoch())
.start_slot(E::slots_per_epoch()),
};

if attestation_slot < earliest_permissible_slot {
return Err(Error::PastSlot {
attestation_slot,
Expand Down
102 changes: 88 additions & 14 deletions beacon_node/beacon_chain/src/beacon_block_reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use safe_arith::SafeArith;
use slog::error;
use state_processing::{
common::{
altair, get_attestation_participation_flag_indices, get_attesting_indices_from_state,
altair, get_attestation_participation_flag_indices_altair,
get_attestation_participation_flag_indices_deneb, get_attesting_indices_from_state,
},
per_block_processing::{
altair::sync_committee::compute_sync_aggregate_rewards, get_slashable_indices,
Expand Down Expand Up @@ -60,26 +61,37 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
BeaconChainError::BlockRewardError
})?;

let block_attestation_reward = if let BeaconState::Base(_) = state {
self.compute_beacon_block_attestation_reward_base(block, block_root, state)
let block_attestation_reward = match state {
BeaconState::Base(_) => self
.compute_beacon_block_attestation_reward_base(block, block_root, state)
.map_err(|e| {
error!(
self.log,
"Error calculating base block attestation reward";
"error" => ?e
self.log,
"Error calculating base block attestation reward";
"error" => ?e
);
BeaconChainError::BlockRewardAttestationError
})?
} else {
self.compute_beacon_block_attestation_reward_altair(block, state)
})?,
BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) => self
.compute_beacon_block_attestation_reward_altair(block, state)
.map_err(|e| {
error!(
self.log,
"Error calculating altair block attestation reward";
"error" => ?e
);
BeaconChainError::BlockRewardAttestationError
})?,
BeaconState::Deneb(_) => self
.compute_beacon_block_attestation_reward_deneb(block, state)
.map_err(|e| {
error!(
self.log,
"Error calculating altair block attestation reward";
"error" => ?e
self.log,
"Error calculating deneb block attestation reward";
"error" => ?e
);
BeaconChainError::BlockRewardAttestationError
})?
})?,
};

let total_reward = sync_aggregate_reward
Expand Down Expand Up @@ -192,7 +204,69 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
for attestation in block.body().attestations() {
let data = &attestation.data;
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
let participation_flag_indices = get_attestation_participation_flag_indices(
let participation_flag_indices = get_attestation_participation_flag_indices_altair(
state,
data,
inclusion_delay,
&self.spec,
)?;

let attesting_indices = get_attesting_indices_from_state(state, attestation)?;

let mut proposer_reward_numerator = 0;
for index in attesting_indices {
let index = index as usize;
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
let epoch_participation =
state.get_epoch_participation_mut(data.target.epoch)?;
let validator_participation = epoch_participation
.get_mut(index)
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;

if participation_flag_indices.contains(&flag_index)
&& !validator_participation.has_flag(flag_index)?
{
validator_participation.add_flag(flag_index)?;
proposer_reward_numerator.safe_add_assign(
altair::get_base_reward(
state,
index,
base_reward_per_increment,
&self.spec,
)?
.safe_mul(weight)?,
)?;
}
}
}
total_proposer_reward.safe_add_assign(
proposer_reward_numerator.safe_div(proposer_reward_denominator)?,
)?;
}

Ok(total_proposer_reward)
}

fn compute_beacon_block_attestation_reward_deneb<Payload: AbstractExecPayload<T::EthSpec>>(
&self,
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
state: &mut BeaconState<T::EthSpec>,
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> {
let total_active_balance = state.get_total_active_balance()?;
let base_reward_per_increment =
altair::BaseRewardPerIncrement::new(total_active_balance, &self.spec)?;

let mut total_proposer_reward = 0;

let proposer_reward_denominator = WEIGHT_DENOMINATOR
.safe_sub(PROPOSER_WEIGHT)?
.safe_mul(WEIGHT_DENOMINATOR)?
.safe_div(PROPOSER_WEIGHT)?;

for attestation in block.body().attestations() {
let data = &attestation.data;
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
let participation_flag_indices = get_attestation_participation_flag_indices_deneb(
state,
data,
inclusion_delay,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
attestation_verification::verify_propagation_slot_range(
seen_clock,
failed_att.attestation(),
&self.chain.spec,
);

// Only penalize the peer if it would have been invalid at the moment we received
Expand Down Expand Up @@ -2661,6 +2662,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
let is_timely = attestation_verification::verify_propagation_slot_range(
&self.chain.slot_clock,
attestation,
&self.chain.spec,
)
.is_ok();

Expand Down
87 changes: 78 additions & 9 deletions beacon_node/operation_pool/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use crate::attestation_storage::AttestationRef;
use crate::max_cover::MaxCover;
use crate::reward_cache::RewardCache;
use state_processing::common::{
altair, base, get_attestation_participation_flag_indices, get_attesting_indices,
altair, base, get_attestation_participation_flag_indices_altair,
get_attestation_participation_flag_indices_deneb, get_attesting_indices,
};
use std::collections::HashMap;
use types::{
Expand All @@ -27,10 +28,16 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> {
total_active_balance: u64,
spec: &ChainSpec,
) -> Option<Self> {
if let BeaconState::Base(ref base_state) = state {
Self::new_for_base(att, state, base_state, total_active_balance, spec)
} else {
Self::new_for_altair(att, state, reward_cache, total_active_balance, spec)
match state {
BeaconState::Base(ref base_state) => {
Self::new_for_base(att, state, base_state, total_active_balance, spec)
}
BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) => {
Self::new_for_altair(att, state, reward_cache, total_active_balance, spec)
}
BeaconState::Deneb(_) => {
Self::new_for_deneb(att, state, reward_cache, total_active_balance, spec)
}
}
}

Expand Down Expand Up @@ -68,7 +75,7 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> {
})
}

/// Initialise an attestation cover object for Altair or later.
/// Initialise an attestation cover object for Altair, Merge, Capella.
pub fn new_for_altair(
att: AttestationRef<'a, T>,
state: &BeaconState<T>,
Expand All @@ -79,9 +86,71 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> {
let att_data = att.attestation_data();

let inclusion_delay = state.slot().as_u64().checked_sub(att_data.slot.as_u64())?;
let att_participation_flags =
get_attestation_participation_flag_indices(state, &att_data, inclusion_delay, spec)
.ok()?;
let att_participation_flags = get_attestation_participation_flag_indices_altair(
state,
&att_data,
inclusion_delay,
spec,
)
.ok()?;
let base_reward_per_increment =
altair::BaseRewardPerIncrement::new(total_active_balance, spec).ok()?;

let fresh_validators_rewards = att
.indexed
.attesting_indices
.iter()
.filter_map(|&index| {
if reward_cache
.has_attested_in_epoch(index, att_data.target.epoch)
.ok()?
{
return None;
}

let mut proposer_reward_numerator = 0;

let base_reward =
altair::get_base_reward(state, index as usize, base_reward_per_increment, spec)
.ok()?;

for (flag_index, weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
if att_participation_flags.contains(&flag_index) {
proposer_reward_numerator += base_reward.checked_mul(*weight)?;
}
}

let proposer_reward = proposer_reward_numerator
.checked_div(WEIGHT_DENOMINATOR.checked_mul(spec.proposer_reward_quotient)?)?;

Some((index, proposer_reward)).filter(|_| proposer_reward != 0)
})
.collect();

Some(Self {
att,
fresh_validators_rewards,
})
}

/// Initialise an attestation cover object for Deneb or later
pub fn new_for_deneb(
att: AttestationRef<'a, T>,
state: &BeaconState<T>,
reward_cache: &'a RewardCache,
total_active_balance: u64,
spec: &ChainSpec,
) -> Option<Self> {
let att_data = att.attestation_data();

let inclusion_delay = state.slot().as_u64().checked_sub(att_data.slot.as_u64())?;
let att_participation_flags = get_attestation_participation_flag_indices_deneb(
state,
&att_data,
inclusion_delay,
spec,
)
.ok()?;
let base_reward_per_increment =
altair::BaseRewardPerIncrement::new(total_active_balance, spec).ok()?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use types::{AttestationData, BeaconState, ChainSpec, EthSpec};
///
/// This function will return an error if the source of the attestation doesn't match the
/// state's relevant justified checkpoint.
pub fn get_attestation_participation_flag_indices<T: EthSpec>(
pub fn get_attestation_participation_flag_indices_altair<T: EthSpec>(
state: &BeaconState<T>,
data: &AttestationData,
inclusion_delay: u64,
Expand Down Expand Up @@ -52,3 +52,47 @@ pub fn get_attestation_participation_flag_indices<T: EthSpec>(
}
Ok(participation_flag_indices)
}

/// Get the participation flags for a valid attestation (post-deneb).
///
/// You should have called `verify_attestation_for_block_inclusion` before
/// calling this function, in order to ensure that the attestation's source is correct.
///
/// This function will return an error if the source of the attestation doesn't match the
/// state's relevant justified checkpoint.
pub fn get_attestation_participation_flag_indices_deneb<T: EthSpec>(
state: &BeaconState<T>,
data: &AttestationData,
inclusion_delay: u64,
spec: &ChainSpec,
) -> Result<SmallVec<[usize; NUM_FLAG_INDICES]>, Error> {
let justified_checkpoint = if data.target.epoch == state.current_epoch() {
state.current_justified_checkpoint()
} else {
state.previous_justified_checkpoint()
};

// Matching roots.
let is_matching_source = data.source == justified_checkpoint;
let is_matching_target = is_matching_source
&& data.target.root == *state.get_block_root_at_epoch(data.target.epoch)?;
let is_matching_head =
is_matching_target && data.beacon_block_root == *state.get_block_root(data.slot)?;

if !is_matching_source {
return Err(Error::IncorrectAttestationSource);
}

// Participation flag indices
let mut participation_flag_indices = SmallVec::new();
if is_matching_source && inclusion_delay <= T::slots_per_epoch().integer_sqrt() {
participation_flag_indices.push(TIMELY_SOURCE_FLAG_INDEX);
}
if is_matching_target {
participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX);
}
if is_matching_head && inclusion_delay == spec.min_attestation_inclusion_delay {
participation_flag_indices.push(TIMELY_HEAD_FLAG_INDEX);
}
Ok(participation_flag_indices)
}
5 changes: 4 additions & 1 deletion consensus/state_processing/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub mod base;
pub mod update_progressive_balances_cache;

pub use deposit_data_tree::DepositDataTree;
pub use get_attestation_participation::get_attestation_participation_flag_indices;
pub use get_attestation_participation::{
get_attestation_participation_flag_indices_altair,
get_attestation_participation_flag_indices_deneb,
};
pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_from_state};
pub use get_indexed_attestation::get_indexed_attestation;
pub use initiate_validator_exit::initiate_validator_exit;
Expand Down
Loading

0 comments on commit e93c2d2

Please sign in to comment.