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/fair-exchange/safecoin/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/// Stake<Delegation> is equivalent to the old code and is used for backward
163/// compatibility in 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        (
525            (vote_pubkey, vote_account),
526            create_stake_account(stake, &vote_pubkey),
527        )
528    }
529
530    //   add stake to a vote_pubkey                               (   stake    )
531    pub(crate) fn create_stake_account(
532        stake: u64,
533        vote_pubkey: &Pubkey,
534    ) -> (Pubkey, AccountSharedData) {
535        let stake_pubkey = solana_sdk::pubkey::new_rand();
536        (
537            stake_pubkey,
538            stake_state::create_account(
539                &stake_pubkey,
540                vote_pubkey,
541                &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
542                &Rent::free(),
543                stake,
544            ),
545        )
546    }
547
548    fn create_warming_staked_node_accounts(
549        stake: u64,
550        epoch: Epoch,
551    ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
552        let vote_pubkey = solana_sdk::pubkey::new_rand();
553        let vote_account =
554            vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
555        (
556            (vote_pubkey, vote_account),
557            create_warming_stake_account(stake, epoch, &vote_pubkey),
558        )
559    }
560
561    // add stake to a vote_pubkey                               (   stake    )
562    fn create_warming_stake_account(
563        stake: u64,
564        epoch: Epoch,
565        vote_pubkey: &Pubkey,
566    ) -> (Pubkey, AccountSharedData) {
567        let stake_pubkey = solana_sdk::pubkey::new_rand();
568        (
569            stake_pubkey,
570            stake_state::create_account_with_activation_epoch(
571                &stake_pubkey,
572                vote_pubkey,
573                &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
574                &Rent::free(),
575                stake,
576                epoch,
577            ),
578        )
579    }
580
581    #[test]
582    fn test_stakes_basic() {
583        for i in 0..4 {
584            let stakes_cache = StakesCache::new(Stakes {
585                epoch: i,
586                ..Stakes::default()
587            });
588
589            let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
590                create_staked_node_accounts(10);
591
592            stakes_cache.check_and_store(&vote_pubkey, &vote_account);
593            stakes_cache.check_and_store(&stake_pubkey, &stake_account);
594            let stake = stake_state::stake_from(&stake_account).unwrap();
595            {
596                let stakes = stakes_cache.stakes();
597                let vote_accounts = stakes.vote_accounts();
598                assert!(vote_accounts.get(&vote_pubkey).is_some());
599                assert_eq!(
600                    vote_accounts.get_delegated_stake(&vote_pubkey),
601                    stake.stake(i, None)
602                );
603            }
604
605            stake_account.set_lamports(42);
606            stakes_cache.check_and_store(&stake_pubkey, &stake_account);
607            {
608                let stakes = stakes_cache.stakes();
609                let vote_accounts = stakes.vote_accounts();
610                assert!(vote_accounts.get(&vote_pubkey).is_some());
611                assert_eq!(
612                    vote_accounts.get_delegated_stake(&vote_pubkey),
613                    stake.stake(i, None)
614                ); // stays old stake, because only 10 is activated
615            }
616
617            // activate more
618            let (_stake_pubkey, mut stake_account) = create_stake_account(42, &vote_pubkey);
619            stakes_cache.check_and_store(&stake_pubkey, &stake_account);
620            let stake = stake_state::stake_from(&stake_account).unwrap();
621            {
622                let stakes = stakes_cache.stakes();
623                let vote_accounts = stakes.vote_accounts();
624                assert!(vote_accounts.get(&vote_pubkey).is_some());
625                assert_eq!(
626                    vote_accounts.get_delegated_stake(&vote_pubkey),
627                    stake.stake(i, None)
628                ); // now stake of 42 is activated
629            }
630
631            stake_account.set_lamports(0);
632            stakes_cache.check_and_store(&stake_pubkey, &stake_account);
633            {
634                let stakes = stakes_cache.stakes();
635                let vote_accounts = stakes.vote_accounts();
636                assert!(vote_accounts.get(&vote_pubkey).is_some());
637                assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
638            }
639        }
640    }
641
642    #[test]
643    fn test_stakes_highest() {
644        let stakes_cache = StakesCache::default();
645
646        assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
647
648        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
649            create_staked_node_accounts(10);
650
651        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
652        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
653
654        let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
655            create_staked_node_accounts(20);
656
657        stakes_cache.check_and_store(&vote11_pubkey, &vote11_account);
658        stakes_cache.check_and_store(&stake11_pubkey, &stake11_account);
659
660        let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
661
662        let highest_staked_node = stakes_cache.stakes().highest_staked_node();
663        assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
664    }
665
666    #[test]
667    fn test_stakes_vote_account_disappear_reappear() {
668        let stakes_cache = StakesCache::new(Stakes {
669            epoch: 4,
670            ..Stakes::default()
671        });
672
673        let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
674            create_staked_node_accounts(10);
675
676        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
677        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
678
679        {
680            let stakes = stakes_cache.stakes();
681            let vote_accounts = stakes.vote_accounts();
682            assert!(vote_accounts.get(&vote_pubkey).is_some());
683            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
684        }
685
686        vote_account.set_lamports(0);
687        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
688
689        {
690            let stakes = stakes_cache.stakes();
691            let vote_accounts = stakes.vote_accounts();
692            assert!(vote_accounts.get(&vote_pubkey).is_none());
693            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
694        }
695
696        vote_account.set_lamports(1);
697        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
698
699        {
700            let stakes = stakes_cache.stakes();
701            let vote_accounts = stakes.vote_accounts();
702            assert!(vote_accounts.get(&vote_pubkey).is_some());
703            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
704        }
705
706        // Vote account too big
707        let cache_data = vote_account.data().to_vec();
708        let mut pushed = vote_account.data().to_vec();
709        pushed.push(0);
710        vote_account.set_data(pushed);
711        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
712
713        {
714            let stakes = stakes_cache.stakes();
715            let vote_accounts = stakes.vote_accounts();
716            assert!(vote_accounts.get(&vote_pubkey).is_none());
717            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
718        }
719
720        // Vote account uninitialized
721        let default_vote_state = VoteState::default();
722        let versioned = VoteStateVersions::new_current(default_vote_state);
723        VoteState::to(&versioned, &mut vote_account).unwrap();
724        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
725
726        {
727            let stakes = stakes_cache.stakes();
728            let vote_accounts = stakes.vote_accounts();
729            assert!(vote_accounts.get(&vote_pubkey).is_none());
730            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
731        }
732
733        vote_account.set_data(cache_data);
734        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
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
744    #[test]
745    fn test_stakes_change_delegate() {
746        let stakes_cache = StakesCache::new(Stakes {
747            epoch: 4,
748            ..Stakes::default()
749        });
750
751        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
752            create_staked_node_accounts(10);
753
754        let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
755            create_staked_node_accounts(10);
756
757        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
758        stakes_cache.check_and_store(&vote_pubkey2, &vote_account2);
759
760        // delegates to vote_pubkey
761        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
762
763        let stake = stake_state::stake_from(&stake_account).unwrap();
764
765        {
766            let stakes = stakes_cache.stakes();
767            let vote_accounts = stakes.vote_accounts();
768            assert!(vote_accounts.get(&vote_pubkey).is_some());
769            assert_eq!(
770                vote_accounts.get_delegated_stake(&vote_pubkey),
771                stake.stake(stakes.epoch, Some(&stakes.stake_history))
772            );
773            assert!(vote_accounts.get(&vote_pubkey2).is_some());
774            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
775        }
776
777        // delegates to vote_pubkey2
778        stakes_cache.check_and_store(&stake_pubkey, &stake_account2);
779
780        {
781            let stakes = stakes_cache.stakes();
782            let vote_accounts = stakes.vote_accounts();
783            assert!(vote_accounts.get(&vote_pubkey).is_some());
784            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
785            assert!(vote_accounts.get(&vote_pubkey2).is_some());
786            assert_eq!(
787                vote_accounts.get_delegated_stake(&vote_pubkey2),
788                stake.stake(stakes.epoch, Some(&stakes.stake_history))
789            );
790        }
791    }
792    #[test]
793    fn test_stakes_multiple_stakers() {
794        let stakes_cache = StakesCache::new(Stakes {
795            epoch: 4,
796            ..Stakes::default()
797        });
798
799        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
800            create_staked_node_accounts(10);
801
802        let (stake_pubkey2, stake_account2) = create_stake_account(10, &vote_pubkey);
803
804        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
805
806        // delegates to vote_pubkey
807        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
808        stakes_cache.check_and_store(&stake_pubkey2, &stake_account2);
809
810        {
811            let stakes = stakes_cache.stakes();
812            let vote_accounts = stakes.vote_accounts();
813            assert!(vote_accounts.get(&vote_pubkey).is_some());
814            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
815        }
816    }
817
818    #[test]
819    fn test_activate_epoch() {
820        let stakes_cache = StakesCache::default();
821
822        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
823            create_staked_node_accounts(10);
824
825        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
826        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
827        let stake = stake_state::stake_from(&stake_account).unwrap();
828
829        {
830            let stakes = stakes_cache.stakes();
831            let vote_accounts = stakes.vote_accounts();
832            assert_eq!(
833                vote_accounts.get_delegated_stake(&vote_pubkey),
834                stake.stake(stakes.epoch, Some(&stakes.stake_history))
835            );
836        }
837        let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
838        stakes_cache.activate_epoch(3, &thread_pool);
839        {
840            let stakes = stakes_cache.stakes();
841            let vote_accounts = stakes.vote_accounts();
842            assert_eq!(
843                vote_accounts.get_delegated_stake(&vote_pubkey),
844                stake.stake(stakes.epoch, Some(&stakes.stake_history))
845            );
846        }
847    }
848
849    #[test]
850    fn test_stakes_not_delegate() {
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        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
860        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
861
862        {
863            let stakes = stakes_cache.stakes();
864            let vote_accounts = stakes.vote_accounts();
865            assert!(vote_accounts.get(&vote_pubkey).is_some());
866            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
867        }
868
869        // not a stake account, and whacks above entry
870        stakes_cache.check_and_store(
871            &stake_pubkey,
872            &AccountSharedData::new(1, 0, &stake::program::id()),
873        );
874        {
875            let stakes = stakes_cache.stakes();
876            let vote_accounts = stakes.vote_accounts();
877            assert!(vote_accounts.get(&vote_pubkey).is_some());
878            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
879        }
880    }
881
882    #[test]
883    fn test_vote_balance_and_staked_empty() {
884        let stakes = Stakes::<StakeAccount>::default();
885        assert_eq!(stakes.vote_balance_and_staked(), 0);
886    }
887
888    #[test]
889    fn test_vote_balance_and_staked_normal() {
890        let stakes_cache = StakesCache::default();
891        impl Stakes<StakeAccount> {
892            fn vote_balance_and_warmed_staked(&self) -> u64 {
893                let vote_balance: u64 = self
894                    .vote_accounts
895                    .iter()
896                    .map(|(_pubkey, account)| account.lamports())
897                    .sum();
898                let warmed_stake: u64 = self
899                    .vote_accounts
900                    .delegated_stakes()
901                    .map(|(_pubkey, stake)| stake)
902                    .sum();
903                vote_balance + warmed_stake
904            }
905        }
906
907        let genesis_epoch = 0;
908        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
909            create_warming_staked_node_accounts(10, genesis_epoch);
910        stakes_cache.check_and_store(&vote_pubkey, &vote_account);
911        stakes_cache.check_and_store(&stake_pubkey, &stake_account);
912
913        {
914            let stakes = stakes_cache.stakes();
915            assert_eq!(stakes.vote_balance_and_staked(), 11);
916            assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
917        }
918
919        let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
920        for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
921            stakes_cache.activate_epoch(epoch, &thread_pool);
922            // vote_balance_and_staked() always remain to return same lamports
923            // while vote_balance_and_warmed_staked() gradually increases
924            let stakes = stakes_cache.stakes();
925            assert_eq!(stakes.vote_balance_and_staked(), 11);
926            assert_eq!(
927                stakes.vote_balance_and_warmed_staked(),
928                *expected_warmed_stake
929            );
930        }
931    }
932
933    #[test]
934    fn test_serde_stakes_enum_compat() {
935        #[derive(Debug, PartialEq, Deserialize, Serialize)]
936        struct Dummy {
937            head: String,
938            #[serde(with = "serde_stakes_enum_compat")]
939            stakes: Arc<StakesEnum>,
940            tail: String,
941        }
942        let mut rng = rand::thread_rng();
943        let stakes_cache = StakesCache::new(Stakes {
944            unused: rng.gen(),
945            epoch: rng.gen(),
946            ..Stakes::default()
947        });
948        for _ in 0..rng.gen_range(5usize, 10) {
949            let vote_pubkey = solana_sdk::pubkey::new_rand();
950            let vote_account = vote_state::create_account(
951                &vote_pubkey,
952                &solana_sdk::pubkey::new_rand(), // node_pubkey
953                rng.gen_range(0, 101),           // commission
954                rng.gen_range(0, 1_000_000),     // lamports
955            );
956            stakes_cache.check_and_store(&vote_pubkey, &vote_account);
957            for _ in 0..rng.gen_range(10usize, 20) {
958                let stake_pubkey = solana_sdk::pubkey::new_rand();
959                let rent = Rent::with_slots_per_epoch(rng.gen());
960                let stake_account = stake_state::create_account(
961                    &stake_pubkey, // authorized
962                    &vote_pubkey,
963                    &vote_account,
964                    &rent,
965                    rng.gen_range(0, 1_000_000), // lamports
966                );
967                stakes_cache.check_and_store(&stake_pubkey, &stake_account);
968            }
969        }
970        let stakes: Stakes<StakeAccount> = stakes_cache.stakes().clone();
971        assert!(stakes.vote_accounts.as_ref().len() >= 5);
972        assert!(stakes.stake_delegations.len() >= 50);
973        let dummy = Dummy {
974            head: String::from("dummy-head"),
975            stakes: Arc::new(StakesEnum::from(stakes.clone())),
976            tail: String::from("dummy-tail"),
977        };
978        assert!(dummy.stakes.vote_accounts().as_ref().len() >= 5);
979        let data = bincode::serialize(&dummy).unwrap();
980        let other: Dummy = bincode::deserialize(&data).unwrap();
981        assert_eq!(other, dummy);
982        let stakes = Stakes::<Delegation>::from(stakes);
983        assert!(stakes.vote_accounts.as_ref().len() >= 5);
984        assert!(stakes.stake_delegations.len() >= 50);
985        let other = match &*other.stakes {
986            StakesEnum::Accounts(_) => panic!("wrong type!"),
987            StakesEnum::Delegations(delegations) => delegations,
988        };
989        assert_eq!(other, &stakes)
990    }
991}