Revert "Revert "resolve conflicts (#21795)""

This reverts commit b73d23d50a.
This commit is contained in:
Tyera Eulberg
2021-12-16 14:28:41 -07:00
committed by Tyera Eulberg
parent 89524d7b61
commit 0f6f0545d1
4 changed files with 177 additions and 131 deletions

View File

@ -57,7 +57,7 @@ use {
calculate_stake_weighted_timestamp, MaxAllowableDrift, MAX_ALLOWABLE_DRIFT_PERCENTAGE, calculate_stake_weighted_timestamp, MaxAllowableDrift, MAX_ALLOWABLE_DRIFT_PERCENTAGE,
MAX_ALLOWABLE_DRIFT_PERCENTAGE_FAST, MAX_ALLOWABLE_DRIFT_PERCENTAGE_SLOW, MAX_ALLOWABLE_DRIFT_PERCENTAGE_FAST, MAX_ALLOWABLE_DRIFT_PERCENTAGE_SLOW,
}, },
stakes::Stakes, stakes::{InvalidCacheEntryReason, Stakes, StakesCache},
status_cache::{SlotDelta, StatusCache}, status_cache::{SlotDelta, StatusCache},
system_instruction_processor::{get_system_account_kind, SystemAccountKind}, system_instruction_processor::{get_system_account_kind, SystemAccountKind},
transaction_batch::TransactionBatch, transaction_batch::TransactionBatch,
@ -67,8 +67,6 @@ use {
dashmap::DashMap, dashmap::DashMap,
itertools::Itertools, itertools::Itertools,
log::*, log::*,
num_derive::ToPrimitive,
num_traits::ToPrimitive,
rayon::{ rayon::{
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
ThreadPool, ThreadPoolBuilder, ThreadPool, ThreadPoolBuilder,
@ -840,7 +838,7 @@ pub(crate) struct BankFieldsToSerialize<'a> {
pub(crate) rent_collector: RentCollector, pub(crate) rent_collector: RentCollector,
pub(crate) epoch_schedule: EpochSchedule, pub(crate) epoch_schedule: EpochSchedule,
pub(crate) inflation: Inflation, pub(crate) inflation: Inflation,
pub(crate) stakes: &'a RwLock<Stakes>, pub(crate) stakes: &'a StakesCache,
pub(crate) epoch_stakes: &'a HashMap<Epoch, EpochStakes>, pub(crate) epoch_stakes: &'a HashMap<Epoch, EpochStakes>,
pub(crate) is_delta: bool, pub(crate) is_delta: bool,
} }
@ -879,7 +877,7 @@ impl PartialEq for Bank {
&& self.rent_collector == other.rent_collector && self.rent_collector == other.rent_collector
&& self.epoch_schedule == other.epoch_schedule && self.epoch_schedule == other.epoch_schedule
&& *self.inflation.read().unwrap() == *other.inflation.read().unwrap() && *self.inflation.read().unwrap() == *other.inflation.read().unwrap()
&& *self.stakes.read().unwrap() == *other.stakes.read().unwrap() && *self.stakes_cache.stakes() == *other.stakes_cache.stakes()
&& self.epoch_stakes == other.epoch_stakes && self.epoch_stakes == other.epoch_stakes
&& self.is_delta.load(Relaxed) == other.is_delta.load(Relaxed) && self.is_delta.load(Relaxed) == other.is_delta.load(Relaxed)
} }
@ -1046,7 +1044,7 @@ pub struct Bank {
inflation: Arc<RwLock<Inflation>>, inflation: Arc<RwLock<Inflation>>,
/// cache of vote_account and stake_account state for this fork /// cache of vote_account and stake_account state for this fork
stakes: RwLock<Stakes>, stakes_cache: StakesCache,
/// staked nodes on epoch boundaries, saved off when a bank.slot() is at /// staked nodes on epoch boundaries, saved off when a bank.slot() is at
/// a leader schedule calculation boundary /// a leader schedule calculation boundary
@ -1113,17 +1111,10 @@ struct VoteWithStakeDelegations {
delegations: Vec<(Pubkey, (StakeState, AccountSharedData))>, delegations: Vec<(Pubkey, (StakeState, AccountSharedData))>,
} }
#[derive(Debug, Clone, PartialEq, ToPrimitive)]
enum InvalidReason {
Missing,
BadState,
WrongOwner,
}
struct LoadVoteAndStakeAccountsResult { struct LoadVoteAndStakeAccountsResult {
vote_with_stake_delegations_map: DashMap<Pubkey, VoteWithStakeDelegations>, vote_with_stake_delegations_map: DashMap<Pubkey, VoteWithStakeDelegations>,
invalid_stake_keys: DashMap<Pubkey, InvalidReason>, invalid_stake_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
invalid_vote_keys: DashMap<Pubkey, InvalidReason>, invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -1227,7 +1218,7 @@ impl Bank {
// genesis needs stakes for all epochs up to the epoch implied by // genesis needs stakes for all epochs up to the epoch implied by
// slot = 0 and genesis configuration // slot = 0 and genesis configuration
{ {
let stakes = bank.stakes.read().unwrap(); let stakes = bank.stakes_cache.stakes();
for epoch in 0..=bank.get_leader_schedule_epoch(bank.slot) { for epoch in 0..=bank.get_leader_schedule_epoch(bank.slot) {
bank.epoch_stakes bank.epoch_stakes
.insert(epoch, EpochStakes::new(&stakes, epoch)); .insert(epoch, EpochStakes::new(&stakes, epoch));
@ -1340,7 +1331,7 @@ impl Bank {
transaction_entries_count: AtomicU64::new(0), transaction_entries_count: AtomicU64::new(0),
transactions_per_entry_max: AtomicU64::new(0), transactions_per_entry_max: AtomicU64::new(0),
// we will .clone_with_epoch() this soon after stake data update; so just .clone() for now // we will .clone_with_epoch() this soon after stake data update; so just .clone() for now
stakes: RwLock::new(parent.stakes.read().unwrap().clone()), stakes_cache: StakesCache::new(parent.stakes_cache.stakes().clone()),
epoch_stakes: parent.epoch_stakes.clone(), epoch_stakes: parent.epoch_stakes.clone(),
parent_hash: parent.hash(), parent_hash: parent.hash(),
parent_slot: parent.slot(), parent_slot: parent.slot(),
@ -1397,10 +1388,7 @@ impl Bank {
// Add new entry to stakes.stake_history, set appropriate epoch and // Add new entry to stakes.stake_history, set appropriate epoch and
// update vote accounts with warmed up stakes before saving a // update vote accounts with warmed up stakes before saving a
// snapshot of stakes in epoch stakes // snapshot of stakes in epoch stakes
new.stakes new.stakes_cache.activate_epoch(epoch, &thread_pool);
.write()
.unwrap()
.activate_epoch(epoch, &thread_pool);
// Save a snapshot of stakes for use in consensus and stake weighted networking // Save a snapshot of stakes for use in consensus and stake weighted networking
let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot); let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot);
@ -1527,7 +1515,7 @@ impl Bank {
rent_collector: fields.rent_collector.clone_with_epoch(fields.epoch), rent_collector: fields.rent_collector.clone_with_epoch(fields.epoch),
epoch_schedule: fields.epoch_schedule, epoch_schedule: fields.epoch_schedule,
inflation: Arc::new(RwLock::new(fields.inflation)), inflation: Arc::new(RwLock::new(fields.inflation)),
stakes: RwLock::new(fields.stakes), stakes_cache: StakesCache::new(fields.stakes),
epoch_stakes: fields.epoch_stakes, epoch_stakes: fields.epoch_stakes,
is_delta: AtomicBool::new(fields.is_delta), is_delta: AtomicBool::new(fields.is_delta),
message_processor: new(), message_processor: new(),
@ -1625,7 +1613,7 @@ impl Bank {
rent_collector: self.rent_collector.clone(), rent_collector: self.rent_collector.clone(),
epoch_schedule: self.epoch_schedule, epoch_schedule: self.epoch_schedule,
inflation: *self.inflation.read().unwrap(), inflation: *self.inflation.read().unwrap(),
stakes: &self.stakes, stakes: &self.stakes_cache,
epoch_stakes: &self.epoch_stakes, epoch_stakes: &self.epoch_stakes,
is_delta: self.is_delta.load(Relaxed), is_delta: self.is_delta.load(Relaxed),
} }
@ -1875,12 +1863,11 @@ impl Bank {
}); });
let new_epoch_stakes = let new_epoch_stakes =
EpochStakes::new(&self.stakes.read().unwrap(), leader_schedule_epoch); EpochStakes::new(&self.stakes_cache.stakes(), leader_schedule_epoch);
{ {
let vote_stakes: HashMap<_, _> = self let vote_stakes: HashMap<_, _> = self
.stakes .stakes_cache
.read() .stakes()
.unwrap()
.vote_accounts() .vote_accounts()
.iter() .iter()
.map(|(pubkey, (stake, _))| (*pubkey, *stake)) .map(|(pubkey, (stake, _))| (*pubkey, *stake))
@ -1931,7 +1918,7 @@ impl Bank {
// if I'm the first Bank in an epoch, ensure stake_history is updated // if I'm the first Bank in an epoch, ensure stake_history is updated
self.update_sysvar_account(&sysvar::stake_history::id(), |account| { self.update_sysvar_account(&sysvar::stake_history::id(), |account| {
create_account::<sysvar::stake_history::StakeHistory>( create_account::<sysvar::stake_history::StakeHistory>(
self.stakes.read().unwrap().history(), self.stakes_cache.stakes().history(),
self.inherit_specially_retained_account_fields(account), self.inherit_specially_retained_account_fields(account),
) )
}); });
@ -2004,7 +1991,7 @@ impl Bank {
let validator_rewards = let validator_rewards =
(validator_rate * capitalization as f64 * epoch_duration_in_years) as u64; (validator_rate * capitalization as f64 * epoch_duration_in_years) as u64;
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked(); let old_vote_balance_and_staked = self.stakes_cache.stakes().vote_balance_and_staked();
let validator_point_value = self.pay_validator_rewards_with_thread_pool( let validator_point_value = self.pay_validator_rewards_with_thread_pool(
prev_epoch, prev_epoch,
@ -2027,7 +2014,7 @@ impl Bank {
}); });
} }
let new_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked(); 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; let validator_rewards_paid = new_vote_balance_and_staked - old_vote_balance_and_staked;
assert_eq!( assert_eq!(
validator_rewards_paid, validator_rewards_paid,
@ -2059,7 +2046,7 @@ impl Bank {
.fetch_add(validator_rewards_paid, Relaxed); .fetch_add(validator_rewards_paid, Relaxed);
let active_stake = if let Some(stake_history_entry) = let active_stake = if let Some(stake_history_entry) =
self.stakes.read().unwrap().history().get(&prev_epoch) self.stakes_cache.stakes().history().get(&prev_epoch)
{ {
stake_history_entry.effective stake_history_entry.effective
} else { } else {
@ -2090,10 +2077,10 @@ impl Bank {
thread_pool: &ThreadPool, thread_pool: &ThreadPool,
reward_calc_tracer: Option<impl Fn(&RewardCalculationEvent) + Send + Sync>, reward_calc_tracer: Option<impl Fn(&RewardCalculationEvent) + Send + Sync>,
) -> LoadVoteAndStakeAccountsResult { ) -> LoadVoteAndStakeAccountsResult {
let stakes = self.stakes.read().unwrap(); let stakes = self.stakes_cache.stakes();
let vote_with_stake_delegations_map = DashMap::with_capacity(stakes.vote_accounts().len()); let vote_with_stake_delegations_map = DashMap::with_capacity(stakes.vote_accounts().len());
let invalid_stake_keys: DashMap<Pubkey, InvalidReason> = DashMap::new(); let invalid_stake_keys: DashMap<Pubkey, InvalidCacheEntryReason> = DashMap::new();
let invalid_vote_keys: DashMap<Pubkey, InvalidReason> = DashMap::new(); let invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason> = DashMap::new();
thread_pool.install(|| { thread_pool.install(|| {
stakes stakes
@ -2108,7 +2095,8 @@ impl Bank {
let stake_delegation = match self.get_account_with_fixed_root(stake_pubkey) { let stake_delegation = match self.get_account_with_fixed_root(stake_pubkey) {
Some(stake_account) => { Some(stake_account) => {
if stake_account.owner() != &solana_stake_program::id() { if stake_account.owner() != &solana_stake_program::id() {
invalid_stake_keys.insert(*stake_pubkey, InvalidReason::WrongOwner); invalid_stake_keys
.insert(*stake_pubkey, InvalidCacheEntryReason::WrongOwner);
return; return;
} }
@ -2116,13 +2104,14 @@ impl Bank {
Some(stake_state) => (*stake_pubkey, (stake_state, stake_account)), Some(stake_state) => (*stake_pubkey, (stake_state, stake_account)),
None => { None => {
invalid_stake_keys invalid_stake_keys
.insert(*stake_pubkey, InvalidReason::BadState); .insert(*stake_pubkey, InvalidCacheEntryReason::BadState);
return; return;
} }
} }
} }
None => { None => {
invalid_stake_keys.insert(*stake_pubkey, InvalidReason::Missing); invalid_stake_keys
.insert(*stake_pubkey, InvalidCacheEntryReason::Missing);
return; return;
} }
}; };
@ -2136,13 +2125,14 @@ impl Bank {
Some(vote_account) => { Some(vote_account) => {
if vote_account.owner() != &solana_vote_program::id() { if vote_account.owner() != &solana_vote_program::id() {
invalid_vote_keys invalid_vote_keys
.insert(*vote_pubkey, InvalidReason::WrongOwner); .insert(*vote_pubkey, InvalidCacheEntryReason::WrongOwner);
return; return;
} }
vote_account vote_account
} }
None => { None => {
invalid_vote_keys.insert(*vote_pubkey, InvalidReason::Missing); invalid_vote_keys
.insert(*vote_pubkey, InvalidCacheEntryReason::Missing);
return; return;
} }
}; };
@ -2152,7 +2142,8 @@ impl Bank {
{ {
vote_state.convert_to_current() vote_state.convert_to_current()
} else { } else {
invalid_vote_keys.insert(*vote_pubkey, InvalidReason::BadState); invalid_vote_keys
.insert(*vote_pubkey, InvalidCacheEntryReason::BadState);
return; return;
}; };
@ -2186,51 +2177,6 @@ impl Bank {
} }
} }
fn handle_invalid_stakes_cache_keys(
&self,
invalid_stake_keys: DashMap<Pubkey, InvalidReason>,
invalid_vote_keys: DashMap<Pubkey, InvalidReason>,
) {
if invalid_stake_keys.is_empty() && invalid_vote_keys.is_empty() {
return;
}
// Prune invalid stake delegations and vote accounts that were
// not properly evicted in normal operation.
let mut maybe_stakes_cache = if self
.feature_set
.is_active(&feature_set::evict_invalid_stakes_cache_entries::id())
{
Some(self.stakes.write().unwrap())
} else {
None
};
for (stake_pubkey, reason) in invalid_stake_keys {
if let Some(stakes_cache) = maybe_stakes_cache.as_mut() {
stakes_cache.remove_stake_delegation(&stake_pubkey);
}
datapoint_warn!(
"bank-stake_delegation_accounts-invalid-account",
("slot", self.slot() as i64, i64),
("stake-address", format!("{:?}", stake_pubkey), String),
("reason", reason.to_i64().unwrap_or_default(), i64),
);
}
for (vote_pubkey, reason) in invalid_vote_keys {
if let Some(stakes_cache) = maybe_stakes_cache.as_mut() {
stakes_cache.remove_vote_account(&vote_pubkey);
}
datapoint_warn!(
"bank-stake_delegation_accounts-invalid-account",
("slot", self.slot() as i64, i64),
("vote-address", format!("{:?}", vote_pubkey), String),
("reason", reason.to_i64().unwrap_or_default(), i64),
);
}
}
/// iterate over all stakes, redeem vote credits for each stake we can /// iterate over all stakes, redeem vote credits for each stake we can
/// successfully load and parse, return the lamport value of one point /// successfully load and parse, return the lamport value of one point
fn pay_validator_rewards_with_thread_pool( fn pay_validator_rewards_with_thread_pool(
@ -2241,7 +2187,7 @@ impl Bank {
fix_activating_credits_observed: bool, fix_activating_credits_observed: bool,
thread_pool: &ThreadPool, thread_pool: &ThreadPool,
) -> f64 { ) -> f64 {
let stake_history = self.stakes.read().unwrap().history().clone(); let stake_history = self.stakes_cache.stakes().history().clone();
let vote_with_stake_delegations_map = { let vote_with_stake_delegations_map = {
let LoadVoteAndStakeAccountsResult { let LoadVoteAndStakeAccountsResult {
vote_with_stake_delegations_map, vote_with_stake_delegations_map,
@ -2252,7 +2198,15 @@ impl Bank {
reward_calc_tracer.as_ref(), reward_calc_tracer.as_ref(),
); );
self.handle_invalid_stakes_cache_keys(invalid_stake_keys, invalid_vote_keys); let evict_invalid_stakes_cache_entries = self
.feature_set
.is_active(&feature_set::evict_invalid_stakes_cache_entries::id());
self.stakes_cache.handle_invalid_keys(
invalid_stake_keys,
invalid_vote_keys,
evict_invalid_stakes_cache_entries,
self.slot(),
);
vote_with_stake_delegations_map vote_with_stake_delegations_map
}; };
@ -2675,9 +2629,8 @@ impl Bank {
// highest staked node is the first collector // highest staked node is the first collector
self.collector_id = self self.collector_id = self
.stakes .stakes_cache
.read() .stakes()
.unwrap()
.highest_staked_node() .highest_staked_node()
.unwrap_or_default(); .unwrap_or_default();
@ -4550,13 +4503,11 @@ impl Bank {
.accounts .accounts
.store_slow_cached(self.slot(), pubkey, account); .store_slow_cached(self.slot(), pubkey, account);
if Stakes::is_stake(account) { self.stakes_cache.check_and_store(
self.stakes.write().unwrap().store( pubkey,
pubkey, account,
account, self.stakes_remove_delegation_if_inactive_enabled(),
self.stakes_remove_delegation_if_inactive_enabled(), );
);
}
} }
pub fn force_flush_accounts_cache(&self) { pub fn force_flush_accounts_cache(&self) {
@ -5220,11 +5171,10 @@ impl Bank {
let message = &tx.message(); let message = &tx.message();
let loaded_transaction = raccs.as_ref().unwrap(); let loaded_transaction = raccs.as_ref().unwrap();
for (_i, (pubkey, account)) in (0..message.account_keys.len()) for (_i, (pubkey, account)) in
.zip(loaded_transaction.accounts.iter()) (0..message.account_keys.len()).zip(loaded_transaction.accounts.iter())
.filter(|(_i, (_pubkey, account))| (Stakes::is_stake(account)))
{ {
self.stakes.write().unwrap().store( self.stakes_cache.check_and_store(
pubkey, pubkey,
account, account,
self.stakes_remove_delegation_if_inactive_enabled(), self.stakes_remove_delegation_if_inactive_enabled(),
@ -5235,11 +5185,11 @@ impl Bank {
/// current stake delegations for this bank /// current stake delegations for this bank
pub fn cloned_stake_delegations(&self) -> HashMap<Pubkey, Delegation> { pub fn cloned_stake_delegations(&self) -> HashMap<Pubkey, Delegation> {
self.stakes.read().unwrap().stake_delegations().clone() self.stakes_cache.stakes().stake_delegations().clone()
} }
pub fn staked_nodes(&self) -> HashMap<Pubkey, u64> { pub fn staked_nodes(&self) -> HashMap<Pubkey, u64> {
self.stakes.read().unwrap().staked_nodes() self.stakes_cache.stakes().staked_nodes()
} }
/// current vote accounts for this bank along with the stake /// current vote accounts for this bank along with the stake
@ -5247,9 +5197,8 @@ impl Bank {
/// Note: This clones the entire vote-accounts hashmap. For a single /// Note: This clones the entire vote-accounts hashmap. For a single
/// account lookup use get_vote_account instead. /// account lookup use get_vote_account instead.
pub fn vote_accounts(&self) -> Vec<(Pubkey, (u64 /*stake*/, ArcVoteAccount))> { pub fn vote_accounts(&self) -> Vec<(Pubkey, (u64 /*stake*/, ArcVoteAccount))> {
self.stakes self.stakes_cache
.read() .stakes()
.unwrap()
.vote_accounts() .vote_accounts()
.iter() .iter()
.map(|(k, v)| (*k, v.clone())) .map(|(k, v)| (*k, v.clone()))
@ -5261,9 +5210,8 @@ impl Bank {
&self, &self,
vote_account: &Pubkey, vote_account: &Pubkey,
) -> Option<(u64 /*stake*/, ArcVoteAccount)> { ) -> Option<(u64 /*stake*/, ArcVoteAccount)> {
self.stakes self.stakes_cache
.read() .stakes()
.unwrap()
.vote_accounts() .vote_accounts()
.get(vote_account) .get(vote_account)
.cloned() .cloned()
@ -9998,9 +9946,12 @@ pub(crate) mod tests {
Err(InstructionError::Custom(42)) Err(InstructionError::Custom(42))
} }
// Non-native loader accounts can not be used for instruction processing // Non-builtin loader accounts can not be used for instruction processing
assert!(bank.stakes.read().unwrap().vote_accounts().is_empty()); {
assert!(bank.stakes.read().unwrap().stake_delegations().is_empty()); let stakes = bank.stakes_cache.stakes();
assert!(stakes.vote_accounts().is_empty());
}
assert!(bank.stakes_cache.stakes().stake_delegations().is_empty());
assert_eq!(bank.calculate_capitalization(true), bank.capitalization()); assert_eq!(bank.calculate_capitalization(true), bank.capitalization());
let ((vote_id, vote_account), (stake_id, stake_account)) = let ((vote_id, vote_account), (stake_id, stake_account)) =
@ -10009,14 +9960,20 @@ pub(crate) mod tests {
.fetch_add(vote_account.lamports() + stake_account.lamports(), Relaxed); .fetch_add(vote_account.lamports() + stake_account.lamports(), Relaxed);
bank.store_account(&vote_id, &vote_account); bank.store_account(&vote_id, &vote_account);
bank.store_account(&stake_id, &stake_account); bank.store_account(&stake_id, &stake_account);
assert!(!bank.stakes.read().unwrap().vote_accounts().is_empty()); {
assert!(!bank.stakes.read().unwrap().stake_delegations().is_empty()); let stakes = bank.stakes_cache.stakes();
assert!(!stakes.vote_accounts().is_empty());
}
assert!(!bank.stakes_cache.stakes().stake_delegations().is_empty());
assert_eq!(bank.calculate_capitalization(true), bank.capitalization()); assert_eq!(bank.calculate_capitalization(true), bank.capitalization());
bank.add_builtin("mock_program1", vote_id, mock_ix_processor); bank.add_builtin("mock_program1", vote_id, mock_ix_processor);
bank.add_builtin("mock_program2", stake_id, mock_ix_processor); bank.add_builtin("mock_program2", stake_id, mock_ix_processor);
assert!(bank.stakes.read().unwrap().vote_accounts().is_empty()); {
assert!(bank.stakes.read().unwrap().stake_delegations().is_empty()); let stakes = bank.stakes_cache.stakes();
assert!(stakes.vote_accounts().is_empty());
}
assert!(bank.stakes_cache.stakes().stake_delegations().is_empty());
assert_eq!(bank.calculate_capitalization(true), bank.capitalization()); assert_eq!(bank.calculate_capitalization(true), bank.capitalization());
assert_eq!( assert_eq!(
"mock_program1", "mock_program1",
@ -10035,8 +9992,11 @@ pub(crate) mod tests {
bank.update_accounts_hash(); bank.update_accounts_hash();
let new_hash = bank.get_accounts_hash(); let new_hash = bank.get_accounts_hash();
assert_eq!(old_hash, new_hash); assert_eq!(old_hash, new_hash);
assert!(bank.stakes.read().unwrap().vote_accounts().is_empty()); {
assert!(bank.stakes.read().unwrap().stake_delegations().is_empty()); let stakes = bank.stakes_cache.stakes();
assert!(stakes.vote_accounts().is_empty());
}
assert!(bank.stakes_cache.stakes().stake_delegations().is_empty());
assert_eq!(bank.calculate_capitalization(true), bank.capitalization()); assert_eq!(bank.calculate_capitalization(true), bank.capitalization());
assert_eq!( assert_eq!(
"mock_program1", "mock_program1",

View File

@ -2,9 +2,9 @@
use solana_frozen_abi::abi_example::IgnoreAsHelper; use solana_frozen_abi::abi_example::IgnoreAsHelper;
use { use {
super::{common::UnusedAccounts, *}, super::{common::UnusedAccounts, *},
crate::ancestors::AncestorsForSerialization, crate::{ancestors::AncestorsForSerialization, stakes::StakesCache},
solana_measure::measure::Measure, solana_measure::measure::Measure,
std::cell::RefCell, std::{cell::RefCell, sync::RwLock},
}; };
type AccountsDbFields = super::AccountsDbFields<SerializableAccountStorageEntry>; type AccountsDbFields = super::AccountsDbFields<SerializableAccountStorageEntry>;
@ -42,7 +42,6 @@ impl From<&AccountStorageEntry> for SerializableAccountStorageEntry {
} }
} }
use std::sync::RwLock;
// Deserializable version of Bank which need not be serializable, // Deserializable version of Bank which need not be serializable,
// because it's handled by SerializableVersionedBank. // because it's handled by SerializableVersionedBank.
// So, sync fields with it! // So, sync fields with it!
@ -153,7 +152,7 @@ pub(crate) struct SerializableVersionedBank<'a> {
pub(crate) rent_collector: RentCollector, pub(crate) rent_collector: RentCollector,
pub(crate) epoch_schedule: EpochSchedule, pub(crate) epoch_schedule: EpochSchedule,
pub(crate) inflation: Inflation, pub(crate) inflation: Inflation,
pub(crate) stakes: &'a RwLock<Stakes>, pub(crate) stakes: &'a StakesCache,
pub(crate) unused_accounts: UnusedAccounts, pub(crate) unused_accounts: UnusedAccounts,
pub(crate) epoch_stakes: &'a HashMap<Epoch, EpochStakes>, pub(crate) epoch_stakes: &'a HashMap<Epoch, EpochStakes>,
pub(crate) is_delta: bool, pub(crate) is_delta: bool,

View File

@ -296,7 +296,7 @@ mod test_bank_serialize {
// This some what long test harness is required to freeze the ABI of // This some what long test harness is required to freeze the ABI of
// Bank's serialization due to versioned nature // Bank's serialization due to versioned nature
#[frozen_abi(digest = "6msodEzE7YzFtorBhiP6ax4PKBaPZTkmYdGAdpoxLCvV")] #[frozen_abi(digest = "9NFbb7BMUbSjUr8xwRGhW5kT5jCTQj2U2vZtMxvovsTn")]
#[derive(Serialize, AbiExample)] #[derive(Serialize, AbiExample)]
pub struct BankAbiTestWrapperFuture { pub struct BankAbiTestWrapperFuture {
#[serde(serialize_with = "wrapper_future")] #[serde(serialize_with = "wrapper_future")]

View File

@ -2,13 +2,16 @@
//! node stakes //! node stakes
use { use {
crate::vote_account::{ArcVoteAccount, VoteAccounts, VoteAccountsHashMap}, crate::vote_account::{ArcVoteAccount, VoteAccounts, VoteAccountsHashMap},
dashmap::DashMap,
num_derive::ToPrimitive,
num_traits::ToPrimitive,
rayon::{ rayon::{
iter::{IntoParallelRefIterator, ParallelIterator}, iter::{IntoParallelRefIterator, ParallelIterator},
ThreadPool, ThreadPool,
}, },
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, ReadableAccount}, account::{AccountSharedData, ReadableAccount},
clock::Epoch, clock::{Epoch, Slot},
pubkey::Pubkey, pubkey::Pubkey,
stake::{ stake::{
self, self,
@ -18,9 +21,99 @@ use {
}, },
solana_stake_program::stake_state, solana_stake_program::stake_state,
solana_vote_program::vote_state::VoteState, solana_vote_program::vote_state::VoteState,
std::collections::HashMap, std::{
collections::HashMap,
sync::{RwLock, RwLockReadGuard},
},
}; };
#[derive(Debug, Clone, PartialEq, ToPrimitive)]
pub enum InvalidCacheEntryReason {
Missing,
BadState,
WrongOwner,
}
#[derive(Default, Debug, Deserialize, Serialize, AbiExample)]
pub struct StakesCache(RwLock<Stakes>);
impl StakesCache {
pub fn new(stakes: Stakes) -> Self {
Self(RwLock::new(stakes))
}
pub fn stakes(&self) -> RwLockReadGuard<Stakes> {
self.0.read().unwrap()
}
pub fn is_stake(account: &AccountSharedData) -> bool {
solana_vote_program::check_id(account.owner())
|| stake::program::check_id(account.owner())
&& account.data().len() >= std::mem::size_of::<StakeState>()
}
pub fn check_and_store(
&self,
pubkey: &Pubkey,
account: &AccountSharedData,
remove_delegation_on_inactive: bool,
) {
if Self::is_stake(account) {
let mut stakes = self.0.write().unwrap();
stakes.store(pubkey, account, remove_delegation_on_inactive)
}
}
pub fn activate_epoch(&self, next_epoch: Epoch, thread_pool: &ThreadPool) {
let mut stakes = self.0.write().unwrap();
stakes.activate_epoch(next_epoch, thread_pool)
}
pub fn handle_invalid_keys(
&self,
invalid_stake_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
should_evict_invalid_entries: bool,
current_slot: Slot,
) {
if invalid_stake_keys.is_empty() && invalid_vote_keys.is_empty() {
return;
}
// Prune invalid stake delegations and vote accounts that were
// not properly evicted in normal operation.
let mut maybe_stakes = if should_evict_invalid_entries {
Some(self.0.write().unwrap())
} else {
None
};
for (stake_pubkey, reason) in invalid_stake_keys {
if let Some(stakes) = maybe_stakes.as_mut() {
stakes.remove_stake_delegation(&stake_pubkey);
}
datapoint_warn!(
"bank-stake_delegation_accounts-invalid-account",
("slot", current_slot as i64, i64),
("stake-address", format!("{:?}", stake_pubkey), String),
("reason", reason.to_i64().unwrap_or_default(), i64),
);
}
for (vote_pubkey, reason) in invalid_vote_keys {
if let Some(stakes) = maybe_stakes.as_mut() {
stakes.remove_vote_account(&vote_pubkey);
}
datapoint_warn!(
"bank-stake_delegation_accounts-invalid-account",
("slot", current_slot as i64, i64),
("vote-address", format!("{:?}", vote_pubkey), String),
("reason", reason.to_i64().unwrap_or_default(), i64),
);
}
}
}
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)] #[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)]
pub struct Stakes { pub struct Stakes {
/// vote accounts /// vote accounts
@ -142,12 +235,6 @@ impl Stakes {
.sum::<u64>() .sum::<u64>()
} }
pub fn is_stake(account: &AccountSharedData) -> bool {
solana_vote_program::check_id(account.owner())
|| stake::program::check_id(account.owner())
&& account.data().len() >= std::mem::size_of::<StakeState>()
}
pub fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) { pub fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) {
self.vote_accounts.remove(vote_pubkey); self.vote_accounts.remove(vote_pubkey);
} }