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