explicitly removes accounts with zero lamports from stakes cache (#25015)
Zero lamport accounts are not stored in accounts-db: https://github.com/solana-labs/solana/blob/7b5835ddf/runtime/src/accounts_db.rs#L5007-L5014 https://github.com/solana-labs/solana/blob/7b5835ddf/runtime/src/accounts_db.rs#L4481-L4483 However, in order to purge the account from cache, stakes cache update partially relies on accounts data to be also zeroed out : https://github.com/solana-labs/solana/blob/7b5835ddf/runtime/src/bank.rs#L3471-L3478 This can be error-prone and introduce inconsistency between accounts-db and vote/stake cache. This commit instead explicitly removes accounts from cache if lamports == 0.
This commit is contained in:
parent
f3e916566f
commit
3fff34a65f
|
@ -62,28 +62,44 @@ impl StakesCache {
|
||||||
// but the owner changes, then this needs to evict the account from
|
// but the owner changes, then this needs to evict the account from
|
||||||
// the cache. see:
|
// the cache. see:
|
||||||
// https://github.com/solana-labs/solana/pull/24200#discussion_r849935444
|
// https://github.com/solana-labs/solana/pull/24200#discussion_r849935444
|
||||||
if solana_vote_program::check_id(account.owner()) {
|
let owner = account.owner();
|
||||||
let new_vote_account = if account.lamports() != 0
|
// Zero lamport accounts are not stored in accounts-db
|
||||||
&& VoteState::is_correct_size_and_initialized(account.data())
|
// and so should be removed from cache as well.
|
||||||
{
|
if account.lamports() == 0 {
|
||||||
|
if solana_vote_program::check_id(owner) {
|
||||||
|
let mut stakes = self.0.write().unwrap();
|
||||||
|
stakes.remove_vote_account(pubkey);
|
||||||
|
} else if solana_stake_program::check_id(owner) {
|
||||||
|
let mut stakes = self.0.write().unwrap();
|
||||||
|
stakes.remove_stake_delegation(pubkey);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debug_assert_ne!(account.lamports(), 0u64);
|
||||||
|
if solana_vote_program::check_id(owner) {
|
||||||
|
if VoteState::is_correct_size_and_initialized(account.data()) {
|
||||||
let vote_account = VoteAccount::from(account.clone());
|
let vote_account = VoteAccount::from(account.clone());
|
||||||
{
|
{
|
||||||
// Called to eagerly deserialize vote state
|
// Called to eagerly deserialize vote state
|
||||||
let _res = vote_account.vote_state();
|
let _res = vote_account.vote_state();
|
||||||
}
|
}
|
||||||
Some(vote_account)
|
let mut stakes = self.0.write().unwrap();
|
||||||
|
stakes.upsert_vote_account(pubkey, vote_account);
|
||||||
} else {
|
} else {
|
||||||
None
|
let mut stakes = self.0.write().unwrap();
|
||||||
|
stakes.remove_vote_account(pubkey)
|
||||||
};
|
};
|
||||||
|
} else if solana_stake_program::check_id(owner) {
|
||||||
self.0
|
match StakeAccount::try_from(account.clone()) {
|
||||||
.write()
|
Ok(stake_account) => {
|
||||||
.unwrap()
|
let mut stakes = self.0.write().unwrap();
|
||||||
.update_vote_account(pubkey, new_vote_account);
|
stakes.upsert_stake_delegation(*pubkey, stake_account);
|
||||||
} else if solana_stake_program::check_id(account.owner()) {
|
}
|
||||||
let stake_account = StakeAccount::try_from(account.clone()).ok();
|
Err(_) => {
|
||||||
let mut stakes = self.0.write().unwrap();
|
let mut stakes = self.0.write().unwrap();
|
||||||
stakes.update_stake_delegation(pubkey, stake_account);
|
stakes.remove_stake_delegation(pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,11 +313,11 @@ impl Stakes<StakeAccount> {
|
||||||
+ self.vote_accounts.iter().map(get_lamports).sum::<u64>()
|
+ self.vote_accounts.iter().map(get_lamports).sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) {
|
fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) {
|
||||||
self.vote_accounts.remove(vote_pubkey);
|
self.vote_accounts.remove(vote_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_stake_delegation(&mut self, stake_pubkey: &Pubkey) {
|
fn remove_stake_delegation(&mut self, stake_pubkey: &Pubkey) {
|
||||||
if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
|
if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
|
||||||
let removed_delegation = stake_account.delegation();
|
let removed_delegation = stake_account.delegation();
|
||||||
let removed_stake = removed_delegation.stake(self.epoch, Some(&self.stake_history));
|
let removed_stake = removed_delegation.stake(self.epoch, Some(&self.stake_history));
|
||||||
|
@ -310,74 +326,36 @@ impl Stakes<StakeAccount> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_vote_account(
|
fn upsert_vote_account(&mut self, vote_pubkey: &Pubkey, vote_account: VoteAccount) {
|
||||||
&mut self,
|
debug_assert_ne!(vote_account.lamports(), 0u64);
|
||||||
vote_pubkey: &Pubkey,
|
debug_assert!(vote_account.is_deserialized());
|
||||||
new_vote_account: Option<VoteAccount>,
|
|
||||||
) {
|
|
||||||
// unconditionally remove existing at first; there is no dependent calculated state for
|
// unconditionally remove existing at first; there is no dependent calculated state for
|
||||||
// votes, not like stakes (stake codepath maintains calculated stake value grouped by
|
// votes, not like stakes (stake codepath maintains calculated stake value grouped by
|
||||||
// delegated vote pubkey)
|
// delegated vote pubkey)
|
||||||
let old_entry = self.vote_accounts.remove(vote_pubkey);
|
let stake = match self.vote_accounts.remove(vote_pubkey) {
|
||||||
if let Some(new_vote_account) = new_vote_account {
|
None => self.calculate_stake(vote_pubkey, self.epoch, &self.stake_history),
|
||||||
debug_assert!(new_vote_account.is_deserialized());
|
Some((stake, _)) => stake,
|
||||||
let new_stake = old_entry.as_ref().map_or_else(
|
};
|
||||||
|| self.calculate_stake(vote_pubkey, self.epoch, &self.stake_history),
|
let entry = (stake, vote_account);
|
||||||
|(old_stake, _old_vote_account)| *old_stake,
|
self.vote_accounts.insert(*vote_pubkey, entry);
|
||||||
);
|
|
||||||
|
|
||||||
self.vote_accounts
|
|
||||||
.insert(*vote_pubkey, (new_stake, new_vote_account));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_stake_delegation(
|
fn upsert_stake_delegation(&mut self, stake_pubkey: Pubkey, stake_account: StakeAccount) {
|
||||||
&mut self,
|
debug_assert_ne!(stake_account.lamports(), 0u64);
|
||||||
stake_pubkey: &Pubkey,
|
let delegation = stake_account.delegation();
|
||||||
new_stake_account: Option<StakeAccount>,
|
let voter_pubkey = delegation.voter_pubkey;
|
||||||
) {
|
let stake = delegation.stake(self.epoch, Some(&self.stake_history));
|
||||||
// old_stake is stake lamports and voter_pubkey from the pre-store() version
|
match self.stake_delegations.insert(stake_pubkey, stake_account) {
|
||||||
let old_stake = self
|
None => self.vote_accounts.add_stake(&voter_pubkey, stake),
|
||||||
.stake_delegations
|
Some(old_stake_account) => {
|
||||||
.get(stake_pubkey)
|
let old_delegation = old_stake_account.delegation();
|
||||||
.map(|stake_account| {
|
let old_voter_pubkey = old_delegation.voter_pubkey;
|
||||||
let delegation = stake_account.delegation();
|
let old_stake = old_delegation.stake(self.epoch, Some(&self.stake_history));
|
||||||
(
|
if voter_pubkey != old_voter_pubkey || stake != old_stake {
|
||||||
delegation.voter_pubkey,
|
self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
|
||||||
delegation.stake(self.epoch, Some(&self.stake_history)),
|
self.vote_accounts.add_stake(&voter_pubkey, stake);
|
||||||
)
|
}
|
||||||
});
|
|
||||||
let new_delegation = new_stake_account.as_ref().map(StakeAccount::delegation);
|
|
||||||
let new_stake = new_stake_account.as_ref().and_then(|new_stake_account| {
|
|
||||||
let new_delegation = new_delegation?;
|
|
||||||
// When account is removed (lamports == 0), this check ensures
|
|
||||||
// resetting cached stake value below, even if the account happens
|
|
||||||
// to be still staked for some (odd) reason.
|
|
||||||
let stake = if new_stake_account.lamports() == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
new_delegation.stake(self.epoch, Some(&self.stake_history))
|
|
||||||
};
|
|
||||||
Some((new_delegation.voter_pubkey, stake))
|
|
||||||
});
|
|
||||||
// check if adjustments need to be made...
|
|
||||||
if new_stake != old_stake {
|
|
||||||
if let Some((voter_pubkey, stake)) = old_stake {
|
|
||||||
self.vote_accounts.sub_stake(&voter_pubkey, stake);
|
|
||||||
}
|
}
|
||||||
if let Some((voter_pubkey, stake)) = new_stake {
|
|
||||||
self.vote_accounts.add_stake(&voter_pubkey, stake);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: should this remove stake account if lamports == 0?
|
|
||||||
if new_delegation.is_some() {
|
|
||||||
self.stake_delegations
|
|
||||||
.insert(*stake_pubkey, new_stake_account.unwrap());
|
|
||||||
} else {
|
|
||||||
// when stake is no longer delegated, remove it from Stakes so that
|
|
||||||
// given `pubkey` can be used for any owner in the future, while not
|
|
||||||
// affecting Stakes.
|
|
||||||
self.stake_delegations.remove(stake_pubkey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue