From 559ea055c201cd62ce35d10961e59eeda9ff3b5d Mon Sep 17 00:00:00 2001 From: Mark Mackey Date: Tue, 24 Sep 2024 12:41:05 -0500 Subject: [PATCH] process_payload_attestation() --- .../update_progressive_balances_cache.rs | 25 +++- .../process_operations.rs | 107 +++++++++++++++++- .../verify_payload_attestation.rs | 1 + .../progressive_balances_cache.rs | 64 ++++++++++- .../types/src/indexed_payload_attestation.rs | 7 ++ consensus/types/src/participation_flags.rs | 9 ++ 6 files changed, 199 insertions(+), 14 deletions(-) diff --git a/consensus/state_processing/src/common/update_progressive_balances_cache.rs b/consensus/state_processing/src/common/update_progressive_balances_cache.rs index 1fdfe802c4..e04ac3633b 100644 --- a/consensus/state_processing/src/common/update_progressive_balances_cache.rs +++ b/consensus/state_processing/src/common/update_progressive_balances_cache.rs @@ -86,8 +86,8 @@ fn update_flag_total_balances( Ok(()) } -/// Updates the `ProgressiveBalancesCache` when a new target attestation has been processed. -pub fn update_progressive_balances_on_attestation( +/// Updates the `ProgressiveBalancesCache` when a particiation flag is added to a validator. +pub fn update_progressive_balances_on_flag_added( state: &mut BeaconState, epoch: Epoch, flag_index: usize, @@ -95,7 +95,26 @@ pub fn update_progressive_balances_on_attestation( validator_slashed: bool, ) -> Result<(), BlockProcessingError> { if is_progressive_balances_enabled(state) { - state.progressive_balances_cache_mut().on_new_attestation( + state.progressive_balances_cache_mut().on_flag_added( + epoch, + validator_slashed, + flag_index, + validator_effective_balance, + )?; + } + Ok(()) +} + +/// Updates the `ProgressiveBalancesCache` when a particiation flag is removed from a validator. +pub fn update_progressive_balances_on_flag_removed( + state: &mut BeaconState, + epoch: Epoch, + flag_index: usize, + validator_effective_balance: u64, + validator_slashed: bool, +) -> Result<(), BlockProcessingError> { + if is_progressive_balances_enabled(state) { + state.progressive_balances_cache_mut().on_flag_removed( epoch, validator_slashed, flag_index, diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index eb23a39150..be46fbab09 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -1,4 +1,7 @@ use super::*; +use crate::common::update_progressive_balances_cache::{ + update_progressive_balances_on_flag_added, update_progressive_balances_on_flag_removed, +}; use crate::common::{ get_attestation_participation_flag_indices, increase_balance, initiate_validator_exit, slash_validator, @@ -131,7 +134,6 @@ pub mod base { pub mod altair_deneb { use super::*; - use crate::common::update_progressive_balances_cache::update_progressive_balances_on_attestation; pub fn process_attestations<'a, E: EthSpec, I>( state: &mut BeaconState, @@ -200,7 +202,7 @@ pub mod altair_deneb { proposer_reward_numerator .safe_add_assign(state.get_base_reward(index)?.safe_mul(weight)?)?; - update_progressive_balances_on_attestation( + update_progressive_balances_on_flag_added( state, data.target.epoch, flag_index, @@ -723,11 +725,103 @@ pub fn process_payload_attestation( ctxt: &mut ConsensusContext, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - let _indexed = + let current_epoch = ctxt.current_epoch; + let previous_epoch = ctxt.previous_epoch; + let target_epoch = if state.slot() % E::slots_per_epoch() == Slot::new(0) { + previous_epoch + } else { + current_epoch + }; + let proposer_index = ctxt.get_proposer_index(state, spec)? as usize; + + let indexed_payload_attestation = verify_payload_attestation(state, payload_attestation, ctxt, verify_signatures, spec) .map_err(|e| e.into_with_index(att_index))?; - // TODO((EIP-7732): finish implementing this function + let data = &payload_attestation.data; + let payload_was_present = data.slot == state.latest_full_slot()?; + let voted_present = data.payload_status == PayloadStatus::PayloadPresent; + let proposer_reward_denominator = WEIGHT_DENOMINATOR + .safe_sub(PROPOSER_WEIGHT)? + .safe_mul(WEIGHT_DENOMINATOR)? + .safe_div(PROPOSER_WEIGHT)?; + + if voted_present != payload_was_present { + // Unset the flags in case they were set by equivocating PTC attestation + let mut proposer_penalty_numerator = 0; + + for index in indexed_payload_attestation.attesting_indices_iter() { + let index = *index as usize; + + let validator_effective_balance = state.epoch_cache().get_effective_balance(index)?; + let validator_slashed = state.slashings_cache().is_slashed(index); + + for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() { + let epoch_participation = state.get_epoch_participation_mut( + target_epoch, + previous_epoch, + current_epoch, + )?; + let validator_participation = epoch_participation + .get_mut(index) + .ok_or(BeaconStateError::ParticipationOutOfBounds(index))?; + if validator_participation.has_flag(flag_index)? { + validator_participation.remove_flag(flag_index)?; + proposer_penalty_numerator + .safe_add_assign(state.get_base_reward(index)?.safe_mul(weight)?)?; + + update_progressive_balances_on_flag_removed( + state, + target_epoch, + flag_index, + validator_effective_balance, + validator_slashed, + )?; + } + } + } + // penalize the proposer + let proposer_penalty = proposer_penalty_numerator + .safe_mul(2)? + .safe_div(proposer_reward_denominator)?; + decrease_balance(state, proposer_index, proposer_penalty)?; + + return Ok(()); + } + + // Reward the proposer and set all the participation flags in case of correct attestations + let mut proposer_reward_numerator = 0; + for index in indexed_payload_attestation.attesting_indices_iter() { + let index = *index as usize; + + let validator_effective_balance = state.epoch_cache().get_effective_balance(index)?; + let validator_slashed = state.slashings_cache().is_slashed(index); + + for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() { + let epoch_participation = + state.get_epoch_participation_mut(target_epoch, previous_epoch, current_epoch)?; + let validator_participation = epoch_participation + .get_mut(index) + .ok_or(BeaconStateError::ParticipationOutOfBounds(index))?; + if !validator_participation.has_flag(flag_index)? { + validator_participation.add_flag(flag_index)?; + proposer_reward_numerator + .safe_add_assign(state.get_base_reward(index)?.safe_mul(weight)?)?; + + update_progressive_balances_on_flag_added( + state, + target_epoch, + flag_index, + validator_effective_balance, + validator_slashed, + )?; + } + } + } + + // Reward the proposer + let proposer_reward = proposer_reward_numerator.safe_div(proposer_reward_denominator)?; + increase_balance(state, proposer_index, proposer_reward)?; Ok(()) } @@ -743,9 +837,12 @@ where I: Iterator>, { // Ensure required caches are all built. These should be no-ops during regular operation. - // TODO(EIP-7732): check necessary caches + // TODO(EIP-7732): verify necessary caches state.build_committee_cache(RelativeEpoch::Current, spec)?; state.build_committee_cache(RelativeEpoch::Previous, spec)?; + initialize_epoch_cache(state, spec)?; + initialize_progressive_balances_cache(state, spec)?; + state.build_slashings_cache()?; payload_attestations .enumerate() diff --git a/consensus/state_processing/src/per_block_processing/verify_payload_attestation.rs b/consensus/state_processing/src/per_block_processing/verify_payload_attestation.rs index 85271976f7..fa09073a6e 100644 --- a/consensus/state_processing/src/per_block_processing/verify_payload_attestation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_payload_attestation.rs @@ -31,6 +31,7 @@ pub fn verify_payload_attestation<'ctxt, E: EthSpec>( let indexed_payload_attestation = ctxt.get_indexed_payload_attestation(state, data.slot, &payload_attestation)?; + is_valid_indexed_payload_attestation( state, &indexed_payload_attestation, diff --git a/consensus/types/src/beacon_state/progressive_balances_cache.rs b/consensus/types/src/beacon_state/progressive_balances_cache.rs index 70d9690eb1..a8e5bf8dae 100644 --- a/consensus/types/src/beacon_state/progressive_balances_cache.rs +++ b/consensus/types/src/beacon_state/progressive_balances_cache.rs @@ -60,7 +60,7 @@ impl EpochTotalBalances { .ok_or(BeaconStateError::InvalidFlagIndex(flag_index)) } - pub fn on_new_attestation( + pub fn on_flag_added( &mut self, is_slashed: bool, flag_index: usize, @@ -77,6 +77,23 @@ impl EpochTotalBalances { Ok(()) } + pub fn on_flag_removed( + &mut self, + is_slashed: bool, + flag_index: usize, + validator_effective_balance: u64, + ) -> Result<(), BeaconStateError> { + if is_slashed { + return Ok(()); + } + let balance = self + .total_flag_balances + .get_mut(flag_index) + .ok_or(BeaconStateError::InvalidFlagIndex(flag_index))?; + balance.safe_sub_assign(validator_effective_balance)?; + Ok(()) + } + pub fn on_slashing( &mut self, participation_flags: ParticipationFlags, @@ -148,10 +165,12 @@ impl ProgressiveBalancesCache { .map_or(false, |inner| inner.current_epoch == epoch) } - /// When a new target attestation has been processed, we update the cached - /// `current_epoch_target_attesting_balance` to include the validator effective balance. + /// Updates the `ProgressiveBalancesCache` when a participation flag is added for a validator. + /// + /// This function increments the total balance associated with the specified `flag_index` + /// by the validator's `effective_balance`, provided the validator is not slashed. /// If the epoch is neither the current epoch nor the previous epoch, an error is returned. - pub fn on_new_attestation( + pub fn on_flag_added( &mut self, epoch: Epoch, is_slashed: bool, @@ -161,13 +180,46 @@ impl ProgressiveBalancesCache { let cache = self.get_inner_mut()?; if epoch == cache.current_epoch { - cache.current_epoch_cache.on_new_attestation( + cache.current_epoch_cache.on_flag_added( is_slashed, flag_index, validator_effective_balance, )?; } else if epoch.safe_add(1)? == cache.current_epoch { - cache.previous_epoch_cache.on_new_attestation( + cache.previous_epoch_cache.on_flag_added( + is_slashed, + flag_index, + validator_effective_balance, + )?; + } else { + return Err(BeaconStateError::ProgressiveBalancesCacheInconsistent); + } + + Ok(()) + } + + /// Updates the `ProgressiveBalancesCache` when a participation flag is removed for a validator. + /// + /// This function decrements the total balance associated with the specified `flag_index` + /// by the validator's `effective_balance`, provided the validator is not slashed. + /// If the epoch is neither the current epoch nor the previous epoch, an error is returned. + pub fn on_flag_removed( + &mut self, + epoch: Epoch, + is_slashed: bool, + flag_index: usize, + validator_effective_balance: u64, + ) -> Result<(), BeaconStateError> { + let cache = self.get_inner_mut()?; + + if epoch == cache.current_epoch { + cache.current_epoch_cache.on_flag_removed( + is_slashed, + flag_index, + validator_effective_balance, + )?; + } else if epoch.safe_add(1)? == cache.current_epoch { + cache.previous_epoch_cache.on_flag_removed( is_slashed, flag_index, validator_effective_balance, diff --git a/consensus/types/src/indexed_payload_attestation.rs b/consensus/types/src/indexed_payload_attestation.rs index 3f04c44dc1..c7f588cf57 100644 --- a/consensus/types/src/indexed_payload_attestation.rs +++ b/consensus/types/src/indexed_payload_attestation.rs @@ -1,5 +1,6 @@ use crate::test_utils::TestRandom; use crate::*; +use core::slice::Iter; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; @@ -26,6 +27,12 @@ pub struct IndexedPayloadAttestation { pub signature: AggregateSignature, } +impl IndexedPayloadAttestation { + pub fn attesting_indices_iter(&self) -> Iter<'_, u64> { + self.attesting_indices.iter() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/participation_flags.rs b/consensus/types/src/participation_flags.rs index e94e56f0cd..6a75c82c85 100644 --- a/consensus/types/src/participation_flags.rs +++ b/consensus/types/src/participation_flags.rs @@ -22,6 +22,15 @@ impl ParticipationFlags { Ok(()) } + pub fn remove_flag(&mut self, flag_index: usize) -> Result<(), ArithError> { + if flag_index >= NUM_FLAG_INDICES { + return Err(ArithError::Overflow); + } + let mask = 1u8.safe_shl(flag_index as u32)?; + self.bits &= !mask; + Ok(()) + } + pub fn has_flag(&self, flag_index: usize) -> Result { if flag_index >= NUM_FLAG_INDICES { return Err(ArithError::Overflow);