|
@@ -48,12 +48,11 @@ use {
|
|
|
serde_snapshot::BankIncrementalSnapshotPersistence,
|
|
serde_snapshot::BankIncrementalSnapshotPersistence,
|
|
|
snapshot_hash::SnapshotHash,
|
|
snapshot_hash::SnapshotHash,
|
|
|
stake_account::StakeAccount,
|
|
stake_account::StakeAccount,
|
|
|
- stake_history::StakeHistory,
|
|
|
|
|
stake_weighted_timestamp::{
|
|
stake_weighted_timestamp::{
|
|
|
calculate_stake_weighted_timestamp, MaxAllowableDrift,
|
|
calculate_stake_weighted_timestamp, MaxAllowableDrift,
|
|
|
MAX_ALLOWABLE_DRIFT_PERCENTAGE_FAST, MAX_ALLOWABLE_DRIFT_PERCENTAGE_SLOW_V2,
|
|
MAX_ALLOWABLE_DRIFT_PERCENTAGE_FAST, MAX_ALLOWABLE_DRIFT_PERCENTAGE_SLOW_V2,
|
|
|
},
|
|
},
|
|
|
- stakes::{InvalidCacheEntryReason, Stakes, StakesCache, StakesEnum},
|
|
|
|
|
|
|
+ stakes::{Stakes, StakesCache, StakesEnum},
|
|
|
status_cache::{SlotDelta, StatusCache},
|
|
status_cache::{SlotDelta, StatusCache},
|
|
|
transaction_batch::{OwnedOrBorrowed, TransactionBatch},
|
|
transaction_batch::{OwnedOrBorrowed, TransactionBatch},
|
|
|
verify_precompiles::verify_precompiles,
|
|
verify_precompiles::verify_precompiles,
|
|
@@ -65,8 +64,7 @@ use {
|
|
|
log::*,
|
|
log::*,
|
|
|
rayon::{
|
|
rayon::{
|
|
|
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
|
|
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
|
|
|
- slice::ParallelSlice,
|
|
|
|
|
- ThreadPool, ThreadPoolBuilder,
|
|
|
|
|
|
|
+ ThreadPoolBuilder,
|
|
|
},
|
|
},
|
|
|
serde::Serialize,
|
|
serde::Serialize,
|
|
|
solana_accounts_db::{
|
|
solana_accounts_db::{
|
|
@@ -88,7 +86,6 @@ use {
|
|
|
blockhash_queue::BlockhashQueue,
|
|
blockhash_queue::BlockhashQueue,
|
|
|
epoch_accounts_hash::EpochAccountsHash,
|
|
epoch_accounts_hash::EpochAccountsHash,
|
|
|
sorted_storages::SortedStorages,
|
|
sorted_storages::SortedStorages,
|
|
|
- stake_rewards::StakeReward,
|
|
|
|
|
storable_accounts::StorableAccounts,
|
|
storable_accounts::StorableAccounts,
|
|
|
},
|
|
},
|
|
|
solana_bpf_loader_program::syscalls::{
|
|
solana_bpf_loader_program::syscalls::{
|
|
@@ -157,10 +154,7 @@ use {
|
|
|
},
|
|
},
|
|
|
transaction_context::{TransactionAccount, TransactionReturnData},
|
|
transaction_context::{TransactionAccount, TransactionReturnData},
|
|
|
},
|
|
},
|
|
|
- solana_stake_program::{
|
|
|
|
|
- points::{InflationPointCalculationEvent, PointValue},
|
|
|
|
|
- stake_state::StakeStateV2,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ solana_stake_program::points::InflationPointCalculationEvent,
|
|
|
solana_svm::{
|
|
solana_svm::{
|
|
|
account_loader::{collect_rent_from_account, LoadedTransaction},
|
|
account_loader::{collect_rent_from_account, LoadedTransaction},
|
|
|
account_overrides::AccountOverrides,
|
|
account_overrides::AccountOverrides,
|
|
@@ -182,7 +176,6 @@ use {
|
|
|
solana_svm_transaction::svm_message::SVMMessage,
|
|
solana_svm_transaction::svm_message::SVMMessage,
|
|
|
solana_timings::{ExecuteTimingType, ExecuteTimings},
|
|
solana_timings::{ExecuteTimingType, ExecuteTimings},
|
|
|
solana_vote::vote_account::{VoteAccount, VoteAccountsHashMap},
|
|
solana_vote::vote_account::{VoteAccount, VoteAccountsHashMap},
|
|
|
- solana_vote_program::vote_state::VoteState,
|
|
|
|
|
std::{
|
|
std::{
|
|
|
collections::{HashMap, HashSet},
|
|
collections::{HashMap, HashSet},
|
|
|
convert::TryFrom,
|
|
convert::TryFrom,
|
|
@@ -954,22 +947,6 @@ pub struct Bank {
|
|
|
bank_hash_stats: AtomicBankHashStats,
|
|
bank_hash_stats: AtomicBankHashStats,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-struct VoteWithStakeDelegations {
|
|
|
|
|
- vote_state: Arc<VoteState>,
|
|
|
|
|
- vote_account: AccountSharedData,
|
|
|
|
|
- delegations: Vec<(Pubkey, StakeAccount<Delegation>)>,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-type VoteWithStakeDelegationsMap = DashMap<Pubkey, VoteWithStakeDelegations>;
|
|
|
|
|
-
|
|
|
|
|
-type InvalidCacheKeyMap = DashMap<Pubkey, InvalidCacheEntryReason>;
|
|
|
|
|
-
|
|
|
|
|
-struct LoadVoteAndStakeAccountsResult {
|
|
|
|
|
- vote_with_stake_delegations_map: VoteWithStakeDelegationsMap,
|
|
|
|
|
- invalid_vote_keys: InvalidCacheKeyMap,
|
|
|
|
|
- vote_accounts_cache_miss_count: usize,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
|
struct VoteReward {
|
|
struct VoteReward {
|
|
|
vote_account: AccountSharedData,
|
|
vote_account: AccountSharedData,
|
|
@@ -1449,9 +1426,7 @@ impl Bank {
|
|
|
let leader_schedule_epoch = new.epoch_schedule().get_leader_schedule_epoch(slot);
|
|
let leader_schedule_epoch = new.epoch_schedule().get_leader_schedule_epoch(slot);
|
|
|
new.update_epoch_stakes(leader_schedule_epoch);
|
|
new.update_epoch_stakes(leader_schedule_epoch);
|
|
|
}
|
|
}
|
|
|
- if new.is_partitioned_rewards_code_enabled() {
|
|
|
|
|
- new.distribute_partitioned_epoch_rewards();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ new.distribute_partitioned_epoch_rewards();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
let (_epoch, slot_index) = new.epoch_schedule.get_epoch_and_slot_index(new.slot);
|
|
let (_epoch, slot_index) = new.epoch_schedule.get_epoch_and_slot_index(new.slot);
|
|
@@ -1617,24 +1592,15 @@ impl Bank {
|
|
|
|
|
|
|
|
let mut rewards_metrics = RewardsMetrics::default();
|
|
let mut rewards_metrics = RewardsMetrics::default();
|
|
|
// After saving a snapshot of stakes, apply stake rewards and commission
|
|
// After saving a snapshot of stakes, apply stake rewards and commission
|
|
|
- let (_, update_rewards_with_thread_pool_time_us) =
|
|
|
|
|
- measure_us!(if self.is_partitioned_rewards_code_enabled() {
|
|
|
|
|
- self.begin_partitioned_rewards(
|
|
|
|
|
- reward_calc_tracer,
|
|
|
|
|
- &thread_pool,
|
|
|
|
|
- parent_epoch,
|
|
|
|
|
- parent_slot,
|
|
|
|
|
- parent_height,
|
|
|
|
|
- &mut rewards_metrics,
|
|
|
|
|
- );
|
|
|
|
|
- } else {
|
|
|
|
|
- self.update_rewards_with_thread_pool(
|
|
|
|
|
- parent_epoch,
|
|
|
|
|
- reward_calc_tracer,
|
|
|
|
|
- &thread_pool,
|
|
|
|
|
- &mut rewards_metrics,
|
|
|
|
|
- )
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ let (_, update_rewards_with_thread_pool_time_us) = measure_us!(self
|
|
|
|
|
+ .begin_partitioned_rewards(
|
|
|
|
|
+ reward_calc_tracer,
|
|
|
|
|
+ &thread_pool,
|
|
|
|
|
+ parent_epoch,
|
|
|
|
|
+ parent_slot,
|
|
|
|
|
+ parent_height,
|
|
|
|
|
+ &mut rewards_metrics,
|
|
|
|
|
+ ));
|
|
|
|
|
|
|
|
report_new_epoch_metrics(
|
|
report_new_epoch_metrics(
|
|
|
epoch,
|
|
epoch,
|
|
@@ -2417,93 +2383,6 @@ impl Bank {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // update rewards based on the previous epoch
|
|
|
|
|
- fn update_rewards_with_thread_pool(
|
|
|
|
|
- &mut self,
|
|
|
|
|
- prev_epoch: Epoch,
|
|
|
|
|
- reward_calc_tracer: Option<impl Fn(&RewardCalculationEvent) + Send + Sync>,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- metrics: &mut RewardsMetrics,
|
|
|
|
|
- ) {
|
|
|
|
|
- let capitalization = self.capitalization();
|
|
|
|
|
- let PrevEpochInflationRewards {
|
|
|
|
|
- validator_rewards,
|
|
|
|
|
- prev_epoch_duration_in_years,
|
|
|
|
|
- validator_rate,
|
|
|
|
|
- foundation_rate,
|
|
|
|
|
- } = self.calculate_previous_epoch_inflation_rewards(capitalization, prev_epoch);
|
|
|
|
|
-
|
|
|
|
|
- let old_vote_balance_and_staked = self.stakes_cache.stakes().vote_balance_and_staked();
|
|
|
|
|
-
|
|
|
|
|
- self.pay_validator_rewards_with_thread_pool(
|
|
|
|
|
- prev_epoch,
|
|
|
|
|
- validator_rewards,
|
|
|
|
|
- reward_calc_tracer,
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- metrics,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let new_vote_balance_and_staked = self.stakes_cache.stakes().vote_balance_and_staked();
|
|
|
|
|
- let validator_rewards_paid = new_vote_balance_and_staked - old_vote_balance_and_staked;
|
|
|
|
|
- assert_eq!(
|
|
|
|
|
- validator_rewards_paid,
|
|
|
|
|
- u64::try_from(
|
|
|
|
|
- self.rewards
|
|
|
|
|
- .read()
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- .iter()
|
|
|
|
|
- .map(|(_address, reward_info)| {
|
|
|
|
|
- match reward_info.reward_type {
|
|
|
|
|
- RewardType::Voting | RewardType::Staking => reward_info.lamports,
|
|
|
|
|
- _ => 0,
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .sum::<i64>()
|
|
|
|
|
- )
|
|
|
|
|
- .unwrap()
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // verify that we didn't pay any more than we expected to
|
|
|
|
|
- assert!(validator_rewards >= validator_rewards_paid);
|
|
|
|
|
-
|
|
|
|
|
- info!(
|
|
|
|
|
- "distributed inflation: {} (rounded from: {})",
|
|
|
|
|
- validator_rewards_paid, validator_rewards
|
|
|
|
|
- );
|
|
|
|
|
- let (num_stake_accounts, num_vote_accounts) = {
|
|
|
|
|
- let stakes = self.stakes_cache.stakes();
|
|
|
|
|
- (
|
|
|
|
|
- stakes.stake_delegations().len(),
|
|
|
|
|
- stakes.vote_accounts().len(),
|
|
|
|
|
- )
|
|
|
|
|
- };
|
|
|
|
|
- self.capitalization
|
|
|
|
|
- .fetch_add(validator_rewards_paid, Relaxed);
|
|
|
|
|
-
|
|
|
|
|
- let active_stake = if let Some(stake_history_entry) =
|
|
|
|
|
- self.stakes_cache.stakes().history().get(prev_epoch)
|
|
|
|
|
- {
|
|
|
|
|
- stake_history_entry.effective
|
|
|
|
|
- } else {
|
|
|
|
|
- 0
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- datapoint_warn!(
|
|
|
|
|
- "epoch_rewards",
|
|
|
|
|
- ("slot", self.slot, i64),
|
|
|
|
|
- ("epoch", prev_epoch, i64),
|
|
|
|
|
- ("validator_rate", validator_rate, f64),
|
|
|
|
|
- ("foundation_rate", foundation_rate, f64),
|
|
|
|
|
- ("epoch_duration_in_years", prev_epoch_duration_in_years, f64),
|
|
|
|
|
- ("validator_rewards", validator_rewards_paid, i64),
|
|
|
|
|
- ("active_stake", active_stake, i64),
|
|
|
|
|
- ("pre_capitalization", capitalization, i64),
|
|
|
|
|
- ("post_capitalization", self.capitalization(), i64),
|
|
|
|
|
- ("num_stake_accounts", num_stake_accounts, i64),
|
|
|
|
|
- ("num_vote_accounts", num_vote_accounts, i64),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
fn filter_stake_delegations<'a>(
|
|
fn filter_stake_delegations<'a>(
|
|
|
&self,
|
|
&self,
|
|
|
stakes: &'a Stakes<StakeAccount<Delegation>>,
|
|
stakes: &'a Stakes<StakeAccount<Delegation>>,
|
|
@@ -2537,396 +2416,6 @@ impl Bank {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn _load_vote_and_stake_accounts(
|
|
|
|
|
- &self,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- reward_calc_tracer: Option<impl RewardCalcTracer>,
|
|
|
|
|
- ) -> LoadVoteAndStakeAccountsResult {
|
|
|
|
|
- let stakes = self.stakes_cache.stakes();
|
|
|
|
|
- let stake_delegations = self.filter_stake_delegations(&stakes);
|
|
|
|
|
-
|
|
|
|
|
- // Obtain all unique voter pubkeys from stake delegations.
|
|
|
|
|
- fn merge(mut acc: HashSet<Pubkey>, other: HashSet<Pubkey>) -> HashSet<Pubkey> {
|
|
|
|
|
- if acc.len() < other.len() {
|
|
|
|
|
- return merge(other, acc);
|
|
|
|
|
- }
|
|
|
|
|
- acc.extend(other);
|
|
|
|
|
- acc
|
|
|
|
|
- }
|
|
|
|
|
- let voter_pubkeys = thread_pool.install(|| {
|
|
|
|
|
- stake_delegations
|
|
|
|
|
- .par_iter()
|
|
|
|
|
- .fold(
|
|
|
|
|
- HashSet::default,
|
|
|
|
|
- |mut voter_pubkeys, (_stake_pubkey, stake_account)| {
|
|
|
|
|
- voter_pubkeys.insert(stake_account.delegation().voter_pubkey);
|
|
|
|
|
- voter_pubkeys
|
|
|
|
|
- },
|
|
|
|
|
- )
|
|
|
|
|
- .reduce(HashSet::default, merge)
|
|
|
|
|
- });
|
|
|
|
|
- // Obtain vote-accounts for unique voter pubkeys.
|
|
|
|
|
- let cached_vote_accounts = stakes.vote_accounts();
|
|
|
|
|
- let solana_vote_program: Pubkey = solana_vote_program::id();
|
|
|
|
|
- let vote_accounts_cache_miss_count = AtomicUsize::default();
|
|
|
|
|
- let get_vote_account = |vote_pubkey: &Pubkey| -> Option<VoteAccount> {
|
|
|
|
|
- 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 check, and can be removed once
|
|
|
|
|
- // vote_accounts_cache_miss_count is shown to be always zero.
|
|
|
|
|
- let account = self.get_account_with_fixed_root(vote_pubkey)?;
|
|
|
|
|
- if account.owner() == &solana_vote_program
|
|
|
|
|
- && VoteState::deserialize(account.data()).is_ok()
|
|
|
|
|
- {
|
|
|
|
|
- vote_accounts_cache_miss_count.fetch_add(1, Relaxed);
|
|
|
|
|
- }
|
|
|
|
|
- VoteAccount::try_from(account).ok()
|
|
|
|
|
- };
|
|
|
|
|
- let invalid_vote_keys = DashMap::<Pubkey, InvalidCacheEntryReason>::new();
|
|
|
|
|
- let make_vote_delegations_entry = |vote_pubkey| {
|
|
|
|
|
- let Some(vote_account) = get_vote_account(&vote_pubkey) else {
|
|
|
|
|
- invalid_vote_keys.insert(vote_pubkey, InvalidCacheEntryReason::Missing);
|
|
|
|
|
- return None;
|
|
|
|
|
- };
|
|
|
|
|
- if vote_account.owner() != &solana_vote_program {
|
|
|
|
|
- invalid_vote_keys.insert(vote_pubkey, InvalidCacheEntryReason::WrongOwner);
|
|
|
|
|
- return None;
|
|
|
|
|
- }
|
|
|
|
|
- let vote_with_stake_delegations = VoteWithStakeDelegations {
|
|
|
|
|
- vote_state: Arc::new(vote_account.vote_state().clone()),
|
|
|
|
|
- vote_account: AccountSharedData::from(vote_account),
|
|
|
|
|
- delegations: Vec::default(),
|
|
|
|
|
- };
|
|
|
|
|
- Some((vote_pubkey, vote_with_stake_delegations))
|
|
|
|
|
- };
|
|
|
|
|
- let vote_with_stake_delegations_map: DashMap<Pubkey, VoteWithStakeDelegations> =
|
|
|
|
|
- thread_pool.install(|| {
|
|
|
|
|
- voter_pubkeys
|
|
|
|
|
- .into_par_iter()
|
|
|
|
|
- .filter_map(make_vote_delegations_entry)
|
|
|
|
|
- .collect()
|
|
|
|
|
- });
|
|
|
|
|
- // Join stake accounts with vote-accounts.
|
|
|
|
|
- let push_stake_delegation = |(stake_pubkey, stake_account): (&Pubkey, &StakeAccount<_>)| {
|
|
|
|
|
- let delegation = stake_account.delegation();
|
|
|
|
|
- let Some(mut vote_delegations) =
|
|
|
|
|
- vote_with_stake_delegations_map.get_mut(&delegation.voter_pubkey)
|
|
|
|
|
- else {
|
|
|
|
|
- return;
|
|
|
|
|
- };
|
|
|
|
|
- if let Some(reward_calc_tracer) = reward_calc_tracer.as_ref() {
|
|
|
|
|
- let delegation =
|
|
|
|
|
- InflationPointCalculationEvent::Delegation(*delegation, solana_vote_program);
|
|
|
|
|
- let event = RewardCalculationEvent::Staking(stake_pubkey, &delegation);
|
|
|
|
|
- reward_calc_tracer(&event);
|
|
|
|
|
- }
|
|
|
|
|
- let stake_delegation = (*stake_pubkey, stake_account.clone());
|
|
|
|
|
- vote_delegations.delegations.push(stake_delegation);
|
|
|
|
|
- };
|
|
|
|
|
- thread_pool.install(|| {
|
|
|
|
|
- stake_delegations
|
|
|
|
|
- .into_par_iter()
|
|
|
|
|
- .for_each(push_stake_delegation);
|
|
|
|
|
- });
|
|
|
|
|
- LoadVoteAndStakeAccountsResult {
|
|
|
|
|
- vote_with_stake_delegations_map,
|
|
|
|
|
- invalid_vote_keys,
|
|
|
|
|
- vote_accounts_cache_miss_count: vote_accounts_cache_miss_count.into_inner(),
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// Load, calculate and payout epoch rewards for stake and vote accounts
|
|
|
|
|
- fn pay_validator_rewards_with_thread_pool(
|
|
|
|
|
- &mut self,
|
|
|
|
|
- rewarded_epoch: Epoch,
|
|
|
|
|
- rewards: u64,
|
|
|
|
|
- reward_calc_tracer: Option<impl RewardCalcTracer>,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- metrics: &mut RewardsMetrics,
|
|
|
|
|
- ) {
|
|
|
|
|
- let stake_history = self.stakes_cache.stakes().history().clone();
|
|
|
|
|
- let vote_with_stake_delegations_map =
|
|
|
|
|
- self.load_vote_and_stake_accounts(thread_pool, reward_calc_tracer.as_ref(), metrics);
|
|
|
|
|
-
|
|
|
|
|
- let point_value = self.calculate_reward_points(
|
|
|
|
|
- &vote_with_stake_delegations_map,
|
|
|
|
|
- rewards,
|
|
|
|
|
- &stake_history,
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- metrics,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- if let Some(point_value) = point_value {
|
|
|
|
|
- let (vote_account_rewards, stake_rewards) = self.redeem_rewards(
|
|
|
|
|
- vote_with_stake_delegations_map,
|
|
|
|
|
- rewarded_epoch,
|
|
|
|
|
- point_value,
|
|
|
|
|
- &stake_history,
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- reward_calc_tracer.as_ref(),
|
|
|
|
|
- metrics,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // this checking of an unactivated feature can be enabled in tests or with a validator by passing `--partitioned-epoch-rewards-compare-calculation`
|
|
|
|
|
- if self
|
|
|
|
|
- .partitioned_epoch_rewards_config()
|
|
|
|
|
- .test_compare_partitioned_epoch_rewards
|
|
|
|
|
- {
|
|
|
|
|
- // immutable `&self` to avoid side effects
|
|
|
|
|
- (self as &Bank).compare_with_partitioned_rewards(
|
|
|
|
|
- &stake_rewards,
|
|
|
|
|
- &vote_account_rewards,
|
|
|
|
|
- rewarded_epoch,
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- null_tracer(),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- self.store_stake_accounts(thread_pool, &stake_rewards, metrics);
|
|
|
|
|
- let vote_rewards = self.store_vote_accounts(vote_account_rewards, metrics);
|
|
|
|
|
- self.update_reward_history(stake_rewards, vote_rewards);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn load_vote_and_stake_accounts(
|
|
|
|
|
- &mut self,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- reward_calc_tracer: Option<impl RewardCalcTracer>,
|
|
|
|
|
- metrics: &mut RewardsMetrics,
|
|
|
|
|
- ) -> VoteWithStakeDelegationsMap {
|
|
|
|
|
- let (
|
|
|
|
|
- LoadVoteAndStakeAccountsResult {
|
|
|
|
|
- vote_with_stake_delegations_map,
|
|
|
|
|
- invalid_vote_keys,
|
|
|
|
|
- vote_accounts_cache_miss_count,
|
|
|
|
|
- },
|
|
|
|
|
- load_vote_and_stake_accounts_us,
|
|
|
|
|
- ) = measure_us!({
|
|
|
|
|
- self._load_vote_and_stake_accounts(thread_pool, reward_calc_tracer.as_ref())
|
|
|
|
|
- });
|
|
|
|
|
- metrics
|
|
|
|
|
- .load_vote_and_stake_accounts_us
|
|
|
|
|
- .fetch_add(load_vote_and_stake_accounts_us, Relaxed);
|
|
|
|
|
- metrics.vote_accounts_cache_miss_count += vote_accounts_cache_miss_count;
|
|
|
|
|
- self.stakes_cache
|
|
|
|
|
- .handle_invalid_keys(invalid_vote_keys, self.slot());
|
|
|
|
|
- vote_with_stake_delegations_map
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn calculate_reward_points(
|
|
|
|
|
- &self,
|
|
|
|
|
- vote_with_stake_delegations_map: &VoteWithStakeDelegationsMap,
|
|
|
|
|
- rewards: u64,
|
|
|
|
|
- stake_history: &StakeHistory,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- metrics: &RewardsMetrics,
|
|
|
|
|
- ) -> Option<PointValue> {
|
|
|
|
|
- let new_warmup_cooldown_rate_epoch = self.new_warmup_cooldown_rate_epoch();
|
|
|
|
|
- let (points, calculate_points_us) = measure_us!(thread_pool.install(|| {
|
|
|
|
|
- vote_with_stake_delegations_map
|
|
|
|
|
- .par_iter()
|
|
|
|
|
- .map(|entry| {
|
|
|
|
|
- let VoteWithStakeDelegations {
|
|
|
|
|
- vote_state,
|
|
|
|
|
- delegations,
|
|
|
|
|
- ..
|
|
|
|
|
- } = entry.value();
|
|
|
|
|
-
|
|
|
|
|
- delegations
|
|
|
|
|
- .par_iter()
|
|
|
|
|
- .map(|(_stake_pubkey, stake_account)| {
|
|
|
|
|
- solana_stake_program::points::calculate_points(
|
|
|
|
|
- stake_account.stake_state(),
|
|
|
|
|
- vote_state,
|
|
|
|
|
- stake_history,
|
|
|
|
|
- new_warmup_cooldown_rate_epoch,
|
|
|
|
|
- )
|
|
|
|
|
- .unwrap_or(0)
|
|
|
|
|
- })
|
|
|
|
|
- .sum::<u128>()
|
|
|
|
|
- })
|
|
|
|
|
- .sum()
|
|
|
|
|
- }));
|
|
|
|
|
- metrics
|
|
|
|
|
- .calculate_points_us
|
|
|
|
|
- .fetch_add(calculate_points_us, Relaxed);
|
|
|
|
|
-
|
|
|
|
|
- (points > 0).then_some(PointValue { rewards, points })
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn redeem_rewards(
|
|
|
|
|
- &self,
|
|
|
|
|
- vote_with_stake_delegations_map: DashMap<Pubkey, VoteWithStakeDelegations>,
|
|
|
|
|
- rewarded_epoch: Epoch,
|
|
|
|
|
- point_value: PointValue,
|
|
|
|
|
- stake_history: &StakeHistory,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- reward_calc_tracer: Option<impl RewardCalcTracer>,
|
|
|
|
|
- metrics: &mut RewardsMetrics,
|
|
|
|
|
- ) -> (VoteRewards, StakeRewards) {
|
|
|
|
|
- let new_warmup_cooldown_rate_epoch = self.new_warmup_cooldown_rate_epoch();
|
|
|
|
|
- let vote_account_rewards: VoteRewards =
|
|
|
|
|
- DashMap::with_capacity(vote_with_stake_delegations_map.len());
|
|
|
|
|
- let stake_delegation_iterator = vote_with_stake_delegations_map.into_par_iter().flat_map(
|
|
|
|
|
- |(
|
|
|
|
|
- vote_pubkey,
|
|
|
|
|
- VoteWithStakeDelegations {
|
|
|
|
|
- vote_state,
|
|
|
|
|
- vote_account,
|
|
|
|
|
- delegations,
|
|
|
|
|
- },
|
|
|
|
|
- )| {
|
|
|
|
|
- vote_account_rewards.insert(
|
|
|
|
|
- vote_pubkey,
|
|
|
|
|
- VoteReward {
|
|
|
|
|
- vote_account,
|
|
|
|
|
- commission: vote_state.commission,
|
|
|
|
|
- vote_rewards: 0,
|
|
|
|
|
- vote_needs_store: false,
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
- delegations
|
|
|
|
|
- .into_par_iter()
|
|
|
|
|
- .map(move |delegation| (vote_pubkey, Arc::clone(&vote_state), delegation))
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let (stake_rewards, redeem_rewards_us) = measure_us!(thread_pool.install(|| {
|
|
|
|
|
- stake_delegation_iterator
|
|
|
|
|
- .filter_map(|(vote_pubkey, vote_state, (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 (mut stake_account, stake_state) =
|
|
|
|
|
- <(AccountSharedData, StakeStateV2)>::from(stake_account);
|
|
|
|
|
- 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,
|
|
|
|
|
- );
|
|
|
|
|
- if let Ok((stakers_reward, voters_reward)) = redeemed {
|
|
|
|
|
- // track voter rewards
|
|
|
|
|
- if let Some(VoteReward {
|
|
|
|
|
- vote_account: _,
|
|
|
|
|
- commission: _,
|
|
|
|
|
- vote_rewards: vote_rewards_sum,
|
|
|
|
|
- vote_needs_store,
|
|
|
|
|
- }) = vote_account_rewards.get_mut(&vote_pubkey).as_deref_mut()
|
|
|
|
|
- {
|
|
|
|
|
- *vote_needs_store = true;
|
|
|
|
|
- *vote_rewards_sum = vote_rewards_sum.saturating_add(voters_reward);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let post_balance = stake_account.lamports();
|
|
|
|
|
- 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()
|
|
|
|
|
- }));
|
|
|
|
|
- metrics.redeem_rewards_us += redeem_rewards_us;
|
|
|
|
|
- (vote_account_rewards, stake_rewards)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn store_stake_accounts(
|
|
|
|
|
- &self,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- stake_rewards: &[StakeReward],
|
|
|
|
|
- metrics: &RewardsMetrics,
|
|
|
|
|
- ) {
|
|
|
|
|
- // store stake account even if stake_reward is 0
|
|
|
|
|
- // because credits observed has changed
|
|
|
|
|
- let now = Instant::now();
|
|
|
|
|
- let slot = self.slot();
|
|
|
|
|
- self.stakes_cache.update_stake_accounts(
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- stake_rewards,
|
|
|
|
|
- self.new_warmup_cooldown_rate_epoch(),
|
|
|
|
|
- );
|
|
|
|
|
- assert!(!self.freeze_started());
|
|
|
|
|
- thread_pool.install(|| {
|
|
|
|
|
- stake_rewards.par_chunks(512).for_each(|chunk| {
|
|
|
|
|
- let to_store = (slot, chunk);
|
|
|
|
|
- self.update_bank_hash_stats(&to_store);
|
|
|
|
|
- self.rc.accounts.store_accounts_cached(to_store);
|
|
|
|
|
- })
|
|
|
|
|
- });
|
|
|
|
|
- metrics
|
|
|
|
|
- .store_stake_accounts_us
|
|
|
|
|
- .fetch_add(now.elapsed().as_micros() as u64, Relaxed);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn store_vote_accounts(
|
|
|
|
|
- &self,
|
|
|
|
|
- vote_account_rewards: VoteRewards,
|
|
|
|
|
- metrics: &RewardsMetrics,
|
|
|
|
|
- ) -> Vec<(Pubkey, RewardInfo)> {
|
|
|
|
|
- let (vote_rewards, store_vote_accounts_us) = measure_us!(vote_account_rewards
|
|
|
|
|
- .into_iter()
|
|
|
|
|
- .filter_map(
|
|
|
|
|
- |(
|
|
|
|
|
- vote_pubkey,
|
|
|
|
|
- VoteReward {
|
|
|
|
|
- mut vote_account,
|
|
|
|
|
- commission,
|
|
|
|
|
- vote_rewards,
|
|
|
|
|
- vote_needs_store,
|
|
|
|
|
- },
|
|
|
|
|
- )| {
|
|
|
|
|
- if let Err(err) = vote_account.checked_add_lamports(vote_rewards) {
|
|
|
|
|
- debug!("reward redemption failed for {}: {:?}", vote_pubkey, err);
|
|
|
|
|
- return None;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if vote_needs_store {
|
|
|
|
|
- self.store_account(&vote_pubkey, &vote_account);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Some((
|
|
|
|
|
- vote_pubkey,
|
|
|
|
|
- RewardInfo {
|
|
|
|
|
- reward_type: RewardType::Voting,
|
|
|
|
|
- lamports: vote_rewards as i64,
|
|
|
|
|
- post_balance: vote_account.lamports(),
|
|
|
|
|
- commission: Some(commission),
|
|
|
|
|
- },
|
|
|
|
|
- ))
|
|
|
|
|
- },
|
|
|
|
|
- )
|
|
|
|
|
- .collect::<Vec<_>>());
|
|
|
|
|
-
|
|
|
|
|
- metrics
|
|
|
|
|
- .store_vote_accounts_us
|
|
|
|
|
- .fetch_add(store_vote_accounts_us, Relaxed);
|
|
|
|
|
- vote_rewards
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/// return reward info for each vote account
|
|
/// return reward info for each vote account
|
|
|
/// return account data for each vote account that needs to be stored
|
|
/// return account data for each vote account that needs to be stored
|
|
|
/// This return value is a little awkward at the moment so that downstream existing code in the non-partitioned rewards code path can be re-used without duplication or modification.
|
|
/// This return value is a little awkward at the moment so that downstream existing code in the non-partitioned rewards code path can be re-used without duplication or modification.
|
|
@@ -5673,16 +5162,13 @@ impl Bank {
|
|
|
let measure_total = Measure::start("");
|
|
let measure_total = Measure::start("");
|
|
|
|
|
|
|
|
let slot = self.slot();
|
|
let slot = self.slot();
|
|
|
- let ignore = (!self.is_partitioned_rewards_feature_enabled()
|
|
|
|
|
- && self.force_partition_rewards_in_first_block_of_epoch())
|
|
|
|
|
- .then_some(sysvar::epoch_rewards::id());
|
|
|
|
|
let (accounts_delta_hash, accounts_delta_hash_us) = measure_us!({
|
|
let (accounts_delta_hash, accounts_delta_hash_us) = measure_us!({
|
|
|
self.rc
|
|
self.rc
|
|
|
.accounts
|
|
.accounts
|
|
|
.accounts_db
|
|
.accounts_db
|
|
|
.calculate_accounts_delta_hash_internal(
|
|
.calculate_accounts_delta_hash_internal(
|
|
|
slot,
|
|
slot,
|
|
|
- ignore,
|
|
|
|
|
|
|
+ None,
|
|
|
self.skipped_rewrites.lock().unwrap().clone(),
|
|
self.skipped_rewrites.lock().unwrap().clone(),
|
|
|
)
|
|
)
|
|
|
});
|
|
});
|