solana_runtime/
stakes.rs

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