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