solana_runtime/
stakes.rs

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