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
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 let owner = account.owner();
99 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 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 let _old_vote_account = {
131 let mut stakes = self.0.write().unwrap();
132 stakes.remove_vote_account(pubkey)
133 };
134 }
135 }
136 } else {
137 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#[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: VoteAccounts,
210
211 #[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 #[cfg_attr(feature = "frozen-abi", stable_abi_sample(with = "Default::default()"))]
221 #[serde(skip)]
222 #[wincode(skip)]
223 delegated_stakes: DelegatedStakes,
224
225 unused: u64,
227
228 epoch: Epoch,
230
231 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 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 #[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 .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 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 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 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 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 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 pub(crate) fn stake_delegations(&self) -> &ImblHashMap<Pubkey, StakeAccount> {
674 &self.stake_delegations
675 }
676
677 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#[cfg(feature = "dev-context-only-utils")]
704macro_rules! impl_stake_format_conversion {
705 ($from:ty, $to:ty, |$binding:ident| $expr:expr) => {
706 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<Pubkey, u64>,
745 other: HashMap<Pubkey, u64>,
746) -> HashMap<Pubkey, 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 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 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 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 ); }
937
938 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 ); }
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 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.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 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 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 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 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}