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