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