1use {
4 crate::{stake_account, stake_history::StakeHistory},
5 im::HashMap as ImHashMap,
6 log::error,
7 num_derive::ToPrimitive,
8 rayon::{prelude::*, ThreadPool},
9 solana_account::{AccountSharedData, ReadableAccount},
10 solana_clock::Epoch,
11 solana_pubkey::Pubkey,
12 solana_stake_interface::state::{Delegation, StakeActivationStatus},
13 solana_stake_program::stake_state::Stake,
14 solana_vote::vote_account::{VoteAccount, VoteAccounts},
15 solana_vote_interface::state::VoteStateVersions,
16 std::{
17 collections::HashMap,
18 ops::Add,
19 sync::{Arc, RwLock, RwLockReadGuard},
20 },
21 thiserror::Error,
22};
23
24mod serde_stakes;
25pub(crate) use serde_stakes::serde_stakes_to_delegation_format;
26pub use serde_stakes::SerdeStakesToStakeFormat;
27
28#[derive(Debug, Error)]
29pub enum Error {
30 #[error("Invalid delegation: {0}")]
31 InvalidDelegation(Pubkey),
32 #[error(transparent)]
33 InvalidStakeAccount(#[from] stake_account::Error),
34 #[error("Stake account not found: {0}")]
35 StakeAccountNotFound(Pubkey),
36 #[error("Vote account mismatch: {0}")]
37 VoteAccountMismatch(Pubkey),
38 #[error("Vote account not cached: {0}")]
39 VoteAccountNotCached(Pubkey),
40 #[error("Vote account not found: {0}")]
41 VoteAccountNotFound(Pubkey),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)]
45pub enum InvalidCacheEntryReason {
46 Missing,
47 BadState,
48 WrongOwner,
49}
50
51type StakeAccount = stake_account::StakeAccount<Delegation>;
52
53#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
54#[derive(Default, Debug)]
55pub(crate) struct StakesCache(RwLock<Stakes<StakeAccount>>);
56
57impl StakesCache {
58 pub(crate) fn new(stakes: Stakes<StakeAccount>) -> Self {
59 Self(RwLock::new(stakes))
60 }
61
62 pub(crate) fn stakes(&self) -> RwLockReadGuard<Stakes<StakeAccount>> {
63 self.0.read().unwrap()
64 }
65
66 pub(crate) fn check_and_store(
67 &self,
68 pubkey: &Pubkey,
69 account: &impl ReadableAccount,
70 new_rate_activation_epoch: Option<Epoch>,
71 ) {
72 let owner = account.owner();
77 if account.lamports() == 0 {
80 if solana_vote_program::check_id(owner) {
81 let _old_vote_account = {
82 let mut stakes = self.0.write().unwrap();
83 stakes.remove_vote_account(pubkey)
84 };
85 } else if solana_stake_program::check_id(owner) {
86 let mut stakes = self.0.write().unwrap();
87 stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
88 }
89 return;
90 }
91 debug_assert_ne!(account.lamports(), 0u64);
92 if solana_vote_program::check_id(owner) {
93 if VoteStateVersions::is_correct_size_and_initialized(account.data()) {
94 match VoteAccount::try_from(account.to_account_shared_data()) {
95 Ok(vote_account) => {
96 let _old_vote_account = {
98 let mut stakes = self.0.write().unwrap();
99 stakes.upsert_vote_account(
100 pubkey,
101 vote_account,
102 new_rate_activation_epoch,
103 )
104 };
105 }
106 Err(_) => {
107 let _old_vote_account = {
109 let mut stakes = self.0.write().unwrap();
110 stakes.remove_vote_account(pubkey)
111 };
112 }
113 }
114 } else {
115 let _old_vote_account = {
117 let mut stakes = self.0.write().unwrap();
118 stakes.remove_vote_account(pubkey)
119 };
120 };
121 } else if solana_stake_program::check_id(owner) {
122 match StakeAccount::try_from(account.to_account_shared_data()) {
123 Ok(stake_account) => {
124 let mut stakes = self.0.write().unwrap();
125 stakes.upsert_stake_delegation(
126 *pubkey,
127 stake_account,
128 new_rate_activation_epoch,
129 );
130 }
131 Err(_) => {
132 let mut stakes = self.0.write().unwrap();
133 stakes.remove_stake_delegation(pubkey, new_rate_activation_epoch);
134 }
135 }
136 }
137 }
138
139 pub(crate) fn activate_epoch(
140 &self,
141 next_epoch: Epoch,
142 thread_pool: &ThreadPool,
143 new_rate_activation_epoch: Option<Epoch>,
144 ) {
145 let mut stakes = self.0.write().unwrap();
146 stakes.activate_epoch(next_epoch, thread_pool, new_rate_activation_epoch)
147 }
148}
149
150#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
158#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
159pub struct Stakes<T: Clone> {
160 vote_accounts: VoteAccounts,
162
163 stake_delegations: ImHashMap<Pubkey, T>,
165
166 unused: u64,
168
169 epoch: Epoch,
171
172 stake_history: StakeHistory,
174}
175
176#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
185#[derive(Debug, Clone)]
186pub enum StakesEnum {
187 Accounts(Stakes<StakeAccount>),
188 Delegations(Stakes<Delegation>),
189 Stakes(Stakes<Stake>),
190}
191
192impl<T: Clone> Stakes<T> {
193 pub fn vote_accounts(&self) -> &VoteAccounts {
194 &self.vote_accounts
195 }
196
197 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
198 self.vote_accounts.staked_nodes()
199 }
200}
201
202impl Stakes<StakeAccount> {
203 pub(crate) fn new<F>(stakes: &Stakes<Delegation>, get_account: F) -> Result<Self, Error>
208 where
209 F: Fn(&Pubkey) -> Option<AccountSharedData> + Sync,
210 {
211 let stake_delegations = stakes
212 .stake_delegations
213 .iter()
214 .collect::<Vec<_>>()
217 .into_par_iter()
218 .try_fold(ImHashMap::new, |mut map, (pubkey, delegation)| {
222 let Some(stake_account) = get_account(pubkey) else {
223 return Err(Error::StakeAccountNotFound(*pubkey));
224 };
225
226 let voter_pubkey = &delegation.voter_pubkey;
229 if stakes.vote_accounts.get(voter_pubkey).is_none() {
230 if let Some(account) = get_account(voter_pubkey) {
231 if VoteStateVersions::is_correct_size_and_initialized(account.data())
232 && VoteAccount::try_from(account.clone()).is_ok()
233 {
234 error!("vote account not cached: {voter_pubkey}, {account:?}");
235 return Err(Error::VoteAccountNotCached(*voter_pubkey));
236 }
237 }
238 }
239
240 let stake_account = StakeAccount::try_from(stake_account)?;
241 if stake_account.delegation() == delegation {
244 map.insert(*pubkey, stake_account);
245 Ok(map)
246 } else {
247 Err(Error::InvalidDelegation(*pubkey))
248 }
249 })
250 .try_reduce(ImHashMap::new, |a, b| Ok(a.union(b)))?;
251
252 for (pubkey, vote_account) in stakes.vote_accounts.iter() {
257 let Some(account) = get_account(pubkey) else {
258 return Err(Error::VoteAccountNotFound(*pubkey));
259 };
260 let vote_account = vote_account.account();
261 if vote_account != &account {
262 error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}");
263 return Err(Error::VoteAccountMismatch(*pubkey));
264 }
265 }
266
267 Ok(Self {
268 vote_accounts: stakes.vote_accounts.clone(),
269 stake_delegations,
270 unused: stakes.unused,
271 epoch: stakes.epoch,
272 stake_history: stakes.stake_history.clone(),
273 })
274 }
275
276 #[cfg(feature = "dev-context-only-utils")]
277 pub fn new_for_tests(
278 epoch: Epoch,
279 vote_accounts: VoteAccounts,
280 stake_delegations: ImHashMap<Pubkey, StakeAccount>,
281 ) -> Self {
282 Self {
283 vote_accounts,
284 stake_delegations,
285 unused: 0,
286 epoch,
287 stake_history: StakeHistory::default(),
288 }
289 }
290
291 pub(crate) fn history(&self) -> &StakeHistory {
292 &self.stake_history
293 }
294
295 fn activate_epoch(
296 &mut self,
297 next_epoch: Epoch,
298 thread_pool: &ThreadPool,
299 new_rate_activation_epoch: Option<Epoch>,
300 ) {
301 let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
302 let stake_history_entry = thread_pool.install(|| {
305 stake_delegations
306 .par_iter()
307 .fold(StakeActivationStatus::default, |acc, stake_account| {
308 let delegation = stake_account.delegation();
309 acc + delegation.stake_activating_and_deactivating(
310 self.epoch,
311 &self.stake_history,
312 new_rate_activation_epoch,
313 )
314 })
315 .reduce(StakeActivationStatus::default, Add::add)
316 });
317 self.stake_history.add(self.epoch, stake_history_entry);
318 self.epoch = next_epoch;
319 self.vote_accounts = refresh_vote_accounts(
322 thread_pool,
323 self.epoch,
324 &self.vote_accounts,
325 &stake_delegations,
326 &self.stake_history,
327 new_rate_activation_epoch,
328 );
329 }
330
331 fn calculate_stake(
333 stake_delegations: &ImHashMap<Pubkey, StakeAccount>,
334 voter_pubkey: &Pubkey,
335 epoch: Epoch,
336 stake_history: &StakeHistory,
337 new_rate_activation_epoch: Option<Epoch>,
338 ) -> u64 {
339 stake_delegations
340 .values()
341 .map(StakeAccount::delegation)
342 .filter(|delegation| &delegation.voter_pubkey == voter_pubkey)
343 .map(|delegation| delegation.stake(epoch, stake_history, new_rate_activation_epoch))
344 .sum()
345 }
346
347 fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) -> Option<VoteAccount> {
348 self.vote_accounts.remove(vote_pubkey).map(|(_, a)| a)
349 }
350
351 fn remove_stake_delegation(
352 &mut self,
353 stake_pubkey: &Pubkey,
354 new_rate_activation_epoch: Option<Epoch>,
355 ) {
356 if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
357 let removed_delegation = stake_account.delegation();
358 let removed_stake = removed_delegation.stake(
359 self.epoch,
360 &self.stake_history,
361 new_rate_activation_epoch,
362 );
363 self.vote_accounts
364 .sub_stake(&removed_delegation.voter_pubkey, removed_stake);
365 }
366 }
367
368 fn upsert_vote_account(
369 &mut self,
370 vote_pubkey: &Pubkey,
371 vote_account: VoteAccount,
372 new_rate_activation_epoch: Option<Epoch>,
373 ) -> Option<VoteAccount> {
374 debug_assert_ne!(vote_account.lamports(), 0u64);
375
376 let stake_delegations = &self.stake_delegations;
377 self.vote_accounts.insert(*vote_pubkey, vote_account, || {
378 Self::calculate_stake(
379 stake_delegations,
380 vote_pubkey,
381 self.epoch,
382 &self.stake_history,
383 new_rate_activation_epoch,
384 )
385 })
386 }
387
388 fn upsert_stake_delegation(
389 &mut self,
390 stake_pubkey: Pubkey,
391 stake_account: StakeAccount,
392 new_rate_activation_epoch: Option<Epoch>,
393 ) {
394 debug_assert_ne!(stake_account.lamports(), 0u64);
395 let delegation = stake_account.delegation();
396 let voter_pubkey = delegation.voter_pubkey;
397 let stake = delegation.stake(self.epoch, &self.stake_history, new_rate_activation_epoch);
398 match self.stake_delegations.insert(stake_pubkey, stake_account) {
399 None => self.vote_accounts.add_stake(&voter_pubkey, stake),
400 Some(old_stake_account) => {
401 let old_delegation = old_stake_account.delegation();
402 let old_voter_pubkey = old_delegation.voter_pubkey;
403 let old_stake = old_delegation.stake(
404 self.epoch,
405 &self.stake_history,
406 new_rate_activation_epoch,
407 );
408 if voter_pubkey != old_voter_pubkey || stake != old_stake {
409 self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
410 self.vote_accounts.add_stake(&voter_pubkey, stake);
411 }
412 }
413 }
414 }
415
416 pub(crate) fn stake_delegations(&self) -> &ImHashMap<Pubkey, StakeAccount> {
417 &self.stake_delegations
418 }
419
420 pub(crate) fn highest_staked_node(&self) -> Option<&Pubkey> {
421 let vote_account = self.vote_accounts.find_max_by_delegated_stake()?;
422 Some(vote_account.node_pubkey())
423 }
424}
425
426impl StakesEnum {
427 pub fn vote_accounts(&self) -> &VoteAccounts {
428 match self {
429 StakesEnum::Accounts(stakes) => stakes.vote_accounts(),
430 StakesEnum::Delegations(stakes) => stakes.vote_accounts(),
431 StakesEnum::Stakes(stakes) => stakes.vote_accounts(),
432 }
433 }
434
435 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
436 match self {
437 StakesEnum::Accounts(stakes) => stakes.staked_nodes(),
438 StakesEnum::Delegations(stakes) => stakes.staked_nodes(),
439 StakesEnum::Stakes(stakes) => stakes.staked_nodes(),
440 }
441 }
442}
443
444#[cfg(feature = "dev-context-only-utils")]
447impl From<Stakes<StakeAccount>> for Stakes<Delegation> {
448 fn from(stakes: Stakes<StakeAccount>) -> Self {
449 let stake_delegations = stakes
450 .stake_delegations
451 .into_iter()
452 .map(|(pubkey, stake_account)| (pubkey, *stake_account.delegation()))
453 .collect();
454 Self {
455 vote_accounts: stakes.vote_accounts,
456 stake_delegations,
457 unused: stakes.unused,
458 epoch: stakes.epoch,
459 stake_history: stakes.stake_history,
460 }
461 }
462}
463
464#[cfg(feature = "dev-context-only-utils")]
467impl From<Stakes<StakeAccount>> for Stakes<Stake> {
468 fn from(stakes: Stakes<StakeAccount>) -> Self {
469 let stake_delegations = stakes
470 .stake_delegations
471 .into_iter()
472 .map(|(pubkey, stake_account)| (pubkey, *stake_account.stake()))
473 .collect();
474 Self {
475 vote_accounts: stakes.vote_accounts,
476 stake_delegations,
477 unused: stakes.unused,
478 epoch: stakes.epoch,
479 stake_history: stakes.stake_history,
480 }
481 }
482}
483
484#[cfg(feature = "dev-context-only-utils")]
487impl From<Stakes<Stake>> for Stakes<Delegation> {
488 fn from(stakes: Stakes<Stake>) -> Self {
489 let stake_delegations = stakes
490 .stake_delegations
491 .into_iter()
492 .map(|(pubkey, stake)| (pubkey, stake.delegation))
493 .collect();
494 Self {
495 vote_accounts: stakes.vote_accounts,
496 stake_delegations,
497 unused: stakes.unused,
498 epoch: stakes.epoch,
499 stake_history: stakes.stake_history,
500 }
501 }
502}
503
504#[cfg(feature = "dev-context-only-utils")]
507impl From<StakesEnum> for Stakes<Delegation> {
508 fn from(stakes: StakesEnum) -> Self {
509 match stakes {
510 StakesEnum::Accounts(stakes) => stakes.into(),
511 StakesEnum::Delegations(stakes) => stakes,
512 StakesEnum::Stakes(stakes) => stakes.into(),
513 }
514 }
515}
516
517impl From<Stakes<StakeAccount>> for StakesEnum {
518 fn from(stakes: Stakes<StakeAccount>) -> Self {
519 Self::Accounts(stakes)
520 }
521}
522
523impl From<Stakes<Delegation>> for StakesEnum {
524 fn from(stakes: Stakes<Delegation>) -> Self {
525 Self::Delegations(stakes)
526 }
527}
528
529#[cfg(feature = "dev-context-only-utils")]
535impl PartialEq<StakesEnum> for StakesEnum {
536 fn eq(&self, other: &StakesEnum) -> bool {
537 match (self, other) {
538 (Self::Accounts(stakes), Self::Accounts(other)) => stakes == other,
539 (Self::Delegations(stakes), Self::Delegations(other)) => stakes == other,
540 (Self::Stakes(stakes), Self::Stakes(other)) => stakes == other,
541 (stakes, other) => {
542 let stakes = Stakes::<Delegation>::from(stakes.clone());
543 let other = Stakes::<Delegation>::from(other.clone());
544 stakes == other
545 }
546 }
547 }
548}
549
550fn refresh_vote_accounts(
551 thread_pool: &ThreadPool,
552 epoch: Epoch,
553 vote_accounts: &VoteAccounts,
554 stake_delegations: &[&StakeAccount],
555 stake_history: &StakeHistory,
556 new_rate_activation_epoch: Option<Epoch>,
557) -> VoteAccounts {
558 type StakesHashMap = HashMap<Pubkey, u64>;
559 fn merge(mut stakes: StakesHashMap, other: StakesHashMap) -> StakesHashMap {
560 if stakes.len() < other.len() {
561 return merge(other, stakes);
562 }
563 for (pubkey, stake) in other {
564 *stakes.entry(pubkey).or_default() += stake;
565 }
566 stakes
567 }
568 let delegated_stakes = thread_pool.install(|| {
569 stake_delegations
570 .par_iter()
571 .fold(HashMap::default, |mut delegated_stakes, stake_account| {
572 let delegation = stake_account.delegation();
573 let entry = delegated_stakes.entry(delegation.voter_pubkey).or_default();
574 *entry += delegation.stake(epoch, stake_history, new_rate_activation_epoch);
575 delegated_stakes
576 })
577 .reduce(HashMap::default, merge)
578 });
579 vote_accounts
580 .iter()
581 .map(|(&vote_pubkey, vote_account)| {
582 let delegated_stake = delegated_stakes
583 .get(&vote_pubkey)
584 .copied()
585 .unwrap_or_default();
586 (vote_pubkey, (delegated_stake, vote_account.clone()))
587 })
588 .collect()
589}
590
591#[cfg(test)]
592pub(crate) mod tests {
593 use {
594 super::*,
595 rayon::ThreadPoolBuilder,
596 solana_account::WritableAccount,
597 solana_pubkey::Pubkey,
598 solana_rent::Rent,
599 solana_stake_interface as stake,
600 solana_stake_program::stake_state,
601 solana_vote_interface::state::{VoteState, VoteStateVersions},
602 solana_vote_program::vote_state,
603 };
604
605 pub(crate) fn create_staked_node_accounts(
607 stake: u64,
608 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
609 let vote_pubkey = solana_pubkey::new_rand();
610 let vote_account =
611 vote_state::create_account(&vote_pubkey, &solana_pubkey::new_rand(), 0, 1);
612 let stake_pubkey = solana_pubkey::new_rand();
613 (
614 (vote_pubkey, vote_account),
615 (
616 stake_pubkey,
617 create_stake_account(stake, &vote_pubkey, &stake_pubkey),
618 ),
619 )
620 }
621
622 pub(crate) fn create_stake_account(
624 stake: u64,
625 vote_pubkey: &Pubkey,
626 stake_pubkey: &Pubkey,
627 ) -> AccountSharedData {
628 stake_state::create_account(
629 stake_pubkey,
630 vote_pubkey,
631 &vote_state::create_account(vote_pubkey, &solana_pubkey::new_rand(), 0, 1),
632 &Rent::free(),
633 stake,
634 )
635 }
636
637 #[test]
638 fn test_stakes_basic() {
639 for i in 0..4 {
640 let stakes_cache = StakesCache::new(Stakes {
641 epoch: i,
642 ..Stakes::default()
643 });
644
645 let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
646 create_staked_node_accounts(10);
647
648 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
649 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
650 let stake = stake_state::stake_from(&stake_account).unwrap();
651 {
652 let stakes = stakes_cache.stakes();
653 let vote_accounts = stakes.vote_accounts();
654 assert!(vote_accounts.get(&vote_pubkey).is_some());
655 assert_eq!(
656 vote_accounts.get_delegated_stake(&vote_pubkey),
657 stake.stake(i, &StakeHistory::default(), None)
658 );
659 }
660
661 stake_account.set_lamports(42);
662 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
663 {
664 let stakes = stakes_cache.stakes();
665 let vote_accounts = stakes.vote_accounts();
666 assert!(vote_accounts.get(&vote_pubkey).is_some());
667 assert_eq!(
668 vote_accounts.get_delegated_stake(&vote_pubkey),
669 stake.stake(i, &StakeHistory::default(), None)
670 ); }
672
673 let mut stake_account =
675 create_stake_account(42, &vote_pubkey, &solana_pubkey::new_rand());
676 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
677 let stake = stake_state::stake_from(&stake_account).unwrap();
678 {
679 let stakes = stakes_cache.stakes();
680 let vote_accounts = stakes.vote_accounts();
681 assert!(vote_accounts.get(&vote_pubkey).is_some());
682 assert_eq!(
683 vote_accounts.get_delegated_stake(&vote_pubkey),
684 stake.stake(i, &StakeHistory::default(), None)
685 ); }
687
688 stake_account.set_lamports(0);
689 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
690 {
691 let stakes = stakes_cache.stakes();
692 let vote_accounts = stakes.vote_accounts();
693 assert!(vote_accounts.get(&vote_pubkey).is_some());
694 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
695 }
696 }
697 }
698
699 #[test]
700 fn test_stakes_highest() {
701 let stakes_cache = StakesCache::default();
702
703 assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
704
705 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
706 create_staked_node_accounts(10);
707
708 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
709 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
710
711 let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
712 create_staked_node_accounts(20);
713
714 stakes_cache.check_and_store(&vote11_pubkey, &vote11_account, None);
715 stakes_cache.check_and_store(&stake11_pubkey, &stake11_account, None);
716
717 let vote11_node_pubkey = vote_state::from(&vote11_account).unwrap().node_pubkey;
718
719 let highest_staked_node = stakes_cache.stakes().highest_staked_node().copied();
720 assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
721 }
722
723 #[test]
724 fn test_stakes_vote_account_disappear_reappear() {
725 let stakes_cache = StakesCache::new(Stakes {
726 epoch: 4,
727 ..Stakes::default()
728 });
729
730 let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
731 create_staked_node_accounts(10);
732
733 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
734 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
735
736 {
737 let stakes = stakes_cache.stakes();
738 let vote_accounts = stakes.vote_accounts();
739 assert!(vote_accounts.get(&vote_pubkey).is_some());
740 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
741 }
742
743 vote_account.set_lamports(0);
744 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
745
746 {
747 let stakes = stakes_cache.stakes();
748 let vote_accounts = stakes.vote_accounts();
749 assert!(vote_accounts.get(&vote_pubkey).is_none());
750 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
751 }
752
753 vote_account.set_lamports(1);
754 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
755
756 {
757 let stakes = stakes_cache.stakes();
758 let vote_accounts = stakes.vote_accounts();
759 assert!(vote_accounts.get(&vote_pubkey).is_some());
760 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
761 }
762
763 let cache_data = vote_account.data().to_vec();
765 let mut pushed = vote_account.data().to_vec();
766 pushed.push(0);
767 vote_account.set_data(pushed);
768 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
769
770 {
771 let stakes = stakes_cache.stakes();
772 let vote_accounts = stakes.vote_accounts();
773 assert!(vote_accounts.get(&vote_pubkey).is_none());
774 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
775 }
776
777 let default_vote_state = VoteState::default();
779 let versioned = VoteStateVersions::new_current(default_vote_state);
780 vote_state::to(&versioned, &mut vote_account).unwrap();
781 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
782
783 {
784 let stakes = stakes_cache.stakes();
785 let vote_accounts = stakes.vote_accounts();
786 assert!(vote_accounts.get(&vote_pubkey).is_none());
787 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
788 }
789
790 vote_account.set_data(cache_data);
791 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
792
793 {
794 let stakes = stakes_cache.stakes();
795 let vote_accounts = stakes.vote_accounts();
796 assert!(vote_accounts.get(&vote_pubkey).is_some());
797 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
798 }
799 }
800
801 #[test]
802 fn test_stakes_change_delegate() {
803 let stakes_cache = StakesCache::new(Stakes {
804 epoch: 4,
805 ..Stakes::default()
806 });
807
808 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
809 create_staked_node_accounts(10);
810
811 let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
812 create_staked_node_accounts(10);
813
814 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
815 stakes_cache.check_and_store(&vote_pubkey2, &vote_account2, None);
816
817 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
819
820 let stake = stake_state::stake_from(&stake_account).unwrap();
821
822 {
823 let stakes = stakes_cache.stakes();
824 let vote_accounts = stakes.vote_accounts();
825 assert!(vote_accounts.get(&vote_pubkey).is_some());
826 assert_eq!(
827 vote_accounts.get_delegated_stake(&vote_pubkey),
828 stake.stake(stakes.epoch, &stakes.stake_history, None)
829 );
830 assert!(vote_accounts.get(&vote_pubkey2).is_some());
831 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
832 }
833
834 stakes_cache.check_and_store(&stake_pubkey, &stake_account2, None);
836
837 {
838 let stakes = stakes_cache.stakes();
839 let vote_accounts = stakes.vote_accounts();
840 assert!(vote_accounts.get(&vote_pubkey).is_some());
841 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
842 assert!(vote_accounts.get(&vote_pubkey2).is_some());
843 assert_eq!(
844 vote_accounts.get_delegated_stake(&vote_pubkey2),
845 stake.stake(stakes.epoch, &stakes.stake_history, None)
846 );
847 }
848 }
849 #[test]
850 fn test_stakes_multiple_stakers() {
851 let stakes_cache = StakesCache::new(Stakes {
852 epoch: 4,
853 ..Stakes::default()
854 });
855
856 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
857 create_staked_node_accounts(10);
858
859 let stake_pubkey2 = solana_pubkey::new_rand();
860 let stake_account2 = create_stake_account(10, &vote_pubkey, &stake_pubkey2);
861
862 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
863
864 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
866 stakes_cache.check_and_store(&stake_pubkey2, &stake_account2, None);
867
868 {
869 let stakes = stakes_cache.stakes();
870 let vote_accounts = stakes.vote_accounts();
871 assert!(vote_accounts.get(&vote_pubkey).is_some());
872 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
873 }
874 }
875
876 #[test]
877 fn test_activate_epoch() {
878 let stakes_cache = StakesCache::default();
879
880 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
881 create_staked_node_accounts(10);
882
883 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
884 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
885 let stake = stake_state::stake_from(&stake_account).unwrap();
886
887 {
888 let stakes = stakes_cache.stakes();
889 let vote_accounts = stakes.vote_accounts();
890 assert_eq!(
891 vote_accounts.get_delegated_stake(&vote_pubkey),
892 stake.stake(stakes.epoch, &stakes.stake_history, None)
893 );
894 }
895 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
896 stakes_cache.activate_epoch(3, &thread_pool, None);
897 {
898 let stakes = stakes_cache.stakes();
899 let vote_accounts = stakes.vote_accounts();
900 assert_eq!(
901 vote_accounts.get_delegated_stake(&vote_pubkey),
902 stake.stake(stakes.epoch, &stakes.stake_history, None)
903 );
904 }
905 }
906
907 #[test]
908 fn test_stakes_not_delegate() {
909 let stakes_cache = StakesCache::new(Stakes {
910 epoch: 4,
911 ..Stakes::default()
912 });
913
914 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
915 create_staked_node_accounts(10);
916
917 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
918 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
919
920 {
921 let stakes = stakes_cache.stakes();
922 let vote_accounts = stakes.vote_accounts();
923 assert!(vote_accounts.get(&vote_pubkey).is_some());
924 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
925 }
926
927 stakes_cache.check_and_store(
929 &stake_pubkey,
930 &AccountSharedData::new(1, 0, &stake::program::id()),
931 None,
932 );
933 {
934 let stakes = stakes_cache.stakes();
935 let vote_accounts = stakes.vote_accounts();
936 assert!(vote_accounts.get(&vote_pubkey).is_some());
937 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
938 }
939 }
940}