From 7a15ec99cd85547c4dea7cfef89f2b6954671084 Mon Sep 17 00:00:00 2001 From: Tyera Date: Mon, 15 Apr 2024 17:41:32 -0600 Subject: [PATCH] Move Bank::calculate_stake_vote_rewards to sub-submodule (#827) --- runtime/src/bank.rs | 140 ----------------- .../partitioned_epoch_rewards/calculation.rs | 144 +++++++++++++++++- .../src/bank/partitioned_epoch_rewards/mod.rs | 11 +- 3 files changed, 150 insertions(+), 145 deletions(-) diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 2b2780898..9a87e72c8 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -874,15 +874,6 @@ pub struct CommitTransactionCounts { pub signature_count: u64, } -#[derive(Debug, Default)] -/// result of calculating the stake rewards at end of epoch -struct StakeRewardCalculation { - /// each individual stake account to reward - stake_rewards: StakeRewards, - /// total lamports across all `stake_rewards` - total_stake_rewards_lamports: u64, -} - impl Bank { fn default_with_accounts(accounts: Accounts) -> Self { let mut bank = Self { @@ -2553,137 +2544,6 @@ impl Bank { (points > 0).then_some(PointValue { rewards, points }) } - /// Calculates epoch rewards for stake/vote accounts - /// Returns vote rewards, stake rewards, and the sum of all stake rewards in lamports - fn calculate_stake_vote_rewards( - &self, - reward_calculate_params: &EpochRewardCalculateParamInfo, - rewarded_epoch: Epoch, - point_value: PointValue, - thread_pool: &ThreadPool, - reward_calc_tracer: Option, - metrics: &mut RewardsMetrics, - ) -> (VoteRewardsAccounts, StakeRewardCalculation) { - let EpochRewardCalculateParamInfo { - stake_history, - stake_delegations, - cached_vote_accounts, - } = reward_calculate_params; - - let solana_vote_program: Pubkey = solana_vote_program::id(); - - let get_vote_account = |vote_pubkey: &Pubkey| -> Option { - if let Some(vote_account) = cached_vote_accounts.get(vote_pubkey) { - return Some(vote_account.clone()); - } - // If accounts-db contains a valid vote account, then it should - // already have been cached in cached_vote_accounts; so the code - // below is only for sanity checking, and can be removed once - // the cache is deemed to be reliable. - let account = self.get_account_with_fixed_root(vote_pubkey)?; - VoteAccount::try_from(account).ok() - }; - - let new_warmup_cooldown_rate_epoch = self.new_warmup_cooldown_rate_epoch(); - let vote_account_rewards: VoteRewards = DashMap::new(); - let total_stake_rewards = AtomicU64::default(); - let (stake_rewards, measure_stake_rewards_us) = measure_us!(thread_pool.install(|| { - stake_delegations - .par_iter() - .filter_map(|(stake_pubkey, stake_account)| { - // curry closure to add the contextual stake_pubkey - let reward_calc_tracer = reward_calc_tracer.as_ref().map(|outer| { - // inner - move |inner_event: &_| { - outer(&RewardCalculationEvent::Staking(stake_pubkey, inner_event)) - } - }); - - let stake_pubkey = **stake_pubkey; - let stake_account = (*stake_account).to_owned(); - - let delegation = stake_account.delegation(); - let (mut stake_account, stake_state) = - <(AccountSharedData, StakeStateV2)>::from(stake_account); - let vote_pubkey = delegation.voter_pubkey; - let vote_account = get_vote_account(&vote_pubkey)?; - if vote_account.owner() != &solana_vote_program { - return None; - } - let vote_state = vote_account.vote_state().cloned().ok()?; - - let pre_lamport = stake_account.lamports(); - - let redeemed = solana_stake_program::rewards::redeem_rewards( - rewarded_epoch, - stake_state, - &mut stake_account, - &vote_state, - &point_value, - stake_history, - reward_calc_tracer.as_ref(), - new_warmup_cooldown_rate_epoch, - ); - - let post_lamport = stake_account.lamports(); - - if let Ok((stakers_reward, voters_reward)) = redeemed { - debug!( - "calculated reward: {} {} {} {}", - stake_pubkey, pre_lamport, post_lamport, stakers_reward - ); - - // track voter rewards - let mut voters_reward_entry = vote_account_rewards - .entry(vote_pubkey) - .or_insert(VoteReward { - vote_account: vote_account.into(), - commission: vote_state.commission, - vote_rewards: 0, - vote_needs_store: false, - }); - - voters_reward_entry.vote_needs_store = true; - voters_reward_entry.vote_rewards = voters_reward_entry - .vote_rewards - .saturating_add(voters_reward); - - let post_balance = stake_account.lamports(); - total_stake_rewards.fetch_add(stakers_reward, Relaxed); - return Some(StakeReward { - stake_pubkey, - stake_reward_info: RewardInfo { - reward_type: RewardType::Staking, - lamports: i64::try_from(stakers_reward).unwrap(), - post_balance, - commission: Some(vote_state.commission), - }, - stake_account, - }); - } else { - debug!( - "solana_stake_program::rewards::redeem_rewards() failed for {}: {:?}", - stake_pubkey, redeemed - ); - } - None - }) - .collect() - })); - let (vote_rewards, measure_vote_rewards_us) = - measure_us!(Self::calc_vote_accounts_to_store(vote_account_rewards)); - - metrics.redeem_rewards_us += measure_stake_rewards_us + measure_vote_rewards_us; - - ( - vote_rewards, - StakeRewardCalculation { - stake_rewards, - total_stake_rewards_lamports: total_stake_rewards.load(Relaxed), - }, - ) - } - fn redeem_rewards( &self, vote_with_stake_delegations_map: DashMap, diff --git a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs index e45d94d26..4b86acd92 100644 --- a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs +++ b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs @@ -2,26 +2,31 @@ use { super::{ epoch_rewards_hasher::hash_rewards_into_partitions, Bank, CalculateRewardsAndDistributeVoteRewardsResult, CalculateValidatorRewardsResult, - EpochRewardCalculateParamInfo, PartitionedRewardsCalculation, + EpochRewardCalculateParamInfo, PartitionedRewardsCalculation, StakeRewardCalculation, StakeRewardCalculationPartitioned, VoteRewardsAccounts, }, crate::bank::{ PrevEpochInflationRewards, RewardCalcTracer, RewardCalculationEvent, RewardsMetrics, - VoteAccount, + VoteAccount, VoteReward, VoteRewards, }, - log::info, + dashmap::DashMap, + log::{debug, info}, rayon::{ iter::{IntoParallelRefIterator, ParallelIterator}, ThreadPool, }, + solana_accounts_db::stake_rewards::StakeReward, solana_measure::measure_us, solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, pubkey::Pubkey, reward_info::RewardInfo, + reward_type::RewardType, + stake::state::StakeStateV2, }, solana_stake_program::points::PointValue, - std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::{AtomicU64, Ordering::Relaxed}, }; impl Bank { @@ -288,6 +293,137 @@ impl Bank { }) } + /// Calculates epoch rewards for stake/vote accounts + /// Returns vote rewards, stake rewards, and the sum of all stake rewards in lamports + fn calculate_stake_vote_rewards( + &self, + reward_calculate_params: &EpochRewardCalculateParamInfo, + rewarded_epoch: Epoch, + point_value: PointValue, + thread_pool: &ThreadPool, + reward_calc_tracer: Option, + metrics: &mut RewardsMetrics, + ) -> (VoteRewardsAccounts, StakeRewardCalculation) { + let EpochRewardCalculateParamInfo { + stake_history, + stake_delegations, + cached_vote_accounts, + } = reward_calculate_params; + + let solana_vote_program: Pubkey = solana_vote_program::id(); + + let get_vote_account = |vote_pubkey: &Pubkey| -> Option { + if let Some(vote_account) = cached_vote_accounts.get(vote_pubkey) { + return Some(vote_account.clone()); + } + // If accounts-db contains a valid vote account, then it should + // already have been cached in cached_vote_accounts; so the code + // below is only for sanity checking, and can be removed once + // the cache is deemed to be reliable. + let account = self.get_account_with_fixed_root(vote_pubkey)?; + VoteAccount::try_from(account).ok() + }; + + let new_warmup_cooldown_rate_epoch = self.new_warmup_cooldown_rate_epoch(); + let vote_account_rewards: VoteRewards = DashMap::new(); + let total_stake_rewards = AtomicU64::default(); + let (stake_rewards, measure_stake_rewards_us) = measure_us!(thread_pool.install(|| { + stake_delegations + .par_iter() + .filter_map(|(stake_pubkey, stake_account)| { + // curry closure to add the contextual stake_pubkey + let reward_calc_tracer = reward_calc_tracer.as_ref().map(|outer| { + // inner + move |inner_event: &_| { + outer(&RewardCalculationEvent::Staking(stake_pubkey, inner_event)) + } + }); + + let stake_pubkey = **stake_pubkey; + let stake_account = (*stake_account).to_owned(); + + let delegation = stake_account.delegation(); + let (mut stake_account, stake_state) = + <(AccountSharedData, StakeStateV2)>::from(stake_account); + let vote_pubkey = delegation.voter_pubkey; + let vote_account = get_vote_account(&vote_pubkey)?; + if vote_account.owner() != &solana_vote_program { + return None; + } + let vote_state = vote_account.vote_state().cloned().ok()?; + + let pre_lamport = stake_account.lamports(); + + let redeemed = solana_stake_program::rewards::redeem_rewards( + rewarded_epoch, + stake_state, + &mut stake_account, + &vote_state, + &point_value, + stake_history, + reward_calc_tracer.as_ref(), + new_warmup_cooldown_rate_epoch, + ); + + let post_lamport = stake_account.lamports(); + + if let Ok((stakers_reward, voters_reward)) = redeemed { + debug!( + "calculated reward: {} {} {} {}", + stake_pubkey, pre_lamport, post_lamport, stakers_reward + ); + + // track voter rewards + let mut voters_reward_entry = vote_account_rewards + .entry(vote_pubkey) + .or_insert(VoteReward { + vote_account: vote_account.into(), + commission: vote_state.commission, + vote_rewards: 0, + vote_needs_store: false, + }); + + voters_reward_entry.vote_needs_store = true; + voters_reward_entry.vote_rewards = voters_reward_entry + .vote_rewards + .saturating_add(voters_reward); + + let post_balance = stake_account.lamports(); + total_stake_rewards.fetch_add(stakers_reward, Relaxed); + return Some(StakeReward { + stake_pubkey, + stake_reward_info: RewardInfo { + reward_type: RewardType::Staking, + lamports: i64::try_from(stakers_reward).unwrap(), + post_balance, + commission: Some(vote_state.commission), + }, + stake_account, + }); + } else { + debug!( + "solana_stake_program::rewards::redeem_rewards() failed for {}: {:?}", + stake_pubkey, redeemed + ); + } + None + }) + .collect() + })); + let (vote_rewards, measure_vote_rewards_us) = + measure_us!(Self::calc_vote_accounts_to_store(vote_account_rewards)); + + metrics.redeem_rewards_us += measure_stake_rewards_us + measure_vote_rewards_us; + + ( + vote_rewards, + StakeRewardCalculation { + stake_rewards, + total_stake_rewards_lamports: total_stake_rewards.load(Relaxed), + }, + ) + } + /// Calculates epoch reward points from stake/vote accounts. /// Returns reward lamports and points for the epoch or none if points == 0. fn calculate_reward_points_partitioned( diff --git a/runtime/src/bank/partitioned_epoch_rewards/mod.rs b/runtime/src/bank/partitioned_epoch_rewards/mod.rs index 3b42734a9..530be52e0 100644 --- a/runtime/src/bank/partitioned_epoch_rewards/mod.rs +++ b/runtime/src/bank/partitioned_epoch_rewards/mod.rs @@ -5,7 +5,7 @@ mod epoch_rewards_hasher; mod sysvar; use { - super::{Bank, StakeRewardCalculation}, + super::Bank, crate::{stake_account::StakeAccount, stake_history::StakeHistory}, solana_accounts_db::{ partitioned_rewards::PartitionedEpochRewardsConfig, stake_rewards::StakeReward, @@ -50,6 +50,15 @@ pub(super) struct VoteRewardsAccounts { pub(super) accounts_to_store: Vec>, } +#[derive(Debug, Default)] +/// result of calculating the stake rewards at end of epoch +struct StakeRewardCalculation { + /// each individual stake account to reward + stake_rewards: StakeRewards, + /// total lamports across all `stake_rewards` + total_stake_rewards_lamports: u64, +} + #[derive(Debug, Default)] struct CalculateValidatorRewardsResult { vote_rewards_accounts: VoteRewardsAccounts,