solana_runtime/
stakes.rs

1//! Stakes serve as a cache of stake and vote accounts to derive
2//! node stakes
3use {
4    crate::{stake_account, stake_history::StakeHistory},
5    dashmap::DashMap,
6    im::HashMap as ImHashMap,
7    log::error,
8    num_derive::ToPrimitive,
9    num_traits::ToPrimitive,
10    rayon::{prelude::*, ThreadPool},
11    solana_accounts_db::stake_rewards::StakeReward,
12    solana_sdk::{
13        account::{AccountSharedData, ReadableAccount},
14        clock::{Epoch, Slot},
15        pubkey::Pubkey,
16        stake::state::{Delegation, StakeActivationStatus},
17        vote::state::VoteStateVersions,
18    },
19    solana_stake_program::stake_state::Stake,
20    solana_vote::vote_account::{VoteAccount, VoteAccounts},
21    std::{
22        collections::HashMap,
23        ops::Add,
24        sync::{Arc, RwLock, RwLockReadGuard},
25    },
26    thiserror::Error,
27};
28
29mod serde_stakes;
30pub(crate) use serde_stakes::serde_stakes_to_delegation_format;
31pub use serde_stakes::SerdeStakesToStakeFormat;
32
33#[derive(Debug, Error)]
34pub enum Error {
35    #[error("Invalid delegation: {0}")]
36    InvalidDelegation(Pubkey),
37    #[error(transparent)]
38    InvalidStakeAccount(#[from] stake_account::Error),
39    #[error("Stake account not found: {0}")]
40    StakeAccountNotFound(Pubkey),
41    #[error("Vote account mismatch: {0}")]
42    VoteAccountMismatch(Pubkey),
43    #[error("Vote account not cached: {0}")]
44    VoteAccountNotCached(Pubkey),
45    #[error("Vote account not found: {0}")]
46    VoteAccountNotFound(Pubkey),
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)]
50pub enum InvalidCacheEntryReason {
51    Missing,
52    BadState,
53    WrongOwner,
54}
55
56type StakeAccount = stake_account::StakeAccount<Delegation>;
57
58#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
59#[derive(Default, Debug)]
60pub(crate) struct StakesCache(RwLock<Stakes<StakeAccount>>);
61
62impl StakesCache {
63    pub(crate) fn new(stakes: Stakes<StakeAccount>) -> Self {
64        Self(RwLock::new(stakes))
65    }
66
67    pub(crate) fn stakes(&self) -> RwLockReadGuard<Stakes<StakeAccount>> {
68        self.0.read().unwrap()
69    }
70
71    pub(crate) fn check_and_store(
72        &self,
73        pubkey: &Pubkey,
74        account: &impl ReadableAccount,
75        new_rate_activation_epoch: Option<Epoch>,
76    ) {
77        // TODO: If the account is already cached as a vote or stake account
78        // but the owner changes, then this needs to evict the account from
79        // the cache. see:
80        // https://github.com/solana-labs/solana/pull/24200#discussion_r849935444
81        let owner = account.owner();
82        // Zero lamport accounts are not stored in accounts-db
83        // and so should be removed from cache as well.
84        if account.lamports() == 0 {
85            if solana_vote_program::check_id(owner) {
86                let _old_vote_account = {
87                    let mut stakes = self.0.write().unwrap();
88                    stakes.remove_vote_account(pubkey)
89                };
90            } else if solana_stake_program::check_id(owner) {
91                let mut stakes = self.0.write().unwrap();
92                stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
93            }
94            return;
95        }
96        debug_assert_ne!(account.lamports(), 0u64);
97        if solana_vote_program::check_id(owner) {
98            if VoteStateVersions::is_correct_size_and_initialized(account.data()) {
99                match VoteAccount::try_from(account.to_account_shared_data()) {
100                    Ok(vote_account) => {
101                        // drop the old account after releasing the lock
102                        let _old_vote_account = {
103                            let mut stakes = self.0.write().unwrap();
104                            stakes.upsert_vote_account(
105                                pubkey,
106                                vote_account,
107                                new_rate_activation_epoch,
108                            )
109                        };
110                    }
111                    Err(_) => {
112                        // drop the old account after releasing the lock
113                        let _old_vote_account = {
114                            let mut stakes = self.0.write().unwrap();
115                            stakes.remove_vote_account(pubkey)
116                        };
117                    }
118                }
119            } else {
120                // drop the old account after releasing the lock
121                let _old_vote_account = {
122                    let mut stakes = self.0.write().unwrap();
123                    stakes.remove_vote_account(pubkey)
124                };
125            };
126        } else if solana_stake_program::check_id(owner) {
127            match StakeAccount::try_from(account.to_account_shared_data()) {
128                Ok(stake_account) => {
129                    let mut stakes = self.0.write().unwrap();
130                    stakes.upsert_stake_delegation(
131                        *pubkey,
132                        stake_account,
133                        new_rate_activation_epoch,
134                    );
135                }
136                Err(_) => {
137                    let mut stakes = self.0.write().unwrap();
138                    stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
139                }
140            }
141        }
142    }
143
144    pub(crate) fn activate_epoch(
145        &self,
146        next_epoch: Epoch,
147        thread_pool: &ThreadPool,
148        new_rate_activation_epoch: Option<Epoch>,
149    ) {
150        let mut stakes = self.0.write().unwrap();
151        stakes.activate_epoch(next_epoch, thread_pool, new_rate_activation_epoch)
152    }
153
154    pub(crate) fn update_stake_accounts(
155        &self,
156        thread_pool: &ThreadPool,
157        stake_rewards: &[StakeReward],
158        new_rate_activation_epoch: Option<Epoch>,
159    ) {
160        self.0.write().unwrap().update_stake_accounts(
161            thread_pool,
162            stake_rewards,
163            new_rate_activation_epoch,
164        )
165    }
166
167    pub(crate) fn handle_invalid_keys(
168        &self,
169        invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
170        current_slot: Slot,
171    ) {
172        if invalid_vote_keys.is_empty() {
173            return;
174        }
175
176        // Prune invalid stake delegations and vote accounts that were
177        // not properly evicted in normal operation.
178        let mut stakes = self.0.write().unwrap();
179
180        for (vote_pubkey, reason) in invalid_vote_keys {
181            stakes.remove_vote_account(&vote_pubkey);
182            datapoint_warn!(
183                "bank-stake_delegation_accounts-invalid-account",
184                ("slot", current_slot as i64, i64),
185                ("vote-address", format!("{vote_pubkey:?}"), String),
186                ("reason", reason.to_i64().unwrap_or_default(), i64),
187            );
188        }
189    }
190}
191
192/// The generic type T is either Delegation or StakeAccount.
193/// [`Stakes<Delegation>`] is equivalent to the old code and is used for backward
194/// compatibility in [`crate::bank::BankFieldsToDeserialize`].
195/// But banks cache [`Stakes<StakeAccount>`] which includes the entire stake
196/// account and StakeStateV2 deserialized from the account. Doing so, will remove
197/// the need to load the stake account from accounts-db when working with
198/// stake-delegations.
199#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
200#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
201pub struct Stakes<T: Clone> {
202    /// vote accounts
203    vote_accounts: VoteAccounts,
204
205    /// stake_delegations
206    stake_delegations: ImHashMap<Pubkey, T>,
207
208    /// unused
209    unused: u64,
210
211    /// current epoch, used to calculate current stake
212    epoch: Epoch,
213
214    /// history of staking levels
215    stake_history: StakeHistory,
216}
217
218// For backward compatibility, we can only serialize and deserialize
219// Stakes<Delegation> in the old `epoch_stakes` bank snapshot field. However,
220// Stakes<StakeAccount> entries are added to the bank's epoch stakes hashmap
221// when crossing epoch boundaries and Stakes<Stake> entries are added when
222// starting up from bank snapshots that have the new epoch stakes field. By
223// using this enum, the cost of converting all entries to Stakes<Delegation> is
224// put off until serializing new snapshots. This helps avoid bogging down epoch
225// boundaries and startup with the conversion overhead.
226#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
227#[derive(Debug, Clone)]
228pub enum StakesEnum {
229    Accounts(Stakes<StakeAccount>),
230    Delegations(Stakes<Delegation>),
231    Stakes(Stakes<Stake>),
232}
233
234impl<T: Clone> Stakes<T> {
235    pub fn vote_accounts(&self) -> &VoteAccounts {
236        &self.vote_accounts
237    }
238
239    pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
240        self.vote_accounts.staked_nodes()
241    }
242}
243
244impl Stakes<StakeAccount> {
245    /// Creates a Stake<StakeAccount> from Stake<Delegation> by loading the
246    /// full account state for respective stake pubkeys. get_account function
247    /// should return the account at the respective slot where stakes where
248    /// cached.
249    pub(crate) fn new<F>(stakes: &Stakes<Delegation>, get_account: F) -> Result<Self, Error>
250    where
251        F: Fn(&Pubkey) -> Option<AccountSharedData> + Sync,
252    {
253        let stake_delegations = stakes
254            .stake_delegations
255            .iter()
256            // im::HashMap doesn't support rayon so we manually build a temporary vector. Note this is
257            // what std HashMap::par_iter() does internally too.
258            .collect::<Vec<_>>()
259            .into_par_iter()
260            // We use fold/reduce to aggregate the results, which does a bit more work than calling
261            // collect()/collect_vec_list() and then im::HashMap::from_iter(collected.into_iter()),
262            // but it does it in background threads, so effectively it's faster.
263            .try_fold(ImHashMap::new, |mut map, (pubkey, delegation)| {
264                let Some(stake_account) = get_account(pubkey) else {
265                    return Err(Error::StakeAccountNotFound(*pubkey));
266                };
267
268                // Assert that all valid vote-accounts referenced in stake delegations are already
269                // contained in `stakes.vote_account`.
270                let voter_pubkey = &delegation.voter_pubkey;
271                if stakes.vote_accounts.get(voter_pubkey).is_none() {
272                    if let Some(account) = get_account(voter_pubkey) {
273                        if VoteStateVersions::is_correct_size_and_initialized(account.data())
274                            && VoteAccount::try_from(account.clone()).is_ok()
275                        {
276                            error!("vote account not cached: {voter_pubkey}, {account:?}");
277                            return Err(Error::VoteAccountNotCached(*voter_pubkey));
278                        }
279                    }
280                }
281
282                let stake_account = StakeAccount::try_from(stake_account)?;
283                // Sanity check that the delegation is consistent with what is
284                // stored in the account.
285                if stake_account.delegation() == delegation {
286                    map.insert(*pubkey, stake_account);
287                    Ok(map)
288                } else {
289                    Err(Error::InvalidDelegation(*pubkey))
290                }
291            })
292            .try_reduce(ImHashMap::new, |a, b| Ok(a.union(b)))?;
293
294        // Assert that cached vote accounts are consistent with accounts-db.
295        //
296        // This currently includes ~5500 accounts, parallelizing brings minor
297        // (sub 2s) improvements.
298        for (pubkey, vote_account) in stakes.vote_accounts.iter() {
299            let Some(account) = get_account(pubkey) else {
300                return Err(Error::VoteAccountNotFound(*pubkey));
301            };
302            let vote_account = vote_account.account();
303            if vote_account != &account {
304                error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}");
305                return Err(Error::VoteAccountMismatch(*pubkey));
306            }
307        }
308
309        Ok(Self {
310            vote_accounts: stakes.vote_accounts.clone(),
311            stake_delegations,
312            unused: stakes.unused,
313            epoch: stakes.epoch,
314            stake_history: stakes.stake_history.clone(),
315        })
316    }
317
318    #[cfg(feature = "dev-context-only-utils")]
319    pub fn new_for_tests(
320        epoch: Epoch,
321        vote_accounts: VoteAccounts,
322        stake_delegations: ImHashMap<Pubkey, StakeAccount>,
323    ) -> Self {
324        Self {
325            vote_accounts,
326            stake_delegations,
327            unused: 0,
328            epoch,
329            stake_history: StakeHistory::default(),
330        }
331    }
332
333    pub(crate) fn history(&self) -> &StakeHistory {
334        &self.stake_history
335    }
336
337    fn activate_epoch(
338        &mut self,
339        next_epoch: Epoch,
340        thread_pool: &ThreadPool,
341        new_rate_activation_epoch: Option<Epoch>,
342    ) {
343        let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
344        // Wrap up the prev epoch by adding new stake history entry for the
345        // prev epoch.
346        let stake_history_entry = thread_pool.install(|| {
347            stake_delegations
348                .par_iter()
349                .fold(StakeActivationStatus::default, |acc, stake_account| {
350                    let delegation = stake_account.delegation();
351                    acc + delegation.stake_activating_and_deactivating(
352                        self.epoch,
353                        &self.stake_history,
354                        new_rate_activation_epoch,
355                    )
356                })
357                .reduce(StakeActivationStatus::default, Add::add)
358        });
359        self.stake_history.add(self.epoch, stake_history_entry);
360        self.epoch = next_epoch;
361        // Refresh the stake distribution of vote accounts for the next epoch,
362        // using new stake history.
363        self.vote_accounts = refresh_vote_accounts(
364            thread_pool,
365            self.epoch,
366            &self.vote_accounts,
367            &stake_delegations,
368            &self.stake_history,
369            new_rate_activation_epoch,
370        );
371    }
372
373    /// Sum the stakes that point to the given voter_pubkey
374    fn calculate_stake(
375        stake_delegations: &ImHashMap<Pubkey, StakeAccount>,
376        voter_pubkey: &Pubkey,
377        epoch: Epoch,
378        stake_history: &StakeHistory,
379        new_rate_activation_epoch: Option<Epoch>,
380    ) -> u64 {
381        stake_delegations
382            .values()
383            .map(StakeAccount::delegation)
384            .filter(|delegation| &delegation.voter_pubkey == voter_pubkey)
385            .map(|delegation| delegation.stake(epoch, stake_history, new_rate_activation_epoch))
386            .sum()
387    }
388
389    /// Sum the lamports of the vote accounts and the delegated stake
390    pub(crate) fn vote_balance_and_staked(&self) -> u64 {
391        let get_stake = |stake_account: &StakeAccount| stake_account.delegation().stake;
392        let get_lamports = |(_, vote_account): (_, &VoteAccount)| vote_account.lamports();
393
394        self.stake_delegations.values().map(get_stake).sum::<u64>()
395            + self.vote_accounts.iter().map(get_lamports).sum::<u64>()
396    }
397
398    fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) -> Option<VoteAccount> {
399        self.vote_accounts.remove(vote_pubkey).map(|(_, a)| a)
400    }
401
402    fn remove_stake_delegation(
403        &mut self,
404        stake_pubkey: &Pubkey,
405        new_rate_activation_epoch: Option<Epoch>,
406    ) {
407        if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
408            let removed_delegation = stake_account.delegation();
409            let removed_stake = removed_delegation.stake(
410                self.epoch,
411                &self.stake_history,
412                new_rate_activation_epoch,
413            );
414            self.vote_accounts
415                .sub_stake(&removed_delegation.voter_pubkey, removed_stake);
416        }
417    }
418
419    fn upsert_vote_account(
420        &mut self,
421        vote_pubkey: &Pubkey,
422        vote_account: VoteAccount,
423        new_rate_activation_epoch: Option<Epoch>,
424    ) -> Option<VoteAccount> {
425        debug_assert_ne!(vote_account.lamports(), 0u64);
426
427        let stake_delegations = &self.stake_delegations;
428        self.vote_accounts.insert(*vote_pubkey, vote_account, || {
429            Self::calculate_stake(
430                stake_delegations,
431                vote_pubkey,
432                self.epoch,
433                &self.stake_history,
434                new_rate_activation_epoch,
435            )
436        })
437    }
438
439    fn upsert_stake_delegation(
440        &mut self,
441        stake_pubkey: Pubkey,
442        stake_account: StakeAccount,
443        new_rate_activation_epoch: Option<Epoch>,
444    ) {
445        debug_assert_ne!(stake_account.lamports(), 0u64);
446        let delegation = stake_account.delegation();
447        let voter_pubkey = delegation.voter_pubkey;
448        let stake = delegation.stake(self.epoch, &self.stake_history, new_rate_activation_epoch);
449        match self.stake_delegations.insert(stake_pubkey, stake_account) {
450            None => self.vote_accounts.add_stake(&voter_pubkey, stake),
451            Some(old_stake_account) => {
452                let old_delegation = old_stake_account.delegation();
453                let old_voter_pubkey = old_delegation.voter_pubkey;
454                let old_stake = old_delegation.stake(
455                    self.epoch,
456                    &self.stake_history,
457                    new_rate_activation_epoch,
458                );
459                if voter_pubkey != old_voter_pubkey || stake != old_stake {
460                    self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
461                    self.vote_accounts.add_stake(&voter_pubkey, stake);
462                }
463            }
464        }
465    }
466
467    fn update_stake_accounts(
468        &mut self,
469        thread_pool: &ThreadPool,
470        stake_rewards: &[StakeReward],
471        new_rate_activation_epoch: Option<Epoch>,
472    ) {
473        let stake_delegations: Vec<_> = thread_pool.install(|| {
474            stake_rewards
475                .into_par_iter()
476                .filter_map(|stake_reward| {
477                    let stake_account = StakeAccount::try_from(stake_reward.stake_account.clone());
478                    Some((stake_reward.stake_pubkey, stake_account.ok()?))
479                })
480                .collect()
481        });
482        self.stake_delegations = std::mem::take(&mut self.stake_delegations)
483            .into_iter()
484            .chain(stake_delegations)
485            .collect::<HashMap<Pubkey, StakeAccount>>()
486            .into_iter()
487            .filter(|(_, account)| account.lamports() != 0u64)
488            .collect();
489        let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
490        self.vote_accounts = refresh_vote_accounts(
491            thread_pool,
492            self.epoch,
493            &self.vote_accounts,
494            &stake_delegations,
495            &self.stake_history,
496            new_rate_activation_epoch,
497        );
498    }
499
500    pub(crate) fn stake_delegations(&self) -> &ImHashMap<Pubkey, StakeAccount> {
501        &self.stake_delegations
502    }
503
504    pub(crate) fn highest_staked_node(&self) -> Option<&Pubkey> {
505        let vote_account = self.vote_accounts.find_max_by_delegated_stake()?;
506        Some(vote_account.node_pubkey())
507    }
508}
509
510impl StakesEnum {
511    pub fn vote_accounts(&self) -> &VoteAccounts {
512        match self {
513            StakesEnum::Accounts(stakes) => stakes.vote_accounts(),
514            StakesEnum::Delegations(stakes) => stakes.vote_accounts(),
515            StakesEnum::Stakes(stakes) => stakes.vote_accounts(),
516        }
517    }
518
519    pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
520        match self {
521            StakesEnum::Accounts(stakes) => stakes.staked_nodes(),
522            StakesEnum::Delegations(stakes) => stakes.staked_nodes(),
523            StakesEnum::Stakes(stakes) => stakes.staked_nodes(),
524        }
525    }
526}
527
528/// This conversion is very memory intensive so should only be used in
529/// development contexts.
530#[cfg(feature = "dev-context-only-utils")]
531impl From<Stakes<StakeAccount>> for Stakes<Delegation> {
532    fn from(stakes: Stakes<StakeAccount>) -> Self {
533        let stake_delegations = stakes
534            .stake_delegations
535            .into_iter()
536            .map(|(pubkey, stake_account)| (pubkey, *stake_account.delegation()))
537            .collect();
538        Self {
539            vote_accounts: stakes.vote_accounts,
540            stake_delegations,
541            unused: stakes.unused,
542            epoch: stakes.epoch,
543            stake_history: stakes.stake_history,
544        }
545    }
546}
547
548/// This conversion is very memory intensive so should only be used in
549/// development contexts.
550#[cfg(feature = "dev-context-only-utils")]
551impl From<Stakes<StakeAccount>> for Stakes<Stake> {
552    fn from(stakes: Stakes<StakeAccount>) -> Self {
553        let stake_delegations = stakes
554            .stake_delegations
555            .into_iter()
556            .map(|(pubkey, stake_account)| (pubkey, *stake_account.stake()))
557            .collect();
558        Self {
559            vote_accounts: stakes.vote_accounts,
560            stake_delegations,
561            unused: stakes.unused,
562            epoch: stakes.epoch,
563            stake_history: stakes.stake_history,
564        }
565    }
566}
567
568/// This conversion is memory intensive so should only be used in development
569/// contexts.
570#[cfg(feature = "dev-context-only-utils")]
571impl From<Stakes<Stake>> for Stakes<Delegation> {
572    fn from(stakes: Stakes<Stake>) -> Self {
573        let stake_delegations = stakes
574            .stake_delegations
575            .into_iter()
576            .map(|(pubkey, stake)| (pubkey, stake.delegation))
577            .collect();
578        Self {
579            vote_accounts: stakes.vote_accounts,
580            stake_delegations,
581            unused: stakes.unused,
582            epoch: stakes.epoch,
583            stake_history: stakes.stake_history,
584        }
585    }
586}
587
588/// This conversion is memory intensive so should only be used in development
589/// contexts.
590#[cfg(feature = "dev-context-only-utils")]
591impl From<StakesEnum> for Stakes<Delegation> {
592    fn from(stakes: StakesEnum) -> Self {
593        match stakes {
594            StakesEnum::Accounts(stakes) => stakes.into(),
595            StakesEnum::Delegations(stakes) => stakes,
596            StakesEnum::Stakes(stakes) => stakes.into(),
597        }
598    }
599}
600
601impl From<Stakes<StakeAccount>> for StakesEnum {
602    fn from(stakes: Stakes<StakeAccount>) -> Self {
603        Self::Accounts(stakes)
604    }
605}
606
607impl From<Stakes<Delegation>> for StakesEnum {
608    fn from(stakes: Stakes<Delegation>) -> Self {
609        Self::Delegations(stakes)
610    }
611}
612
613// Two StakesEnums are equal as long as they represent the same delegations;
614// whether these delegations are stored as StakeAccounts or Delegations.
615// Therefore, if one side is Stakes<StakeAccount> and the other is a
616// Stakes<Delegation> we convert the former one to Stakes<Delegation> before
617// comparing for equality.
618#[cfg(feature = "dev-context-only-utils")]
619impl PartialEq<StakesEnum> for StakesEnum {
620    fn eq(&self, other: &StakesEnum) -> bool {
621        match (self, other) {
622            (Self::Accounts(stakes), Self::Accounts(other)) => stakes == other,
623            (Self::Delegations(stakes), Self::Delegations(other)) => stakes == other,
624            (Self::Stakes(stakes), Self::Stakes(other)) => stakes == other,
625            (stakes, other) => {
626                let stakes = Stakes::<Delegation>::from(stakes.clone());
627                let other = Stakes::<Delegation>::from(other.clone());
628                stakes == other
629            }
630        }
631    }
632}
633
634fn refresh_vote_accounts(
635    thread_pool: &ThreadPool,
636    epoch: Epoch,
637    vote_accounts: &VoteAccounts,
638    stake_delegations: &[&StakeAccount],
639    stake_history: &StakeHistory,
640    new_rate_activation_epoch: Option<Epoch>,
641) -> VoteAccounts {
642    type StakesHashMap = HashMap</*voter:*/ Pubkey, /*stake:*/ u64>;
643    fn merge(mut stakes: StakesHashMap, other: StakesHashMap) -> StakesHashMap {
644        if stakes.len() < other.len() {
645            return merge(other, stakes);
646        }
647        for (pubkey, stake) in other {
648            *stakes.entry(pubkey).or_default() += stake;
649        }
650        stakes
651    }
652    let delegated_stakes = thread_pool.install(|| {
653        stake_delegations
654            .par_iter()
655            .fold(HashMap::default, |mut delegated_stakes, stake_account| {
656                let delegation = stake_account.delegation();
657                let entry = delegated_stakes.entry(delegation.voter_pubkey).or_default();
658                *entry += delegation.stake(epoch, stake_history, new_rate_activation_epoch);
659                delegated_stakes
660            })
661            .reduce(HashMap::default, merge)
662    });
663    vote_accounts
664        .iter()
665        .map(|(&vote_pubkey, vote_account)| {
666            let delegated_stake = delegated_stakes
667                .get(&vote_pubkey)
668                .copied()
669                .unwrap_or_default();
670            (vote_pubkey, (delegated_stake, vote_account.clone()))
671        })
672        .collect()
673}
674
675#[cfg(test)]
676pub(crate) mod tests {
677    use {
678        super::*,
679        rayon::ThreadPoolBuilder,
680        solana_sdk::{account::WritableAccount, pubkey::Pubkey, rent::Rent, stake},
681        solana_stake_program::stake_state,
682        solana_vote_program::vote_state::{self, VoteState, VoteStateVersions},
683    };
684
685    //  set up some dummies for a staked node     ((     vote      )  (     stake     ))
686    pub(crate) fn create_staked_node_accounts(
687        stake: u64,
688    ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
689        let vote_pubkey = solana_sdk::pubkey::new_rand();
690        let vote_account =
691            vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
692        let stake_pubkey = solana_sdk::pubkey::new_rand();
693        (
694            (vote_pubkey, vote_account),
695            (
696                stake_pubkey,
697                create_stake_account(stake, &vote_pubkey, &stake_pubkey),
698            ),
699        )
700    }
701
702    //   add stake to a vote_pubkey                               (   stake    )
703    pub(crate) fn create_stake_account(
704        stake: u64,
705        vote_pubkey: &Pubkey,
706        stake_pubkey: &Pubkey,
707    ) -> AccountSharedData {
708        stake_state::create_account(
709            stake_pubkey,
710            vote_pubkey,
711            &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
712            &Rent::free(),
713            stake,
714        )
715    }
716
717    fn create_warming_staked_node_accounts(
718        stake: u64,
719        epoch: Epoch,
720    ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
721        let vote_pubkey = solana_sdk::pubkey::new_rand();
722        let vote_account =
723            vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
724        (
725            (vote_pubkey, vote_account),
726            create_warming_stake_account(stake, epoch, &vote_pubkey),
727        )
728    }
729
730    // add stake to a vote_pubkey                               (   stake    )
731    fn create_warming_stake_account(
732        stake: u64,
733        epoch: Epoch,
734        vote_pubkey: &Pubkey,
735    ) -> (Pubkey, AccountSharedData) {
736        let stake_pubkey = solana_sdk::pubkey::new_rand();
737        (
738            stake_pubkey,
739            stake_state::create_account_with_activation_epoch(
740                &stake_pubkey,
741                vote_pubkey,
742                &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
743                &Rent::free(),
744                stake,
745                epoch,
746            ),
747        )
748    }
749
750    #[test]
751    fn test_stakes_basic() {
752        for i in 0..4 {
753            let stakes_cache = StakesCache::new(Stakes {
754                epoch: i,
755                ..Stakes::default()
756            });
757
758            let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
759                create_staked_node_accounts(10);
760
761            stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
762            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
763            let stake = stake_state::stake_from(&stake_account).unwrap();
764            {
765                let stakes = stakes_cache.stakes();
766                let vote_accounts = stakes.vote_accounts();
767                assert!(vote_accounts.get(&vote_pubkey).is_some());
768                assert_eq!(
769                    vote_accounts.get_delegated_stake(&vote_pubkey),
770                    stake.stake(i, &StakeHistory::default(), None)
771                );
772            }
773
774            stake_account.set_lamports(42);
775            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
776            {
777                let stakes = stakes_cache.stakes();
778                let vote_accounts = stakes.vote_accounts();
779                assert!(vote_accounts.get(&vote_pubkey).is_some());
780                assert_eq!(
781                    vote_accounts.get_delegated_stake(&vote_pubkey),
782                    stake.stake(i, &StakeHistory::default(), None)
783                ); // stays old stake, because only 10 is activated
784            }
785
786            // activate more
787            let mut stake_account =
788                create_stake_account(42, &vote_pubkey, &solana_sdk::pubkey::new_rand());
789            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
790            let stake = stake_state::stake_from(&stake_account).unwrap();
791            {
792                let stakes = stakes_cache.stakes();
793                let vote_accounts = stakes.vote_accounts();
794                assert!(vote_accounts.get(&vote_pubkey).is_some());
795                assert_eq!(
796                    vote_accounts.get_delegated_stake(&vote_pubkey),
797                    stake.stake(i, &StakeHistory::default(), None)
798                ); // now stake of 42 is activated
799            }
800
801            stake_account.set_lamports(0);
802            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
803            {
804                let stakes = stakes_cache.stakes();
805                let vote_accounts = stakes.vote_accounts();
806                assert!(vote_accounts.get(&vote_pubkey).is_some());
807                assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
808            }
809        }
810    }
811
812    #[test]
813    fn test_stakes_highest() {
814        let stakes_cache = StakesCache::default();
815
816        assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
817
818        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
819            create_staked_node_accounts(10);
820
821        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
822        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
823
824        let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
825            create_staked_node_accounts(20);
826
827        stakes_cache.check_and_store(&vote11_pubkey, &vote11_account, None);
828        stakes_cache.check_and_store(&stake11_pubkey, &stake11_account, None);
829
830        let vote11_node_pubkey = vote_state::from(&vote11_account).unwrap().node_pubkey;
831
832        let highest_staked_node = stakes_cache.stakes().highest_staked_node().copied();
833        assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
834    }
835
836    #[test]
837    fn test_stakes_vote_account_disappear_reappear() {
838        let stakes_cache = StakesCache::new(Stakes {
839            epoch: 4,
840            ..Stakes::default()
841        });
842
843        let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
844            create_staked_node_accounts(10);
845
846        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
847        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
848
849        {
850            let stakes = stakes_cache.stakes();
851            let vote_accounts = stakes.vote_accounts();
852            assert!(vote_accounts.get(&vote_pubkey).is_some());
853            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
854        }
855
856        vote_account.set_lamports(0);
857        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
858
859        {
860            let stakes = stakes_cache.stakes();
861            let vote_accounts = stakes.vote_accounts();
862            assert!(vote_accounts.get(&vote_pubkey).is_none());
863            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
864        }
865
866        vote_account.set_lamports(1);
867        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
868
869        {
870            let stakes = stakes_cache.stakes();
871            let vote_accounts = stakes.vote_accounts();
872            assert!(vote_accounts.get(&vote_pubkey).is_some());
873            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
874        }
875
876        // Vote account too big
877        let cache_data = vote_account.data().to_vec();
878        let mut pushed = vote_account.data().to_vec();
879        pushed.push(0);
880        vote_account.set_data(pushed);
881        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
882
883        {
884            let stakes = stakes_cache.stakes();
885            let vote_accounts = stakes.vote_accounts();
886            assert!(vote_accounts.get(&vote_pubkey).is_none());
887            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
888        }
889
890        // Vote account uninitialized
891        let default_vote_state = VoteState::default();
892        let versioned = VoteStateVersions::new_current(default_vote_state);
893        vote_state::to(&versioned, &mut vote_account).unwrap();
894        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
895
896        {
897            let stakes = stakes_cache.stakes();
898            let vote_accounts = stakes.vote_accounts();
899            assert!(vote_accounts.get(&vote_pubkey).is_none());
900            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
901        }
902
903        vote_account.set_data(cache_data);
904        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
905
906        {
907            let stakes = stakes_cache.stakes();
908            let vote_accounts = stakes.vote_accounts();
909            assert!(vote_accounts.get(&vote_pubkey).is_some());
910            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
911        }
912    }
913
914    #[test]
915    fn test_stakes_change_delegate() {
916        let stakes_cache = StakesCache::new(Stakes {
917            epoch: 4,
918            ..Stakes::default()
919        });
920
921        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
922            create_staked_node_accounts(10);
923
924        let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
925            create_staked_node_accounts(10);
926
927        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
928        stakes_cache.check_and_store(&vote_pubkey2, &vote_account2, None);
929
930        // delegates to vote_pubkey
931        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
932
933        let stake = stake_state::stake_from(&stake_account).unwrap();
934
935        {
936            let stakes = stakes_cache.stakes();
937            let vote_accounts = stakes.vote_accounts();
938            assert!(vote_accounts.get(&vote_pubkey).is_some());
939            assert_eq!(
940                vote_accounts.get_delegated_stake(&vote_pubkey),
941                stake.stake(stakes.epoch, &stakes.stake_history, None)
942            );
943            assert!(vote_accounts.get(&vote_pubkey2).is_some());
944            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
945        }
946
947        // delegates to vote_pubkey2
948        stakes_cache.check_and_store(&stake_pubkey, &stake_account2, None);
949
950        {
951            let stakes = stakes_cache.stakes();
952            let vote_accounts = stakes.vote_accounts();
953            assert!(vote_accounts.get(&vote_pubkey).is_some());
954            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
955            assert!(vote_accounts.get(&vote_pubkey2).is_some());
956            assert_eq!(
957                vote_accounts.get_delegated_stake(&vote_pubkey2),
958                stake.stake(stakes.epoch, &stakes.stake_history, None)
959            );
960        }
961    }
962    #[test]
963    fn test_stakes_multiple_stakers() {
964        let stakes_cache = StakesCache::new(Stakes {
965            epoch: 4,
966            ..Stakes::default()
967        });
968
969        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
970            create_staked_node_accounts(10);
971
972        let stake_pubkey2 = solana_sdk::pubkey::new_rand();
973        let stake_account2 = create_stake_account(10, &vote_pubkey, &stake_pubkey2);
974
975        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
976
977        // delegates to vote_pubkey
978        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
979        stakes_cache.check_and_store(&stake_pubkey2, &stake_account2, None);
980
981        {
982            let stakes = stakes_cache.stakes();
983            let vote_accounts = stakes.vote_accounts();
984            assert!(vote_accounts.get(&vote_pubkey).is_some());
985            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
986        }
987    }
988
989    #[test]
990    fn test_activate_epoch() {
991        let stakes_cache = StakesCache::default();
992
993        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
994            create_staked_node_accounts(10);
995
996        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
997        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
998        let stake = stake_state::stake_from(&stake_account).unwrap();
999
1000        {
1001            let stakes = stakes_cache.stakes();
1002            let vote_accounts = stakes.vote_accounts();
1003            assert_eq!(
1004                vote_accounts.get_delegated_stake(&vote_pubkey),
1005                stake.stake(stakes.epoch, &stakes.stake_history, None)
1006            );
1007        }
1008        let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1009        stakes_cache.activate_epoch(3, &thread_pool, None);
1010        {
1011            let stakes = stakes_cache.stakes();
1012            let vote_accounts = stakes.vote_accounts();
1013            assert_eq!(
1014                vote_accounts.get_delegated_stake(&vote_pubkey),
1015                stake.stake(stakes.epoch, &stakes.stake_history, None)
1016            );
1017        }
1018    }
1019
1020    #[test]
1021    fn test_stakes_not_delegate() {
1022        let stakes_cache = StakesCache::new(Stakes {
1023            epoch: 4,
1024            ..Stakes::default()
1025        });
1026
1027        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1028            create_staked_node_accounts(10);
1029
1030        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
1031        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
1032
1033        {
1034            let stakes = stakes_cache.stakes();
1035            let vote_accounts = stakes.vote_accounts();
1036            assert!(vote_accounts.get(&vote_pubkey).is_some());
1037            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1038        }
1039
1040        // not a stake account, and whacks above entry
1041        stakes_cache.check_and_store(
1042            &stake_pubkey,
1043            &AccountSharedData::new(1, 0, &stake::program::id()),
1044            None,
1045        );
1046        {
1047            let stakes = stakes_cache.stakes();
1048            let vote_accounts = stakes.vote_accounts();
1049            assert!(vote_accounts.get(&vote_pubkey).is_some());
1050            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1051        }
1052    }
1053
1054    #[test]
1055    fn test_vote_balance_and_staked_empty() {
1056        let stakes = Stakes::<StakeAccount>::default();
1057        assert_eq!(stakes.vote_balance_and_staked(), 0);
1058    }
1059
1060    #[test]
1061    fn test_vote_balance_and_staked_normal() {
1062        let stakes_cache = StakesCache::default();
1063        #[allow(non_local_definitions)]
1064        impl Stakes<StakeAccount> {
1065            fn vote_balance_and_warmed_staked(&self) -> u64 {
1066                let vote_balance: u64 = self
1067                    .vote_accounts
1068                    .iter()
1069                    .map(|(_pubkey, account)| account.lamports())
1070                    .sum();
1071                let warmed_stake: u64 = self
1072                    .vote_accounts
1073                    .delegated_stakes()
1074                    .map(|(_pubkey, stake)| stake)
1075                    .sum();
1076                vote_balance + warmed_stake
1077            }
1078        }
1079
1080        let genesis_epoch = 0;
1081        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1082            create_warming_staked_node_accounts(10, genesis_epoch);
1083        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
1084        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
1085
1086        {
1087            let stakes = stakes_cache.stakes();
1088            assert_eq!(stakes.vote_balance_and_staked(), 11);
1089            assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
1090        }
1091
1092        let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1093        for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
1094            stakes_cache.activate_epoch(epoch, &thread_pool, None);
1095            // vote_balance_and_staked() always remain to return same lamports
1096            // while vote_balance_and_warmed_staked() gradually increases
1097            let stakes = stakes_cache.stakes();
1098            assert_eq!(stakes.vote_balance_and_staked(), 11);
1099            assert_eq!(
1100                stakes.vote_balance_and_warmed_staked(),
1101                *expected_warmed_stake
1102            );
1103        }
1104    }
1105}