Skip to main content

solana_runtime/
stakes.rs

1//! Stakes serve as a cache of stake and vote accounts to derive
2//! node stakes
3#[cfg(feature = "frozen-abi")]
4use solana_frozen_abi::stable_abi::{context::SequenceLenMax, sample_collection_sized};
5use {
6    crate::{
7        alpenglow_epoch_type::RewardEpochDelegatedStakes,
8        stake_account,
9        stake_delegation::{delegation_activation_status, delegation_effective_stake},
10        stake_history::StakeHistory,
11    },
12    imbl::HashMap as ImblHashMap,
13    log::error,
14    num_derive::ToPrimitive,
15    rayon::{ThreadPool, prelude::*},
16    serde::Serialize,
17    solana_account::{AccountSharedData, ReadableAccount},
18    solana_accounts_db::utils::create_account_shared_data,
19    solana_clock::Epoch,
20    solana_leader_schedule::SlotLeader,
21    solana_pubkey::Pubkey,
22    solana_stake_interface::{
23        program as stake_program,
24        state::{Delegation, StakeActivationStatus},
25    },
26    solana_vote::vote_account::{VoteAccount, VoteAccounts, VoteAccountsHashMap},
27    solana_vote_interface::state::VoteStateVersions,
28    std::{
29        collections::HashMap,
30        sync::{Arc, RwLock, RwLockReadGuard},
31    },
32    thiserror::Error,
33    wincode::{SchemaWrite, containers::FromIntoIterator, len::BincodeLen},
34};
35#[cfg(feature = "dev-context-only-utils")]
36use {
37    qualifier_attr::{field_qualifiers, qualifiers},
38    solana_stake_interface::state::Stake,
39};
40
41mod serde_stakes;
42#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
43pub(crate) use serde_stakes::DeserializableDelegationStakes;
44pub use serde_stakes::SerdeStakesToStakeFormat;
45#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
46pub(crate) use serde_stakes::serialize_stake_accounts_to_delegation_format;
47
48#[derive(Debug, Error)]
49pub enum Error {
50    #[error("Invalid delegation: {0}")]
51    InvalidDelegation(Pubkey),
52    #[error(transparent)]
53    InvalidStakeAccount(#[from] stake_account::Error),
54    #[error("Stake account not found: {0}")]
55    StakeAccountNotFound(Pubkey),
56    #[error("Vote account mismatch: {0}")]
57    VoteAccountMismatch(Pubkey),
58    #[error("Vote account not cached: {0}")]
59    VoteAccountNotCached(Pubkey),
60    #[error("Vote account not found: {0}")]
61    VoteAccountNotFound(Pubkey),
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)]
65pub enum InvalidCacheEntryReason {
66    Missing,
67    BadState,
68    WrongOwner,
69}
70
71type StakeAccount = stake_account::StakeAccount<Delegation>;
72pub(crate) type DelegatedStakes = ImblHashMap<Pubkey, u64>;
73
74/// StakeStateV2::Stake, a stake with a delegation.
75const STAKE_STATE_STAKE_TAG: [u8; 4] = 2u32.to_le_bytes();
76
77fn stakes_cache_account_shared_data(account: &AccountSharedData) -> AccountSharedData {
78    if account.capacity() > account.data().len() {
79        create_account_shared_data(account)
80    } else {
81        account.clone()
82    }
83}
84
85#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
86#[derive(Default, Debug)]
87pub(crate) struct StakesCache(RwLock<Stakes<StakeAccount>>);
88
89impl StakesCache {
90    pub(crate) fn new(stakes: Stakes<StakeAccount>) -> Self {
91        Self(RwLock::new(stakes))
92    }
93
94    pub(crate) fn stakes(&self) -> RwLockReadGuard<'_, Stakes<StakeAccount>> {
95        self.0.read().unwrap()
96    }
97
98    pub(crate) fn check_and_store(
99        &self,
100        pubkey: &Pubkey,
101        account: &AccountSharedData,
102        new_rate_activation_epoch: Option<Epoch>,
103        use_fixed_point_stake_math: bool,
104    ) {
105        // TODO: If the account is already cached as a vote or stake account
106        // but the owner changes, then this needs to evict the account from
107        // the cache. see:
108        // https://github.com/solana-labs/solana/pull/24200#discussion_r849935444
109        let owner = account.owner();
110        // Zero lamport accounts are not stored in accounts-db
111        // and so should be removed from cache as well.
112        if account.lamports() == 0 {
113            if solana_vote_program::check_id(owner) {
114                let _old_vote_account = {
115                    let mut stakes = self.0.write().unwrap();
116                    stakes.remove_vote_account(pubkey)
117                };
118            } else if stake_program::check_id(owner) {
119                let mut stakes = self.0.write().unwrap();
120                stakes.remove_stake_delegation(
121                    pubkey,
122                    new_rate_activation_epoch,
123                    use_fixed_point_stake_math,
124                );
125            }
126            return;
127        }
128        debug_assert_ne!(account.lamports(), 0u64);
129        if solana_vote_program::check_id(owner) {
130            if VoteStateVersions::is_correct_size_and_initialized(account.data()) {
131                match VoteAccount::try_from(stakes_cache_account_shared_data(account)) {
132                    Ok(vote_account) => {
133                        // drop the old account after releasing the lock
134                        let _old_vote_account = {
135                            let mut stakes = self.0.write().unwrap();
136                            stakes.upsert_vote_account(pubkey, vote_account)
137                        };
138                    }
139                    Err(_) => {
140                        // drop the old account after releasing the lock
141                        let _old_vote_account = {
142                            let mut stakes = self.0.write().unwrap();
143                            stakes.remove_vote_account(pubkey)
144                        };
145                    }
146                }
147            } else {
148                // drop the old account after releasing the lock
149                let _old_vote_account = {
150                    let mut stakes = self.0.write().unwrap();
151                    stakes.remove_vote_account(pubkey)
152                };
153            };
154        } else if stake_program::check_id(owner) {
155            if account.data().starts_with(&STAKE_STATE_STAKE_TAG)
156                && let Ok(stake_account) =
157                    StakeAccount::try_from(stakes_cache_account_shared_data(account))
158            {
159                let mut stakes = self.0.write().unwrap();
160                stakes.upsert_stake_delegation(
161                    *pubkey,
162                    stake_account,
163                    new_rate_activation_epoch,
164                    use_fixed_point_stake_math,
165                );
166            } else {
167                let mut stakes = self.0.write().unwrap();
168                stakes.remove_stake_delegation(
169                    pubkey,
170                    new_rate_activation_epoch,
171                    use_fixed_point_stake_math,
172                );
173            }
174        }
175    }
176
177    pub(crate) fn activate_epoch(
178        &self,
179        next_epoch: Epoch,
180        stake_history: StakeHistory,
181        vote_accounts: VoteAccounts,
182        delegated_stakes: DelegatedStakes,
183    ) {
184        let mut stakes = self.0.write().unwrap();
185        stakes.activate_epoch(next_epoch, stake_history, vote_accounts, delegated_stakes)
186    }
187
188    pub(crate) fn refresh_delegated_stakes(
189        &self,
190        new_rate_activation_epoch: Option<Epoch>,
191        use_fixed_point_stake_math: bool,
192    ) {
193        let mut stakes = self.0.write().unwrap();
194        stakes.refresh_delegated_stakes(new_rate_activation_epoch, use_fixed_point_stake_math);
195    }
196}
197
198/// The generic type T is either Delegation or StakeAccount.
199/// [`Stakes<Delegation>`] is equivalent to the old code and is used for backward
200/// compatibility in [`crate::bank::BankFieldsToDeserialize`].
201/// But banks cache [`Stakes<StakeAccount>`] which includes the entire stake
202/// account and StakeStateV2 deserialized from the account. Doing so, will remove
203/// the need to load the stake account from accounts-db when working with
204/// stake-delegations.
205#[cfg_attr(feature = "frozen-abi", derive(AbiExample, StableAbi, StableAbiSample))]
206#[derive(Default, Clone, PartialEq, Debug, Serialize, SchemaWrite)]
207#[cfg_attr(
208    feature = "dev-context-only-utils",
209    field_qualifiers(
210        vote_accounts(pub),
211        stake_delegations(pub),
212        delegated_stakes(pub),
213        unused(pub),
214        epoch(pub),
215        stake_history(pub),
216    )
217)]
218pub struct Stakes<T: Clone> {
219    /// vote accounts
220    vote_accounts: VoteAccounts,
221
222    /// stake_delegations
223    #[cfg_attr(
224        feature = "frozen-abi",
225        stable_abi_sample(with = "sample_collection_sized(rng, SequenceLenMax(1))")
226    )]
227    #[wincode(with = "FromIntoIterator<ImblHashMap<Pubkey, T>, BincodeLen>")]
228    stake_delegations: ImblHashMap<Pubkey, T>,
229
230    /// current effective stake delegated to each vote account pubkey
231    #[cfg_attr(feature = "frozen-abi", stable_abi_sample(with = "Default::default()"))]
232    #[serde(skip)]
233    #[wincode(skip)]
234    delegated_stakes: DelegatedStakes,
235
236    /// unused
237    unused: u64,
238
239    /// current epoch, used to calculate current stake
240    epoch: Epoch,
241
242    /// history of staking levels
243    stake_history: StakeHistory,
244}
245
246impl<T: Clone> Stakes<T> {
247    pub fn new(vote_accounts: VoteAccounts, epoch: Epoch) -> Stakes<T> {
248        Stakes {
249            vote_accounts,
250            epoch,
251            stake_delegations: ImblHashMap::new(),
252            delegated_stakes: DelegatedStakes::default(),
253            unused: 0,
254            stake_history: StakeHistory::default(),
255        }
256    }
257
258    pub fn clone_and_filter_for_vat(
259        &self,
260        max_vote_accounts: usize,
261        minimum_vote_account_balance: u64,
262    ) -> Stakes<T> {
263        Self::new(
264            self.vote_accounts
265                .clone_and_filter_for_vat(max_vote_accounts, minimum_vote_account_balance),
266            self.epoch,
267        )
268    }
269
270    pub fn vote_accounts(&self) -> &VoteAccounts {
271        &self.vote_accounts
272    }
273
274    pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
275        self.vote_accounts.staked_nodes()
276    }
277
278    /// Destructure self and return the fields needed by EpochStakes
279    pub(crate) fn into_epoch_stakes_fields(self) -> (Epoch, VoteAccounts, StakeHistory) {
280        let Self {
281            vote_accounts,
282            stake_delegations: _,
283            delegated_stakes: _,
284            unused: _,
285            epoch,
286            stake_history,
287        } = self;
288        (epoch, vote_accounts, stake_history)
289    }
290}
291
292impl Stakes<StakeAccount> {
293    pub(crate) fn new_from_accounts_for_genesis<'a, T: ReadableAccount + 'a>(
294        new_rate_activation_epoch: Option<Epoch>,
295        accounts: impl IntoIterator<Item = (&'a Pubkey, &'a T)>,
296        use_fixed_point_stake_math: bool,
297    ) -> Self {
298        let stake_history = StakeHistory::default();
299        let mut vote_accounts = VoteAccountsHashMap::default();
300        let mut delegated_stakes = DelegatedStakes::default();
301        let mut stake_delegations = ImblHashMap::new();
302        let epoch = 0;
303
304        for (pubkey, account) in accounts {
305            if account.lamports() == 0 {
306                continue;
307            }
308
309            if solana_vote_program::check_id(account.owner()) {
310                if VoteStateVersions::is_correct_size_and_initialized(account.data())
311                    && let Ok(vote_account) =
312                        VoteAccount::try_from(create_account_shared_data(account))
313                {
314                    vote_accounts.insert(*pubkey, (0, vote_account));
315                }
316            } else if stake_program::check_id(account.owner())
317                && let Ok(stake_account) =
318                    StakeAccount::try_from(create_account_shared_data(account))
319            {
320                let delegation = stake_account.delegation();
321                let stake = delegation_effective_stake(
322                    delegation,
323                    epoch,
324                    &stake_history,
325                    new_rate_activation_epoch,
326                    use_fixed_point_stake_math,
327                );
328                if stake != 0 {
329                    *delegated_stakes.entry(delegation.voter_pubkey).or_default() += stake;
330                }
331                stake_delegations.insert(*pubkey, stake_account);
332            }
333        }
334
335        let mut vote_accounts = VoteAccounts::from(Arc::new(vote_accounts));
336        for (vote_pubkey, stake) in &delegated_stakes {
337            vote_accounts.add_stake(vote_pubkey, *stake);
338        }
339
340        Self {
341            vote_accounts,
342            stake_delegations,
343            delegated_stakes,
344            unused: 0,
345            epoch,
346            stake_history,
347        }
348    }
349
350    /// Creates a Stake<StakeAccount> from DeserializableDelegationStakes by loading the
351    /// full account state for respective stake pubkeys. get_account function
352    /// should return the account at the respective slot where stakes where
353    /// cached.
354    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
355    pub(crate) fn load_from_deserialized_delegations<F>(
356        stakes: DeserializableDelegationStakes,
357        get_account: F,
358    ) -> Result<Self, Error>
359    where
360        F: Fn(&Pubkey) -> Option<AccountSharedData> + Sync,
361    {
362        let stake_delegations = stakes
363            .stake_delegations
364            .into_par_iter()
365            // We use fold/reduce to aggregate the results, which does a bit more work than calling
366            // collect()/collect_vec_list() and then imbl::HashMap::from_iter(collected.into_iter()),
367            // but it does it in background threads, so effectively it's faster.
368            .try_fold(ImblHashMap::new, |mut map, (pubkey, delegation)| {
369                let Some(stake_account) = get_account(&pubkey) else {
370                    return Err(Error::StakeAccountNotFound(pubkey));
371                };
372
373                // Assert that all valid vote-accounts referenced in stake delegations are already
374                // contained in `stakes.vote_account`.
375                let voter_pubkey = &delegation.voter_pubkey;
376                if stakes.vote_accounts.get(voter_pubkey).is_none()
377                    && let Some(account) = get_account(voter_pubkey)
378                    && VoteStateVersions::is_correct_size_and_initialized(account.data())
379                    && VoteAccount::try_from(account.clone()).is_ok()
380                {
381                    error!("vote account not cached: {voter_pubkey}, {account:?}");
382                    return Err(Error::VoteAccountNotCached(*voter_pubkey));
383                }
384
385                let stake_account = StakeAccount::try_from(stake_account)?;
386                // Sanity check that the delegation is consistent with what is
387                // stored in the account.
388                if stake_account.delegation() == &delegation {
389                    map.insert(pubkey, stake_account);
390                    Ok(map)
391                } else {
392                    Err(Error::InvalidDelegation(pubkey))
393                }
394            })
395            .try_reduce(ImblHashMap::new, |a, b| Ok(a.union(b)))?;
396
397        // Assert that cached vote accounts are consistent with accounts-db.
398        //
399        // This currently includes ~5500 accounts, parallelizing brings minor
400        // (sub 2s) improvements.
401        for (pubkey, vote_account) in stakes.vote_accounts.iter() {
402            let Some(account) = get_account(pubkey) else {
403                return Err(Error::VoteAccountNotFound(*pubkey));
404            };
405            let vote_account = vote_account.account();
406            if vote_account != &account {
407                error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}");
408                return Err(Error::VoteAccountMismatch(*pubkey));
409            }
410        }
411
412        Ok(Self {
413            vote_accounts: stakes.vote_accounts.clone(),
414            stake_delegations,
415            delegated_stakes: DelegatedStakes::default(),
416            unused: stakes.unused,
417            epoch: stakes.epoch,
418            stake_history: stakes.stake_history,
419        })
420    }
421
422    #[cfg(feature = "dev-context-only-utils")]
423    pub fn new_for_tests(
424        epoch: Epoch,
425        vote_accounts: VoteAccounts,
426        stake_delegations: ImblHashMap<Pubkey, StakeAccount>,
427    ) -> Self {
428        let stake_history = StakeHistory::default();
429        let delegated_stakes =
430            Self::calculate_delegated_stakes(&stake_delegations, epoch, &stake_history, None, true);
431        Self {
432            vote_accounts,
433            stake_delegations,
434            delegated_stakes,
435            unused: 0,
436            epoch,
437            stake_history,
438        }
439    }
440
441    pub(crate) fn history(&self) -> &StakeHistory {
442        &self.stake_history
443    }
444
445    pub(crate) fn calculate_activated_stake(
446        &self,
447        next_epoch: Epoch,
448        thread_pool: &ThreadPool,
449        new_rate_activation_epoch: Option<Epoch>,
450        stake_delegations: &[(&Pubkey, &StakeAccount)],
451        use_fixed_point_stake_math: bool,
452    ) -> (
453        StakeHistory,
454        VoteAccounts,
455        DelegatedStakes,
456        RewardEpochDelegatedStakes,
457    ) {
458        // Wrap up the prev epoch by adding new stake history entry for the
459        // prev epoch.
460        let (stake_history_entry, effective_delegated_stakes) = thread_pool.install(|| {
461            stake_delegations
462                .par_iter()
463                .fold(
464                    || (StakeActivationStatus::default(), HashMap::default()),
465                    |(acc, mut delegated_stakes), (_stake_pubkey, stake_account)| {
466                        let delegation = stake_account.delegation();
467                        let activation_status = delegation_activation_status(
468                            delegation,
469                            self.epoch,
470                            &self.stake_history,
471                            new_rate_activation_epoch,
472                            use_fixed_point_stake_math,
473                        );
474                        *delegated_stakes.entry(delegation.voter_pubkey).or_default() +=
475                            activation_status.effective;
476                        (acc + activation_status, delegated_stakes)
477                    },
478                )
479                .reduce(
480                    || (StakeActivationStatus::default(), HashMap::default()),
481                    |(activation_status_a, delegated_stakes_a),
482                     (activation_status_b, delegated_stakes_b)| {
483                        (
484                            activation_status_a + activation_status_b,
485                            merge_delegated_stakes(delegated_stakes_a, delegated_stakes_b),
486                        )
487                    },
488                )
489        });
490        let mut stake_history = self.stake_history.clone();
491        stake_history.add(self.epoch, stake_history_entry);
492        // Refresh the stake distribution of vote accounts for the next epoch,
493        // using new stake history.
494        let (vote_accounts, delegated_stakes) = refresh_vote_accounts(
495            thread_pool,
496            next_epoch,
497            &self.vote_accounts,
498            stake_delegations,
499            &stake_history,
500            new_rate_activation_epoch,
501            use_fixed_point_stake_math,
502        );
503        let reward_epoch_delegated_stakes = RewardEpochDelegatedStakes {
504            epoch: self.epoch,
505            delegated_stakes: effective_delegated_stakes,
506        };
507        (
508            stake_history,
509            vote_accounts,
510            delegated_stakes,
511            reward_epoch_delegated_stakes,
512        )
513    }
514
515    pub(crate) fn activate_epoch(
516        &mut self,
517        next_epoch: Epoch,
518        stake_history: StakeHistory,
519        vote_accounts: VoteAccounts,
520        delegated_stakes: DelegatedStakes,
521    ) {
522        self.epoch = next_epoch;
523        self.stake_history = stake_history;
524        self.vote_accounts = vote_accounts;
525        self.delegated_stakes = delegated_stakes;
526    }
527
528    fn calculate_delegated_stakes(
529        stake_delegations: &ImblHashMap<Pubkey, StakeAccount>,
530        epoch: Epoch,
531        stake_history: &StakeHistory,
532        new_rate_activation_epoch: Option<Epoch>,
533        use_fixed_point_stake_math: bool,
534    ) -> DelegatedStakes {
535        let mut delegated_stakes = DelegatedStakes::new();
536        for stake_account in stake_delegations.values() {
537            let delegation = stake_account.delegation();
538            let stake = delegation_effective_stake(
539                delegation,
540                epoch,
541                stake_history,
542                new_rate_activation_epoch,
543                use_fixed_point_stake_math,
544            );
545            if stake != 0 {
546                *delegated_stakes.entry(delegation.voter_pubkey).or_default() += stake;
547            }
548        }
549        delegated_stakes
550    }
551
552    fn refresh_delegated_stakes(
553        &mut self,
554        new_rate_activation_epoch: Option<Epoch>,
555        use_fixed_point_stake_math: bool,
556    ) {
557        self.delegated_stakes = Self::calculate_delegated_stakes(
558            &self.stake_delegations,
559            self.epoch,
560            &self.stake_history,
561            new_rate_activation_epoch,
562            use_fixed_point_stake_math,
563        );
564    }
565
566    fn add_delegated_stake(&mut self, voter_pubkey: Pubkey, stake: u64) {
567        if stake == 0 {
568            return;
569        }
570        *self.delegated_stakes.entry(voter_pubkey).or_default() += stake;
571    }
572
573    fn sub_delegated_stake(&mut self, voter_pubkey: &Pubkey, stake: u64) {
574        if stake == 0 {
575            return;
576        }
577        let current_stake = self
578            .delegated_stakes
579            .get_mut(voter_pubkey)
580            .expect("subtraction from missing delegated stake");
581        *current_stake = current_stake
582            .checked_sub(stake)
583            .expect("subtraction value exceeds delegated stake");
584        if *current_stake == 0 {
585            self.delegated_stakes.remove(voter_pubkey);
586        }
587    }
588
589    fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) -> Option<VoteAccount> {
590        self.vote_accounts.remove(vote_pubkey).map(|(_, a)| a)
591    }
592
593    fn remove_stake_delegation(
594        &mut self,
595        stake_pubkey: &Pubkey,
596        new_rate_activation_epoch: Option<Epoch>,
597        use_fixed_point_stake_math: bool,
598    ) {
599        if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
600            let removed_delegation = stake_account.delegation();
601            let removed_stake = delegation_effective_stake(
602                removed_delegation,
603                self.epoch,
604                &self.stake_history,
605                new_rate_activation_epoch,
606                use_fixed_point_stake_math,
607            );
608            self.sub_delegated_stake(&removed_delegation.voter_pubkey, removed_stake);
609            self.vote_accounts
610                .sub_stake(&removed_delegation.voter_pubkey, removed_stake);
611        }
612    }
613
614    fn upsert_vote_account(
615        &mut self,
616        vote_pubkey: &Pubkey,
617        vote_account: VoteAccount,
618    ) -> Option<VoteAccount> {
619        debug_assert_ne!(vote_account.lamports(), 0u64);
620
621        let calculate_delegated_stake = || {
622            self.delegated_stakes
623                .get(vote_pubkey)
624                .copied()
625                .unwrap_or_default()
626        };
627        self.vote_accounts
628            .insert(*vote_pubkey, vote_account, calculate_delegated_stake)
629    }
630
631    fn upsert_stake_delegation(
632        &mut self,
633        stake_pubkey: Pubkey,
634        stake_account: StakeAccount,
635        new_rate_activation_epoch: Option<Epoch>,
636        use_fixed_point_stake_math: bool,
637    ) {
638        debug_assert_ne!(stake_account.lamports(), 0u64);
639        let delegation = stake_account.delegation();
640        let voter_pubkey = delegation.voter_pubkey;
641        let stake = delegation_effective_stake(
642            delegation,
643            self.epoch,
644            &self.stake_history,
645            new_rate_activation_epoch,
646            use_fixed_point_stake_math,
647        );
648        match self.stake_delegations.insert(stake_pubkey, stake_account) {
649            None => {
650                self.add_delegated_stake(voter_pubkey, stake);
651                self.vote_accounts.add_stake(&voter_pubkey, stake);
652            }
653            Some(old_stake_account) => {
654                let old_delegation = old_stake_account.delegation();
655                let old_voter_pubkey = old_delegation.voter_pubkey;
656                let old_stake = delegation_effective_stake(
657                    old_delegation,
658                    self.epoch,
659                    &self.stake_history,
660                    new_rate_activation_epoch,
661                    use_fixed_point_stake_math,
662                );
663                if voter_pubkey != old_voter_pubkey || stake != old_stake {
664                    self.sub_delegated_stake(&old_voter_pubkey, old_stake);
665                    self.add_delegated_stake(voter_pubkey, stake);
666                    self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
667                    self.vote_accounts.add_stake(&voter_pubkey, stake);
668                }
669            }
670        }
671    }
672
673    /// Returns a reference to the map of stake delegations.
674    ///
675    /// # Performance
676    ///
677    /// `[imbl::HashMap]` is a [hash array mapped trie (HAMT)][hamt], which means
678    /// that inserts, deletions and lookups are average-case O(1) and
679    /// worst-case O(log n). However, the performance of iterations is poor due
680    /// to depth-first traversal and jumps. Currently it's also impossible to
681    /// iterate over it with [`rayon`].
682    ///
683    /// [hamt]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie
684    pub(crate) fn stake_delegations(&self) -> &ImblHashMap<Pubkey, StakeAccount> {
685        &self.stake_delegations
686    }
687
688    /// Collects stake delegations into a vector, which then can be used for
689    /// parallel iteration with [`rayon`].
690    ///
691    /// # Performance
692    ///
693    /// The execution of this method takes ~200ms and it collects elements of
694    /// the [`imbl::HashMap`], which is a [hash array mapped trie (HAMT)][hamt],
695    /// so that operation involves a depth-first traversal with jumps. However,
696    /// it's still a reasonable tradeoff if the caller iterates over these
697    /// elements.
698    ///
699    /// [hamt]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie
700    pub(crate) fn stake_delegations_vec(&self) -> Vec<(&Pubkey, &StakeAccount)> {
701        self.stake_delegations.iter().collect()
702    }
703
704    pub(crate) fn highest_staked_node(&self) -> Option<SlotLeader> {
705        let (vote_address, vote_account) = self.vote_accounts.find_max_by_delegated_stake()?;
706        Some(SlotLeader {
707            id: *vote_account.node_pubkey(),
708            vote_address: *vote_address,
709        })
710    }
711}
712
713/// Macro to generate `From<Stakes<From>> for Stakes<To>` impls.
714#[cfg(feature = "dev-context-only-utils")]
715macro_rules! impl_stake_format_conversion {
716    ($from:ty, $to:ty, |$binding:ident| $expr:expr) => {
717        /// This conversion is memory intensive so should only be used in development contexts.
718        impl From<Stakes<$from>> for Stakes<$to> {
719            fn from(stakes: Stakes<$from>) -> Self {
720                let Stakes {
721                    vote_accounts,
722                    stake_delegations,
723                    delegated_stakes: _,
724                    unused,
725                    epoch,
726                    stake_history,
727                } = stakes;
728                let stake_delegations = stake_delegations
729                    .into_iter()
730                    .map(|(pubkey, $binding)| (pubkey, $expr))
731                    .collect();
732                Self {
733                    vote_accounts,
734                    stake_delegations,
735                    delegated_stakes: DelegatedStakes::default(),
736                    unused,
737                    epoch,
738                    stake_history,
739                }
740            }
741        }
742    };
743}
744
745#[cfg(feature = "dev-context-only-utils")]
746impl_stake_format_conversion!(StakeAccount, Delegation, |sa| *sa.delegation());
747
748#[cfg(feature = "dev-context-only-utils")]
749impl_stake_format_conversion!(StakeAccount, Stake, |sa| *sa.stake());
750
751#[cfg(feature = "dev-context-only-utils")]
752impl_stake_format_conversion!(Stake, Delegation, |stake| stake.delegation);
753
754fn merge_delegated_stakes(
755    mut stakes: HashMap</*voter:*/ Pubkey, /*stake:*/ u64>,
756    other: HashMap</*voter:*/ Pubkey, /*stake:*/ u64>,
757) -> HashMap</*voter:*/ Pubkey, /*stake:*/ u64> {
758    if stakes.len() < other.len() {
759        return merge_delegated_stakes(other, stakes);
760    }
761    for (pubkey, stake) in other {
762        *stakes.entry(pubkey).or_default() += stake;
763    }
764    stakes
765}
766
767fn refresh_vote_accounts(
768    thread_pool: &ThreadPool,
769    epoch: Epoch,
770    vote_accounts: &VoteAccounts,
771    stake_delegations: &[(&Pubkey, &StakeAccount)],
772    stake_history: &StakeHistory,
773    new_rate_activation_epoch: Option<Epoch>,
774    use_fixed_point_stake_math: bool,
775) -> (VoteAccounts, DelegatedStakes) {
776    fn merge(mut stakes: DelegatedStakes, other: DelegatedStakes) -> DelegatedStakes {
777        if stakes.len() < other.len() {
778            return merge(other, stakes);
779        }
780        for (pubkey, stake) in other {
781            *stakes.entry(pubkey).or_default() += stake;
782        }
783        stakes
784    }
785    let delegated_stakes = thread_pool.install(|| {
786        stake_delegations
787            .par_iter()
788            .fold(
789                DelegatedStakes::default,
790                |mut delegated_stakes, (_stake_pubkey, stake_account)| {
791                    let delegation = stake_account.delegation();
792                    let stake = delegation_effective_stake(
793                        delegation,
794                        epoch,
795                        stake_history,
796                        new_rate_activation_epoch,
797                        use_fixed_point_stake_math,
798                    );
799                    if stake != 0 {
800                        *delegated_stakes.entry(delegation.voter_pubkey).or_default() += stake;
801                    }
802                    delegated_stakes
803                },
804            )
805            .reduce(DelegatedStakes::default, merge)
806    });
807    let vote_accounts = vote_accounts
808        .iter()
809        .map(|(&vote_pubkey, vote_account)| {
810            let delegated_stake = delegated_stakes
811                .get(&vote_pubkey)
812                .copied()
813                .unwrap_or_default();
814            (vote_pubkey, (delegated_stake, vote_account.clone()))
815        })
816        .collect();
817    (vote_accounts, delegated_stakes)
818}
819
820#[cfg(test)]
821pub(crate) mod tests {
822    use {
823        super::*,
824        crate::{stake_delegation::effective_stake, stake_utils},
825        rayon::ThreadPoolBuilder,
826        solana_account::{WritableAccount, state_traits::StateMutWincode as _},
827        solana_pubkey::Pubkey,
828        solana_rent::Rent,
829        solana_stake_interface::{self as stake, state::StakeStateV2},
830        solana_vote_interface::state::{BLS_PUBLIC_KEY_COMPRESSED_SIZE, VoteStateV4},
831        solana_vote_program::vote_state,
832    };
833
834    impl Stakes<Delegation> {
835        /// Convert deserialized stakes into runtime stakes representation
836        pub(crate) fn from_deserialized(stakes: DeserializableDelegationStakes) -> Self {
837            Self {
838                vote_accounts: stakes.vote_accounts,
839                stake_delegations: ImblHashMap::from_iter(stakes.stake_delegations),
840                delegated_stakes: DelegatedStakes::default(),
841                unused: stakes.unused,
842                epoch: stakes.epoch,
843                stake_history: stakes.stake_history,
844            }
845        }
846    }
847
848    //  set up some dummies for a staked node     ((     vote      )  (     stake     ))
849    pub(crate) fn create_staked_node_accounts(
850        stake: u64,
851        rent: &Rent,
852    ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
853        let vote_pubkey = solana_pubkey::new_rand();
854        let node_pubkey = solana_pubkey::new_rand();
855        let vote_account = vote_state::create_v4_account_with_authorized(
856            &node_pubkey,
857            &vote_pubkey,
858            [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
859            &vote_pubkey,
860            0,
861            &vote_pubkey,
862            0,
863            &node_pubkey,
864            1,
865        );
866        let stake_pubkey = solana_pubkey::new_rand();
867        (
868            (vote_pubkey, vote_account),
869            (
870                stake_pubkey,
871                create_stake_account(stake, &vote_pubkey, &stake_pubkey, rent),
872            ),
873        )
874    }
875
876    //   add stake to a vote_pubkey                               (   stake    )
877    pub(crate) fn create_stake_account(
878        stake: u64,
879        vote_pubkey: &Pubkey,
880        stake_pubkey: &Pubkey,
881        rent: &Rent,
882    ) -> AccountSharedData {
883        let node_pubkey = solana_pubkey::new_rand();
884        let lamports = rent.minimum_balance(StakeStateV2::size_of()) + stake;
885        stake_utils::create_stake_account(
886            stake_pubkey,
887            vote_pubkey,
888            &vote_state::create_v4_account_with_authorized(
889                &node_pubkey,
890                vote_pubkey,
891                [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
892                vote_pubkey,
893                0,
894                vote_pubkey,
895                0,
896                &node_pubkey,
897                1,
898            ),
899            rent,
900            lamports,
901        )
902    }
903
904    #[test]
905    fn test_stakes_basic() {
906        for i in 0..4 {
907            let stakes_cache = StakesCache::new(Stakes {
908                epoch: i,
909                ..Stakes::default()
910            });
911            let rent = Rent::default();
912
913            let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
914                create_staked_node_accounts(10, &rent);
915
916            stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
917            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
918            let stake_state: StakeStateV2 = stake_account.state().unwrap();
919            let stake = stake_state.stake().unwrap();
920            {
921                let stakes = stakes_cache.stakes();
922                let vote_accounts = stakes.vote_accounts();
923                assert!(vote_accounts.get(&vote_pubkey).is_some());
924                let expected_stake =
925                    effective_stake(&stake, i, &StakeHistory::default(), None, true);
926                assert_eq!(
927                    vote_accounts.get_delegated_stake(&vote_pubkey),
928                    expected_stake
929                );
930            }
931
932            stake_account.set_lamports(42);
933            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
934            {
935                let stakes = stakes_cache.stakes();
936                let vote_accounts = stakes.vote_accounts();
937                assert!(vote_accounts.get(&vote_pubkey).is_some());
938                let expected_stake =
939                    effective_stake(&stake, i, &StakeHistory::default(), None, true);
940                assert_eq!(
941                    vote_accounts.get_delegated_stake(&vote_pubkey),
942                    expected_stake
943                ); // stays old stake, because only 10 is activated
944            }
945
946            // activate more
947            let mut stake_account =
948                create_stake_account(42, &vote_pubkey, &solana_pubkey::new_rand(), &rent);
949            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
950            let stake_state: StakeStateV2 = stake_account.state().unwrap();
951            let stake = stake_state.stake().unwrap();
952            {
953                let stakes = stakes_cache.stakes();
954                let vote_accounts = stakes.vote_accounts();
955                assert!(vote_accounts.get(&vote_pubkey).is_some());
956                let expected_stake =
957                    effective_stake(&stake, i, &StakeHistory::default(), None, true);
958                assert_eq!(
959                    vote_accounts.get_delegated_stake(&vote_pubkey),
960                    expected_stake
961                ); // now stake of 42 is activated
962            }
963
964            stake_account.set_lamports(0);
965            stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
966            {
967                let stakes = stakes_cache.stakes();
968                let vote_accounts = stakes.vote_accounts();
969                assert!(vote_accounts.get(&vote_pubkey).is_some());
970                assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
971            }
972        }
973    }
974
975    #[test]
976    fn test_stakes_highest() {
977        let stakes_cache = StakesCache::default();
978        let rent = Rent::default();
979
980        assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
981
982        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
983            create_staked_node_accounts(10, &rent);
984
985        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
986        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
987
988        let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
989            create_staked_node_accounts(20, &rent);
990
991        stakes_cache.check_and_store(&vote11_pubkey, &vote11_account, None, true);
992        stakes_cache.check_and_store(&stake11_pubkey, &stake11_account, None, true);
993
994        let vote11_node_pubkey = VoteStateV4::deserialize(vote11_account.data(), &vote11_pubkey)
995            .unwrap()
996            .node_pubkey;
997
998        let highest_staked_node = stakes_cache.stakes().highest_staked_node();
999        assert_eq!(
1000            highest_staked_node,
1001            Some(SlotLeader {
1002                id: vote11_node_pubkey,
1003                vote_address: vote11_pubkey,
1004            })
1005        );
1006    }
1007
1008    #[test]
1009    fn test_stakes_vote_account_disappear_reappear() {
1010        let stakes_cache = StakesCache::new(Stakes {
1011            epoch: 4,
1012            ..Stakes::default()
1013        });
1014        let rent = Rent::default();
1015
1016        let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
1017            create_staked_node_accounts(10, &rent);
1018
1019        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1020        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
1021
1022        {
1023            let stakes = stakes_cache.stakes();
1024            let vote_accounts = stakes.vote_accounts();
1025            assert!(vote_accounts.get(&vote_pubkey).is_some());
1026            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1027        }
1028
1029        vote_account.set_lamports(0);
1030        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1031
1032        {
1033            let stakes = stakes_cache.stakes();
1034            let vote_accounts = stakes.vote_accounts();
1035            assert!(vote_accounts.get(&vote_pubkey).is_none());
1036            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1037        }
1038
1039        vote_account.set_lamports(1);
1040        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1041
1042        {
1043            let stakes = stakes_cache.stakes();
1044            let vote_accounts = stakes.vote_accounts();
1045            assert!(vote_accounts.get(&vote_pubkey).is_some());
1046            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1047        }
1048
1049        // Vote account too big
1050        let cache_data = vote_account.data().to_vec();
1051        let mut pushed = vote_account.data().to_vec();
1052        pushed.push(0);
1053        vote_account.set_data_from_slice(&pushed);
1054        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1055
1056        {
1057            let stakes = stakes_cache.stakes();
1058            let vote_accounts = stakes.vote_accounts();
1059            assert!(vote_accounts.get(&vote_pubkey).is_none());
1060            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1061        }
1062
1063        // Vote account uninitialized
1064        vote_account.set_data_from_slice(&vec![0; VoteStateV4::size_of()]);
1065        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1066
1067        {
1068            let stakes = stakes_cache.stakes();
1069            let vote_accounts = stakes.vote_accounts();
1070            assert!(vote_accounts.get(&vote_pubkey).is_none());
1071            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1072        }
1073
1074        vote_account.set_data_from_slice(&cache_data);
1075        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1076
1077        {
1078            let stakes = stakes_cache.stakes();
1079            let vote_accounts = stakes.vote_accounts();
1080            assert!(vote_accounts.get(&vote_pubkey).is_some());
1081            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1082        }
1083    }
1084
1085    #[test]
1086    fn test_stakes_change_delegate() {
1087        let stakes_cache = StakesCache::new(Stakes {
1088            epoch: 4,
1089            ..Stakes::default()
1090        });
1091        let rent = Rent::default();
1092
1093        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1094            create_staked_node_accounts(10, &rent);
1095
1096        let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
1097            create_staked_node_accounts(10, &rent);
1098
1099        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1100        stakes_cache.check_and_store(&vote_pubkey2, &vote_account2, None, true);
1101
1102        // delegates to vote_pubkey
1103        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
1104
1105        let stake_state: StakeStateV2 = stake_account.state().unwrap();
1106        let stake = stake_state.stake().unwrap();
1107
1108        {
1109            let stakes = stakes_cache.stakes();
1110            let vote_accounts = stakes.vote_accounts();
1111            assert!(vote_accounts.get(&vote_pubkey).is_some());
1112            let expected_stake =
1113                effective_stake(&stake, stakes.epoch, &stakes.stake_history, None, true);
1114            assert_eq!(
1115                vote_accounts.get_delegated_stake(&vote_pubkey),
1116                expected_stake
1117            );
1118            assert!(vote_accounts.get(&vote_pubkey2).is_some());
1119            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
1120        }
1121
1122        // delegates to vote_pubkey2
1123        stakes_cache.check_and_store(&stake_pubkey, &stake_account2, None, true);
1124
1125        {
1126            let stakes = stakes_cache.stakes();
1127            let vote_accounts = stakes.vote_accounts();
1128            assert!(vote_accounts.get(&vote_pubkey).is_some());
1129            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1130            assert!(vote_accounts.get(&vote_pubkey2).is_some());
1131            let expected_stake =
1132                effective_stake(&stake, stakes.epoch, &stakes.stake_history, None, true);
1133            assert_eq!(
1134                vote_accounts.get_delegated_stake(&vote_pubkey2),
1135                expected_stake
1136            );
1137        }
1138    }
1139    #[test]
1140    fn test_stakes_multiple_stakers() {
1141        let stakes_cache = StakesCache::new(Stakes {
1142            epoch: 4,
1143            ..Stakes::default()
1144        });
1145        let rent = Rent::default();
1146
1147        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1148            create_staked_node_accounts(10, &rent);
1149
1150        let stake_pubkey2 = solana_pubkey::new_rand();
1151        let stake_account2 = create_stake_account(10, &vote_pubkey, &stake_pubkey2, &rent);
1152
1153        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1154
1155        // delegates to vote_pubkey
1156        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
1157        stakes_cache.check_and_store(&stake_pubkey2, &stake_account2, None, true);
1158
1159        {
1160            let stakes = stakes_cache.stakes();
1161            let vote_accounts = stakes.vote_accounts();
1162            assert!(vote_accounts.get(&vote_pubkey).is_some());
1163            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
1164        }
1165    }
1166
1167    #[test]
1168    fn test_activate_epoch() {
1169        let stakes_cache = StakesCache::default();
1170        let rent = Rent::default();
1171
1172        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1173            create_staked_node_accounts(10, &rent);
1174
1175        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1176        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
1177        let stake_state: StakeStateV2 = stake_account.state().unwrap();
1178        let stake = stake_state.stake().unwrap();
1179
1180        let initial_expected_stake = {
1181            let stakes = stakes_cache.stakes();
1182            effective_stake(&stake, stakes.epoch, &stakes.stake_history, None, true)
1183        };
1184        {
1185            let stakes = stakes_cache.stakes();
1186            let vote_accounts = stakes.vote_accounts();
1187            assert_eq!(
1188                vote_accounts.get_delegated_stake(&vote_pubkey),
1189                initial_expected_stake
1190            );
1191        }
1192        let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1193        let next_epoch = 3;
1194        let (stake_history, vote_accounts, delegated_stakes, effective_delegated_stakes) = {
1195            let stakes = stakes_cache.stakes();
1196            let stake_delegations = stakes.stake_delegations_vec();
1197            stakes.calculate_activated_stake(
1198                next_epoch,
1199                &thread_pool,
1200                None,
1201                &stake_delegations,
1202                true,
1203            )
1204        };
1205        assert_eq!(
1206            effective_delegated_stakes
1207                .delegated_stakes
1208                .get(&vote_pubkey)
1209                .copied(),
1210            Some(initial_expected_stake)
1211        );
1212        stakes_cache.activate_epoch(next_epoch, stake_history, vote_accounts, delegated_stakes);
1213        {
1214            let stakes = stakes_cache.stakes();
1215            let vote_accounts = stakes.vote_accounts();
1216            let expected_stake =
1217                effective_stake(&stake, stakes.epoch, &stakes.stake_history, None, true);
1218            assert_eq!(
1219                vote_accounts.get_delegated_stake(&vote_pubkey),
1220                expected_stake
1221            );
1222        }
1223    }
1224
1225    #[test]
1226    fn test_stakes_not_delegate() {
1227        let stakes_cache = StakesCache::new(Stakes {
1228            epoch: 4,
1229            ..Stakes::default()
1230        });
1231        let rent = Rent::default();
1232
1233        let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1234            create_staked_node_accounts(10, &rent);
1235
1236        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None, true);
1237        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None, true);
1238
1239        {
1240            let stakes = stakes_cache.stakes();
1241            let vote_accounts = stakes.vote_accounts();
1242            assert!(vote_accounts.get(&vote_pubkey).is_some());
1243            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1244        }
1245
1246        // not a stake account, and whacks above entry
1247        stakes_cache.check_and_store(
1248            &stake_pubkey,
1249            &AccountSharedData::new(1, 0, &stake::program::id()),
1250            None,
1251            true,
1252        );
1253        {
1254            let stakes = stakes_cache.stakes();
1255            let vote_accounts = stakes.vote_accounts();
1256            assert!(vote_accounts.get(&vote_pubkey).is_some());
1257            assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1258        }
1259    }
1260}