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