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