1use {
4 crate::{stake_account, stake_history::StakeHistory},
5 dashmap::DashMap,
6 im::HashMap as ImHashMap,
7 log::error,
8 num_derive::ToPrimitive,
9 num_traits::ToPrimitive,
10 rayon::{prelude::*, ThreadPool},
11 solana_accounts_db::stake_rewards::StakeReward,
12 solana_sdk::{
13 account::{AccountSharedData, ReadableAccount},
14 clock::{Epoch, Slot},
15 pubkey::Pubkey,
16 stake::state::{Delegation, StakeActivationStatus},
17 vote::state::VoteStateVersions,
18 },
19 solana_stake_program::stake_state::Stake,
20 solana_vote::vote_account::{VoteAccount, VoteAccounts},
21 std::{
22 collections::HashMap,
23 ops::Add,
24 sync::{Arc, RwLock, RwLockReadGuard},
25 },
26 thiserror::Error,
27};
28
29mod serde_stakes;
30pub(crate) use serde_stakes::serde_stakes_to_delegation_format;
31pub use serde_stakes::SerdeStakesToStakeFormat;
32
33#[derive(Debug, Error)]
34pub enum Error {
35 #[error("Invalid delegation: {0}")]
36 InvalidDelegation(Pubkey),
37 #[error(transparent)]
38 InvalidStakeAccount(#[from] stake_account::Error),
39 #[error("Stake account not found: {0}")]
40 StakeAccountNotFound(Pubkey),
41 #[error("Vote account mismatch: {0}")]
42 VoteAccountMismatch(Pubkey),
43 #[error("Vote account not cached: {0}")]
44 VoteAccountNotCached(Pubkey),
45 #[error("Vote account not found: {0}")]
46 VoteAccountNotFound(Pubkey),
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)]
50pub enum InvalidCacheEntryReason {
51 Missing,
52 BadState,
53 WrongOwner,
54}
55
56type StakeAccount = stake_account::StakeAccount<Delegation>;
57
58#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
59#[derive(Default, Debug)]
60pub(crate) struct StakesCache(RwLock<Stakes<StakeAccount>>);
61
62impl StakesCache {
63 pub(crate) fn new(stakes: Stakes<StakeAccount>) -> Self {
64 Self(RwLock::new(stakes))
65 }
66
67 pub(crate) fn stakes(&self) -> RwLockReadGuard<Stakes<StakeAccount>> {
68 self.0.read().unwrap()
69 }
70
71 pub(crate) fn check_and_store(
72 &self,
73 pubkey: &Pubkey,
74 account: &impl ReadableAccount,
75 new_rate_activation_epoch: Option<Epoch>,
76 ) {
77 let owner = account.owner();
82 if account.lamports() == 0 {
85 if solana_vote_program::check_id(owner) {
86 let _old_vote_account = {
87 let mut stakes = self.0.write().unwrap();
88 stakes.remove_vote_account(pubkey)
89 };
90 } else if solana_stake_program::check_id(owner) {
91 let mut stakes = self.0.write().unwrap();
92 stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
93 }
94 return;
95 }
96 debug_assert_ne!(account.lamports(), 0u64);
97 if solana_vote_program::check_id(owner) {
98 if VoteStateVersions::is_correct_size_and_initialized(account.data()) {
99 match VoteAccount::try_from(account.to_account_shared_data()) {
100 Ok(vote_account) => {
101 let _old_vote_account = {
103 let mut stakes = self.0.write().unwrap();
104 stakes.upsert_vote_account(
105 pubkey,
106 vote_account,
107 new_rate_activation_epoch,
108 )
109 };
110 }
111 Err(_) => {
112 let _old_vote_account = {
114 let mut stakes = self.0.write().unwrap();
115 stakes.remove_vote_account(pubkey)
116 };
117 }
118 }
119 } else {
120 let _old_vote_account = {
122 let mut stakes = self.0.write().unwrap();
123 stakes.remove_vote_account(pubkey)
124 };
125 };
126 } else if solana_stake_program::check_id(owner) {
127 match StakeAccount::try_from(account.to_account_shared_data()) {
128 Ok(stake_account) => {
129 let mut stakes = self.0.write().unwrap();
130 stakes.upsert_stake_delegation(
131 *pubkey,
132 stake_account,
133 new_rate_activation_epoch,
134 );
135 }
136 Err(_) => {
137 let mut stakes = self.0.write().unwrap();
138 stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
139 }
140 }
141 }
142 }
143
144 pub(crate) fn activate_epoch(
145 &self,
146 next_epoch: Epoch,
147 thread_pool: &ThreadPool,
148 new_rate_activation_epoch: Option<Epoch>,
149 ) {
150 let mut stakes = self.0.write().unwrap();
151 stakes.activate_epoch(next_epoch, thread_pool, new_rate_activation_epoch)
152 }
153
154 pub(crate) fn update_stake_accounts(
155 &self,
156 thread_pool: &ThreadPool,
157 stake_rewards: &[StakeReward],
158 new_rate_activation_epoch: Option<Epoch>,
159 ) {
160 self.0.write().unwrap().update_stake_accounts(
161 thread_pool,
162 stake_rewards,
163 new_rate_activation_epoch,
164 )
165 }
166
167 pub(crate) fn handle_invalid_keys(
168 &self,
169 invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
170 current_slot: Slot,
171 ) {
172 if invalid_vote_keys.is_empty() {
173 return;
174 }
175
176 let mut stakes = self.0.write().unwrap();
179
180 for (vote_pubkey, reason) in invalid_vote_keys {
181 stakes.remove_vote_account(&vote_pubkey);
182 datapoint_warn!(
183 "bank-stake_delegation_accounts-invalid-account",
184 ("slot", current_slot as i64, i64),
185 ("vote-address", format!("{vote_pubkey:?}"), String),
186 ("reason", reason.to_i64().unwrap_or_default(), i64),
187 );
188 }
189 }
190}
191
192#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
200#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
201pub struct Stakes<T: Clone> {
202 vote_accounts: VoteAccounts,
204
205 stake_delegations: ImHashMap<Pubkey, T>,
207
208 unused: u64,
210
211 epoch: Epoch,
213
214 stake_history: StakeHistory,
216}
217
218#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
227#[derive(Debug, Clone)]
228pub enum StakesEnum {
229 Accounts(Stakes<StakeAccount>),
230 Delegations(Stakes<Delegation>),
231 Stakes(Stakes<Stake>),
232}
233
234impl<T: Clone> Stakes<T> {
235 pub fn vote_accounts(&self) -> &VoteAccounts {
236 &self.vote_accounts
237 }
238
239 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
240 self.vote_accounts.staked_nodes()
241 }
242}
243
244impl Stakes<StakeAccount> {
245 pub(crate) fn new<F>(stakes: &Stakes<Delegation>, get_account: F) -> Result<Self, Error>
250 where
251 F: Fn(&Pubkey) -> Option<AccountSharedData> + Sync,
252 {
253 let stake_delegations = stakes
254 .stake_delegations
255 .iter()
256 .collect::<Vec<_>>()
259 .into_par_iter()
260 .try_fold(ImHashMap::new, |mut map, (pubkey, delegation)| {
264 let Some(stake_account) = get_account(pubkey) else {
265 return Err(Error::StakeAccountNotFound(*pubkey));
266 };
267
268 let voter_pubkey = &delegation.voter_pubkey;
271 if stakes.vote_accounts.get(voter_pubkey).is_none() {
272 if let Some(account) = get_account(voter_pubkey) {
273 if VoteStateVersions::is_correct_size_and_initialized(account.data())
274 && VoteAccount::try_from(account.clone()).is_ok()
275 {
276 error!("vote account not cached: {voter_pubkey}, {account:?}");
277 return Err(Error::VoteAccountNotCached(*voter_pubkey));
278 }
279 }
280 }
281
282 let stake_account = StakeAccount::try_from(stake_account)?;
283 if stake_account.delegation() == delegation {
286 map.insert(*pubkey, stake_account);
287 Ok(map)
288 } else {
289 Err(Error::InvalidDelegation(*pubkey))
290 }
291 })
292 .try_reduce(ImHashMap::new, |a, b| Ok(a.union(b)))?;
293
294 for (pubkey, vote_account) in stakes.vote_accounts.iter() {
299 let Some(account) = get_account(pubkey) else {
300 return Err(Error::VoteAccountNotFound(*pubkey));
301 };
302 let vote_account = vote_account.account();
303 if vote_account != &account {
304 error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}");
305 return Err(Error::VoteAccountMismatch(*pubkey));
306 }
307 }
308
309 Ok(Self {
310 vote_accounts: stakes.vote_accounts.clone(),
311 stake_delegations,
312 unused: stakes.unused,
313 epoch: stakes.epoch,
314 stake_history: stakes.stake_history.clone(),
315 })
316 }
317
318 #[cfg(feature = "dev-context-only-utils")]
319 pub fn new_for_tests(
320 epoch: Epoch,
321 vote_accounts: VoteAccounts,
322 stake_delegations: ImHashMap<Pubkey, StakeAccount>,
323 ) -> Self {
324 Self {
325 vote_accounts,
326 stake_delegations,
327 unused: 0,
328 epoch,
329 stake_history: StakeHistory::default(),
330 }
331 }
332
333 pub(crate) fn history(&self) -> &StakeHistory {
334 &self.stake_history
335 }
336
337 fn activate_epoch(
338 &mut self,
339 next_epoch: Epoch,
340 thread_pool: &ThreadPool,
341 new_rate_activation_epoch: Option<Epoch>,
342 ) {
343 let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
344 let stake_history_entry = thread_pool.install(|| {
347 stake_delegations
348 .par_iter()
349 .fold(StakeActivationStatus::default, |acc, stake_account| {
350 let delegation = stake_account.delegation();
351 acc + delegation.stake_activating_and_deactivating(
352 self.epoch,
353 &self.stake_history,
354 new_rate_activation_epoch,
355 )
356 })
357 .reduce(StakeActivationStatus::default, Add::add)
358 });
359 self.stake_history.add(self.epoch, stake_history_entry);
360 self.epoch = next_epoch;
361 self.vote_accounts = refresh_vote_accounts(
364 thread_pool,
365 self.epoch,
366 &self.vote_accounts,
367 &stake_delegations,
368 &self.stake_history,
369 new_rate_activation_epoch,
370 );
371 }
372
373 fn calculate_stake(
375 stake_delegations: &ImHashMap<Pubkey, StakeAccount>,
376 voter_pubkey: &Pubkey,
377 epoch: Epoch,
378 stake_history: &StakeHistory,
379 new_rate_activation_epoch: Option<Epoch>,
380 ) -> u64 {
381 stake_delegations
382 .values()
383 .map(StakeAccount::delegation)
384 .filter(|delegation| &delegation.voter_pubkey == voter_pubkey)
385 .map(|delegation| delegation.stake(epoch, stake_history, new_rate_activation_epoch))
386 .sum()
387 }
388
389 pub(crate) fn vote_balance_and_staked(&self) -> u64 {
391 let get_stake = |stake_account: &StakeAccount| stake_account.delegation().stake;
392 let get_lamports = |(_, vote_account): (_, &VoteAccount)| vote_account.lamports();
393
394 self.stake_delegations.values().map(get_stake).sum::<u64>()
395 + self.vote_accounts.iter().map(get_lamports).sum::<u64>()
396 }
397
398 fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) -> Option<VoteAccount> {
399 self.vote_accounts.remove(vote_pubkey).map(|(_, a)| a)
400 }
401
402 fn remove_stake_delegation(
403 &mut self,
404 stake_pubkey: &Pubkey,
405 new_rate_activation_epoch: Option<Epoch>,
406 ) {
407 if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
408 let removed_delegation = stake_account.delegation();
409 let removed_stake = removed_delegation.stake(
410 self.epoch,
411 &self.stake_history,
412 new_rate_activation_epoch,
413 );
414 self.vote_accounts
415 .sub_stake(&removed_delegation.voter_pubkey, removed_stake);
416 }
417 }
418
419 fn upsert_vote_account(
420 &mut self,
421 vote_pubkey: &Pubkey,
422 vote_account: VoteAccount,
423 new_rate_activation_epoch: Option<Epoch>,
424 ) -> Option<VoteAccount> {
425 debug_assert_ne!(vote_account.lamports(), 0u64);
426
427 let stake_delegations = &self.stake_delegations;
428 self.vote_accounts.insert(*vote_pubkey, vote_account, || {
429 Self::calculate_stake(
430 stake_delegations,
431 vote_pubkey,
432 self.epoch,
433 &self.stake_history,
434 new_rate_activation_epoch,
435 )
436 })
437 }
438
439 fn upsert_stake_delegation(
440 &mut self,
441 stake_pubkey: Pubkey,
442 stake_account: StakeAccount,
443 new_rate_activation_epoch: Option<Epoch>,
444 ) {
445 debug_assert_ne!(stake_account.lamports(), 0u64);
446 let delegation = stake_account.delegation();
447 let voter_pubkey = delegation.voter_pubkey;
448 let stake = delegation.stake(self.epoch, &self.stake_history, new_rate_activation_epoch);
449 match self.stake_delegations.insert(stake_pubkey, stake_account) {
450 None => self.vote_accounts.add_stake(&voter_pubkey, stake),
451 Some(old_stake_account) => {
452 let old_delegation = old_stake_account.delegation();
453 let old_voter_pubkey = old_delegation.voter_pubkey;
454 let old_stake = old_delegation.stake(
455 self.epoch,
456 &self.stake_history,
457 new_rate_activation_epoch,
458 );
459 if voter_pubkey != old_voter_pubkey || stake != old_stake {
460 self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
461 self.vote_accounts.add_stake(&voter_pubkey, stake);
462 }
463 }
464 }
465 }
466
467 fn update_stake_accounts(
468 &mut self,
469 thread_pool: &ThreadPool,
470 stake_rewards: &[StakeReward],
471 new_rate_activation_epoch: Option<Epoch>,
472 ) {
473 let stake_delegations: Vec<_> = thread_pool.install(|| {
474 stake_rewards
475 .into_par_iter()
476 .filter_map(|stake_reward| {
477 let stake_account = StakeAccount::try_from(stake_reward.stake_account.clone());
478 Some((stake_reward.stake_pubkey, stake_account.ok()?))
479 })
480 .collect()
481 });
482 self.stake_delegations = std::mem::take(&mut self.stake_delegations)
483 .into_iter()
484 .chain(stake_delegations)
485 .collect::<HashMap<Pubkey, StakeAccount>>()
486 .into_iter()
487 .filter(|(_, account)| account.lamports() != 0u64)
488 .collect();
489 let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
490 self.vote_accounts = refresh_vote_accounts(
491 thread_pool,
492 self.epoch,
493 &self.vote_accounts,
494 &stake_delegations,
495 &self.stake_history,
496 new_rate_activation_epoch,
497 );
498 }
499
500 pub(crate) fn stake_delegations(&self) -> &ImHashMap<Pubkey, StakeAccount> {
501 &self.stake_delegations
502 }
503
504 pub(crate) fn highest_staked_node(&self) -> Option<&Pubkey> {
505 let vote_account = self.vote_accounts.find_max_by_delegated_stake()?;
506 Some(vote_account.node_pubkey())
507 }
508}
509
510impl StakesEnum {
511 pub fn vote_accounts(&self) -> &VoteAccounts {
512 match self {
513 StakesEnum::Accounts(stakes) => stakes.vote_accounts(),
514 StakesEnum::Delegations(stakes) => stakes.vote_accounts(),
515 StakesEnum::Stakes(stakes) => stakes.vote_accounts(),
516 }
517 }
518
519 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
520 match self {
521 StakesEnum::Accounts(stakes) => stakes.staked_nodes(),
522 StakesEnum::Delegations(stakes) => stakes.staked_nodes(),
523 StakesEnum::Stakes(stakes) => stakes.staked_nodes(),
524 }
525 }
526}
527
528#[cfg(feature = "dev-context-only-utils")]
531impl From<Stakes<StakeAccount>> for Stakes<Delegation> {
532 fn from(stakes: Stakes<StakeAccount>) -> Self {
533 let stake_delegations = stakes
534 .stake_delegations
535 .into_iter()
536 .map(|(pubkey, stake_account)| (pubkey, *stake_account.delegation()))
537 .collect();
538 Self {
539 vote_accounts: stakes.vote_accounts,
540 stake_delegations,
541 unused: stakes.unused,
542 epoch: stakes.epoch,
543 stake_history: stakes.stake_history,
544 }
545 }
546}
547
548#[cfg(feature = "dev-context-only-utils")]
551impl From<Stakes<StakeAccount>> for Stakes<Stake> {
552 fn from(stakes: Stakes<StakeAccount>) -> Self {
553 let stake_delegations = stakes
554 .stake_delegations
555 .into_iter()
556 .map(|(pubkey, stake_account)| (pubkey, *stake_account.stake()))
557 .collect();
558 Self {
559 vote_accounts: stakes.vote_accounts,
560 stake_delegations,
561 unused: stakes.unused,
562 epoch: stakes.epoch,
563 stake_history: stakes.stake_history,
564 }
565 }
566}
567
568#[cfg(feature = "dev-context-only-utils")]
571impl From<Stakes<Stake>> for Stakes<Delegation> {
572 fn from(stakes: Stakes<Stake>) -> Self {
573 let stake_delegations = stakes
574 .stake_delegations
575 .into_iter()
576 .map(|(pubkey, stake)| (pubkey, stake.delegation))
577 .collect();
578 Self {
579 vote_accounts: stakes.vote_accounts,
580 stake_delegations,
581 unused: stakes.unused,
582 epoch: stakes.epoch,
583 stake_history: stakes.stake_history,
584 }
585 }
586}
587
588#[cfg(feature = "dev-context-only-utils")]
591impl From<StakesEnum> for Stakes<Delegation> {
592 fn from(stakes: StakesEnum) -> Self {
593 match stakes {
594 StakesEnum::Accounts(stakes) => stakes.into(),
595 StakesEnum::Delegations(stakes) => stakes,
596 StakesEnum::Stakes(stakes) => stakes.into(),
597 }
598 }
599}
600
601impl From<Stakes<StakeAccount>> for StakesEnum {
602 fn from(stakes: Stakes<StakeAccount>) -> Self {
603 Self::Accounts(stakes)
604 }
605}
606
607impl From<Stakes<Delegation>> for StakesEnum {
608 fn from(stakes: Stakes<Delegation>) -> Self {
609 Self::Delegations(stakes)
610 }
611}
612
613#[cfg(feature = "dev-context-only-utils")]
619impl PartialEq<StakesEnum> for StakesEnum {
620 fn eq(&self, other: &StakesEnum) -> bool {
621 match (self, other) {
622 (Self::Accounts(stakes), Self::Accounts(other)) => stakes == other,
623 (Self::Delegations(stakes), Self::Delegations(other)) => stakes == other,
624 (Self::Stakes(stakes), Self::Stakes(other)) => stakes == other,
625 (stakes, other) => {
626 let stakes = Stakes::<Delegation>::from(stakes.clone());
627 let other = Stakes::<Delegation>::from(other.clone());
628 stakes == other
629 }
630 }
631 }
632}
633
634fn refresh_vote_accounts(
635 thread_pool: &ThreadPool,
636 epoch: Epoch,
637 vote_accounts: &VoteAccounts,
638 stake_delegations: &[&StakeAccount],
639 stake_history: &StakeHistory,
640 new_rate_activation_epoch: Option<Epoch>,
641) -> VoteAccounts {
642 type StakesHashMap = HashMap<Pubkey, u64>;
643 fn merge(mut stakes: StakesHashMap, other: StakesHashMap) -> StakesHashMap {
644 if stakes.len() < other.len() {
645 return merge(other, stakes);
646 }
647 for (pubkey, stake) in other {
648 *stakes.entry(pubkey).or_default() += stake;
649 }
650 stakes
651 }
652 let delegated_stakes = thread_pool.install(|| {
653 stake_delegations
654 .par_iter()
655 .fold(HashMap::default, |mut delegated_stakes, stake_account| {
656 let delegation = stake_account.delegation();
657 let entry = delegated_stakes.entry(delegation.voter_pubkey).or_default();
658 *entry += delegation.stake(epoch, stake_history, new_rate_activation_epoch);
659 delegated_stakes
660 })
661 .reduce(HashMap::default, merge)
662 });
663 vote_accounts
664 .iter()
665 .map(|(&vote_pubkey, vote_account)| {
666 let delegated_stake = delegated_stakes
667 .get(&vote_pubkey)
668 .copied()
669 .unwrap_or_default();
670 (vote_pubkey, (delegated_stake, vote_account.clone()))
671 })
672 .collect()
673}
674
675#[cfg(test)]
676pub(crate) mod tests {
677 use {
678 super::*,
679 rayon::ThreadPoolBuilder,
680 solana_sdk::{account::WritableAccount, pubkey::Pubkey, rent::Rent, stake},
681 solana_stake_program::stake_state,
682 solana_vote_program::vote_state::{self, VoteState, VoteStateVersions},
683 };
684
685 pub(crate) fn create_staked_node_accounts(
687 stake: u64,
688 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
689 let vote_pubkey = solana_sdk::pubkey::new_rand();
690 let vote_account =
691 vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
692 let stake_pubkey = solana_sdk::pubkey::new_rand();
693 (
694 (vote_pubkey, vote_account),
695 (
696 stake_pubkey,
697 create_stake_account(stake, &vote_pubkey, &stake_pubkey),
698 ),
699 )
700 }
701
702 pub(crate) fn create_stake_account(
704 stake: u64,
705 vote_pubkey: &Pubkey,
706 stake_pubkey: &Pubkey,
707 ) -> AccountSharedData {
708 stake_state::create_account(
709 stake_pubkey,
710 vote_pubkey,
711 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
712 &Rent::free(),
713 stake,
714 )
715 }
716
717 fn create_warming_staked_node_accounts(
718 stake: u64,
719 epoch: Epoch,
720 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
721 let vote_pubkey = solana_sdk::pubkey::new_rand();
722 let vote_account =
723 vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
724 (
725 (vote_pubkey, vote_account),
726 create_warming_stake_account(stake, epoch, &vote_pubkey),
727 )
728 }
729
730 fn create_warming_stake_account(
732 stake: u64,
733 epoch: Epoch,
734 vote_pubkey: &Pubkey,
735 ) -> (Pubkey, AccountSharedData) {
736 let stake_pubkey = solana_sdk::pubkey::new_rand();
737 (
738 stake_pubkey,
739 stake_state::create_account_with_activation_epoch(
740 &stake_pubkey,
741 vote_pubkey,
742 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
743 &Rent::free(),
744 stake,
745 epoch,
746 ),
747 )
748 }
749
750 #[test]
751 fn test_stakes_basic() {
752 for i in 0..4 {
753 let stakes_cache = StakesCache::new(Stakes {
754 epoch: i,
755 ..Stakes::default()
756 });
757
758 let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
759 create_staked_node_accounts(10);
760
761 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
762 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
763 let stake = stake_state::stake_from(&stake_account).unwrap();
764 {
765 let stakes = stakes_cache.stakes();
766 let vote_accounts = stakes.vote_accounts();
767 assert!(vote_accounts.get(&vote_pubkey).is_some());
768 assert_eq!(
769 vote_accounts.get_delegated_stake(&vote_pubkey),
770 stake.stake(i, &StakeHistory::default(), None)
771 );
772 }
773
774 stake_account.set_lamports(42);
775 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
776 {
777 let stakes = stakes_cache.stakes();
778 let vote_accounts = stakes.vote_accounts();
779 assert!(vote_accounts.get(&vote_pubkey).is_some());
780 assert_eq!(
781 vote_accounts.get_delegated_stake(&vote_pubkey),
782 stake.stake(i, &StakeHistory::default(), None)
783 ); }
785
786 let mut stake_account =
788 create_stake_account(42, &vote_pubkey, &solana_sdk::pubkey::new_rand());
789 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
790 let stake = stake_state::stake_from(&stake_account).unwrap();
791 {
792 let stakes = stakes_cache.stakes();
793 let vote_accounts = stakes.vote_accounts();
794 assert!(vote_accounts.get(&vote_pubkey).is_some());
795 assert_eq!(
796 vote_accounts.get_delegated_stake(&vote_pubkey),
797 stake.stake(i, &StakeHistory::default(), None)
798 ); }
800
801 stake_account.set_lamports(0);
802 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
803 {
804 let stakes = stakes_cache.stakes();
805 let vote_accounts = stakes.vote_accounts();
806 assert!(vote_accounts.get(&vote_pubkey).is_some());
807 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
808 }
809 }
810 }
811
812 #[test]
813 fn test_stakes_highest() {
814 let stakes_cache = StakesCache::default();
815
816 assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
817
818 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
819 create_staked_node_accounts(10);
820
821 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
822 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
823
824 let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
825 create_staked_node_accounts(20);
826
827 stakes_cache.check_and_store(&vote11_pubkey, &vote11_account, None);
828 stakes_cache.check_and_store(&stake11_pubkey, &stake11_account, None);
829
830 let vote11_node_pubkey = vote_state::from(&vote11_account).unwrap().node_pubkey;
831
832 let highest_staked_node = stakes_cache.stakes().highest_staked_node().copied();
833 assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
834 }
835
836 #[test]
837 fn test_stakes_vote_account_disappear_reappear() {
838 let stakes_cache = StakesCache::new(Stakes {
839 epoch: 4,
840 ..Stakes::default()
841 });
842
843 let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
844 create_staked_node_accounts(10);
845
846 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
847 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
848
849 {
850 let stakes = stakes_cache.stakes();
851 let vote_accounts = stakes.vote_accounts();
852 assert!(vote_accounts.get(&vote_pubkey).is_some());
853 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
854 }
855
856 vote_account.set_lamports(0);
857 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
858
859 {
860 let stakes = stakes_cache.stakes();
861 let vote_accounts = stakes.vote_accounts();
862 assert!(vote_accounts.get(&vote_pubkey).is_none());
863 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
864 }
865
866 vote_account.set_lamports(1);
867 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
868
869 {
870 let stakes = stakes_cache.stakes();
871 let vote_accounts = stakes.vote_accounts();
872 assert!(vote_accounts.get(&vote_pubkey).is_some());
873 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
874 }
875
876 let cache_data = vote_account.data().to_vec();
878 let mut pushed = vote_account.data().to_vec();
879 pushed.push(0);
880 vote_account.set_data(pushed);
881 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
882
883 {
884 let stakes = stakes_cache.stakes();
885 let vote_accounts = stakes.vote_accounts();
886 assert!(vote_accounts.get(&vote_pubkey).is_none());
887 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
888 }
889
890 let default_vote_state = VoteState::default();
892 let versioned = VoteStateVersions::new_current(default_vote_state);
893 vote_state::to(&versioned, &mut vote_account).unwrap();
894 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
895
896 {
897 let stakes = stakes_cache.stakes();
898 let vote_accounts = stakes.vote_accounts();
899 assert!(vote_accounts.get(&vote_pubkey).is_none());
900 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
901 }
902
903 vote_account.set_data(cache_data);
904 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
905
906 {
907 let stakes = stakes_cache.stakes();
908 let vote_accounts = stakes.vote_accounts();
909 assert!(vote_accounts.get(&vote_pubkey).is_some());
910 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
911 }
912 }
913
914 #[test]
915 fn test_stakes_change_delegate() {
916 let stakes_cache = StakesCache::new(Stakes {
917 epoch: 4,
918 ..Stakes::default()
919 });
920
921 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
922 create_staked_node_accounts(10);
923
924 let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
925 create_staked_node_accounts(10);
926
927 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
928 stakes_cache.check_and_store(&vote_pubkey2, &vote_account2, None);
929
930 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
932
933 let stake = stake_state::stake_from(&stake_account).unwrap();
934
935 {
936 let stakes = stakes_cache.stakes();
937 let vote_accounts = stakes.vote_accounts();
938 assert!(vote_accounts.get(&vote_pubkey).is_some());
939 assert_eq!(
940 vote_accounts.get_delegated_stake(&vote_pubkey),
941 stake.stake(stakes.epoch, &stakes.stake_history, None)
942 );
943 assert!(vote_accounts.get(&vote_pubkey2).is_some());
944 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
945 }
946
947 stakes_cache.check_and_store(&stake_pubkey, &stake_account2, None);
949
950 {
951 let stakes = stakes_cache.stakes();
952 let vote_accounts = stakes.vote_accounts();
953 assert!(vote_accounts.get(&vote_pubkey).is_some());
954 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
955 assert!(vote_accounts.get(&vote_pubkey2).is_some());
956 assert_eq!(
957 vote_accounts.get_delegated_stake(&vote_pubkey2),
958 stake.stake(stakes.epoch, &stakes.stake_history, None)
959 );
960 }
961 }
962 #[test]
963 fn test_stakes_multiple_stakers() {
964 let stakes_cache = StakesCache::new(Stakes {
965 epoch: 4,
966 ..Stakes::default()
967 });
968
969 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
970 create_staked_node_accounts(10);
971
972 let stake_pubkey2 = solana_sdk::pubkey::new_rand();
973 let stake_account2 = create_stake_account(10, &vote_pubkey, &stake_pubkey2);
974
975 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
976
977 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
979 stakes_cache.check_and_store(&stake_pubkey2, &stake_account2, None);
980
981 {
982 let stakes = stakes_cache.stakes();
983 let vote_accounts = stakes.vote_accounts();
984 assert!(vote_accounts.get(&vote_pubkey).is_some());
985 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
986 }
987 }
988
989 #[test]
990 fn test_activate_epoch() {
991 let stakes_cache = StakesCache::default();
992
993 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
994 create_staked_node_accounts(10);
995
996 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
997 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
998 let stake = stake_state::stake_from(&stake_account).unwrap();
999
1000 {
1001 let stakes = stakes_cache.stakes();
1002 let vote_accounts = stakes.vote_accounts();
1003 assert_eq!(
1004 vote_accounts.get_delegated_stake(&vote_pubkey),
1005 stake.stake(stakes.epoch, &stakes.stake_history, None)
1006 );
1007 }
1008 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1009 stakes_cache.activate_epoch(3, &thread_pool, None);
1010 {
1011 let stakes = stakes_cache.stakes();
1012 let vote_accounts = stakes.vote_accounts();
1013 assert_eq!(
1014 vote_accounts.get_delegated_stake(&vote_pubkey),
1015 stake.stake(stakes.epoch, &stakes.stake_history, None)
1016 );
1017 }
1018 }
1019
1020 #[test]
1021 fn test_stakes_not_delegate() {
1022 let stakes_cache = StakesCache::new(Stakes {
1023 epoch: 4,
1024 ..Stakes::default()
1025 });
1026
1027 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1028 create_staked_node_accounts(10);
1029
1030 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
1031 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
1032
1033 {
1034 let stakes = stakes_cache.stakes();
1035 let vote_accounts = stakes.vote_accounts();
1036 assert!(vote_accounts.get(&vote_pubkey).is_some());
1037 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
1038 }
1039
1040 stakes_cache.check_and_store(
1042 &stake_pubkey,
1043 &AccountSharedData::new(1, 0, &stake::program::id()),
1044 None,
1045 );
1046 {
1047 let stakes = stakes_cache.stakes();
1048 let vote_accounts = stakes.vote_accounts();
1049 assert!(vote_accounts.get(&vote_pubkey).is_some());
1050 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
1051 }
1052 }
1053
1054 #[test]
1055 fn test_vote_balance_and_staked_empty() {
1056 let stakes = Stakes::<StakeAccount>::default();
1057 assert_eq!(stakes.vote_balance_and_staked(), 0);
1058 }
1059
1060 #[test]
1061 fn test_vote_balance_and_staked_normal() {
1062 let stakes_cache = StakesCache::default();
1063 #[allow(non_local_definitions)]
1064 impl Stakes<StakeAccount> {
1065 fn vote_balance_and_warmed_staked(&self) -> u64 {
1066 let vote_balance: u64 = self
1067 .vote_accounts
1068 .iter()
1069 .map(|(_pubkey, account)| account.lamports())
1070 .sum();
1071 let warmed_stake: u64 = self
1072 .vote_accounts
1073 .delegated_stakes()
1074 .map(|(_pubkey, stake)| stake)
1075 .sum();
1076 vote_balance + warmed_stake
1077 }
1078 }
1079
1080 let genesis_epoch = 0;
1081 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
1082 create_warming_staked_node_accounts(10, genesis_epoch);
1083 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
1084 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
1085
1086 {
1087 let stakes = stakes_cache.stakes();
1088 assert_eq!(stakes.vote_balance_and_staked(), 11);
1089 assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
1090 }
1091
1092 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1093 for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
1094 stakes_cache.activate_epoch(epoch, &thread_pool, None);
1095 let stakes = stakes_cache.stakes();
1098 assert_eq!(stakes.vote_balance_and_staked(), 11);
1099 assert_eq!(
1100 stakes.vote_balance_and_warmed_staked(),
1101 *expected_warmed_stake
1102 );
1103 }
1104 }
1105}