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};
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 let owner = account.owner();
98 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 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 let _old_vote_account = {
130 let mut stakes = self.0.write().unwrap();
131 stakes.remove_vote_account(pubkey)
132 };
133 }
134 }
135 } else {
136 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#[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: VoteAccounts,
209
210 #[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 #[cfg_attr(feature = "frozen-abi", stable_abi_sample(with = "Default::default()"))]
219 #[serde(skip)]
220 delegated_stakes: DelegatedStakes,
221
222 unused: u64,
224
225 epoch: Epoch,
227
228 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 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 #[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 .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 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 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 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 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 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 pub(crate) fn stake_delegations(&self) -> &ImblHashMap<Pubkey, StakeAccount> {
671 &self.stake_delegations
672 }
673
674 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#[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#[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#[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<Pubkey, u64>,
764 other: HashMap<Pubkey, u64>,
765) -> HashMap<Pubkey, 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 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 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 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 ); }
956
957 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 ); }
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 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.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 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 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 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 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}