1use {
4 crate::{
5 stake_account,
6 stake_history::StakeHistory,
7 vote_account::{VoteAccount, VoteAccounts},
8 },
9 dashmap::DashMap,
10 im::HashMap as ImHashMap,
11 log::error,
12 num_derive::ToPrimitive,
13 num_traits::ToPrimitive,
14 rayon::{prelude::*, ThreadPool},
15 solana_sdk::{
16 account::{AccountSharedData, ReadableAccount},
17 clock::{Epoch, Slot},
18 pubkey::Pubkey,
19 stake::state::{Delegation, StakeActivationStatus},
20 },
21 solana_vote_program::vote_state::VoteState,
22 std::{
23 collections::{HashMap, HashSet},
24 ops::Add,
25 sync::{Arc, RwLock, RwLockReadGuard},
26 },
27 thiserror::Error,
28};
29
30#[derive(Debug, Error)]
31pub enum Error {
32 #[error("Invalid delegation: {0}")]
33 InvalidDelegation(Pubkey),
34 #[error(transparent)]
35 InvalidStakeAccount(#[from] stake_account::Error),
36 #[error("Stake account not found: {0}")]
37 StakeAccountNotFound(Pubkey),
38 #[error("Vote account mismatch: {0}")]
39 VoteAccountMismatch(Pubkey),
40 #[error("Vote account not cached: {0}")]
41 VoteAccountNotCached(Pubkey),
42 #[error("Vote account not found: {0}")]
43 VoteAccountNotFound(Pubkey),
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)]
47pub enum InvalidCacheEntryReason {
48 Missing,
49 BadState,
50 WrongOwner,
51}
52
53type StakeAccount = stake_account::StakeAccount<Delegation>;
54
55#[derive(Default, Debug, AbiExample)]
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(&self, pubkey: &Pubkey, account: &impl ReadableAccount) {
68 let owner = account.owner();
73 if account.lamports() == 0 {
76 if solana_vote_program::check_id(owner) {
77 let mut stakes = self.0.write().unwrap();
78 stakes.remove_vote_account(pubkey);
79 } else if solana_stake_program::check_id(owner) {
80 let mut stakes = self.0.write().unwrap();
81 stakes.remove_stake_delegation(pubkey);
82 }
83 return;
84 }
85 debug_assert_ne!(account.lamports(), 0u64);
86 if solana_vote_program::check_id(owner) {
87 if VoteState::is_correct_size_and_initialized(account.data()) {
88 match VoteAccount::try_from(account.to_account_shared_data()) {
89 Ok(vote_account) => {
90 {
91 let _res = vote_account.vote_state();
93 }
94 let mut stakes = self.0.write().unwrap();
95 stakes.upsert_vote_account(pubkey, vote_account);
96 }
97 Err(_) => {
98 let mut stakes = self.0.write().unwrap();
99 stakes.remove_vote_account(pubkey)
100 }
101 }
102 } else {
103 let mut stakes = self.0.write().unwrap();
104 stakes.remove_vote_account(pubkey)
105 };
106 } else if solana_stake_program::check_id(owner) {
107 match StakeAccount::try_from(account.to_account_shared_data()) {
108 Ok(stake_account) => {
109 let mut stakes = self.0.write().unwrap();
110 stakes.upsert_stake_delegation(*pubkey, stake_account);
111 }
112 Err(_) => {
113 let mut stakes = self.0.write().unwrap();
114 stakes.remove_stake_delegation(pubkey);
115 }
116 }
117 }
118 }
119
120 pub(crate) fn activate_epoch(&self, next_epoch: Epoch, thread_pool: &ThreadPool) {
121 let mut stakes = self.0.write().unwrap();
122 stakes.activate_epoch(next_epoch, thread_pool)
123 }
124
125 pub(crate) fn handle_invalid_keys(
126 &self,
127 invalid_stake_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
128 invalid_vote_keys: DashMap<Pubkey, InvalidCacheEntryReason>,
129 current_slot: Slot,
130 ) {
131 if invalid_stake_keys.is_empty() && invalid_vote_keys.is_empty() {
132 return;
133 }
134
135 let mut stakes = self.0.write().unwrap();
138
139 for (stake_pubkey, reason) in invalid_stake_keys {
140 stakes.remove_stake_delegation(&stake_pubkey);
141 datapoint_warn!(
142 "bank-stake_delegation_accounts-invalid-account",
143 ("slot", current_slot as i64, i64),
144 ("stake-address", format!("{:?}", stake_pubkey), String),
145 ("reason", reason.to_i64().unwrap_or_default(), i64),
146 );
147 }
148
149 for (vote_pubkey, reason) in invalid_vote_keys {
150 stakes.remove_vote_account(&vote_pubkey);
151 datapoint_warn!(
152 "bank-stake_delegation_accounts-invalid-account",
153 ("slot", current_slot as i64, i64),
154 ("vote-address", format!("{:?}", vote_pubkey), String),
155 ("reason", reason.to_i64().unwrap_or_default(), i64),
156 );
157 }
158 }
159}
160
161#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)]
169pub struct Stakes<T: Clone> {
170 vote_accounts: VoteAccounts,
172
173 stake_delegations: ImHashMap<Pubkey, T>,
175
176 unused: u64,
178
179 epoch: Epoch,
181
182 stake_history: StakeHistory,
184}
185
186#[derive(Debug, AbiExample)]
194pub enum StakesEnum {
195 Accounts(Stakes<StakeAccount>),
196 Delegations(Stakes<Delegation>),
197}
198
199impl<T: Clone> Stakes<T> {
200 pub fn vote_accounts(&self) -> &VoteAccounts {
201 &self.vote_accounts
202 }
203
204 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
205 self.vote_accounts.staked_nodes()
206 }
207}
208
209impl Stakes<StakeAccount> {
210 pub(crate) fn new<F>(stakes: &Stakes<Delegation>, get_account: F) -> Result<Self, Error>
215 where
216 F: Fn(&Pubkey) -> Option<AccountSharedData>,
217 {
218 let stake_delegations = stakes.stake_delegations.iter().map(|(pubkey, delegation)| {
219 let stake_account = match get_account(pubkey) {
220 None => return Err(Error::StakeAccountNotFound(*pubkey)),
221 Some(account) => account,
222 };
223 let stake_account = StakeAccount::try_from(stake_account)?;
224 if stake_account.delegation() == *delegation {
227 Ok((*pubkey, stake_account))
228 } else {
229 Err(Error::InvalidDelegation(*pubkey))
230 }
231 });
232 for (pubkey, vote_account) in stakes.vote_accounts.iter() {
234 let account = match get_account(pubkey) {
235 None => return Err(Error::VoteAccountNotFound(*pubkey)),
236 Some(account) => account,
237 };
238 let vote_account = vote_account.account();
239 if vote_account != &account {
240 error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}");
241 return Err(Error::VoteAccountMismatch(*pubkey));
242 }
243 }
244 let voter_pubkeys: HashSet<Pubkey> = stakes
247 .stake_delegations
248 .values()
249 .map(|delegation| delegation.voter_pubkey)
250 .filter(|voter_pubkey| stakes.vote_accounts.get(voter_pubkey).is_none())
251 .collect();
252 for pubkey in voter_pubkeys {
253 let account = match get_account(&pubkey) {
254 None => continue,
255 Some(account) => account,
256 };
257 if VoteState::is_correct_size_and_initialized(account.data())
258 && VoteAccount::try_from(account.clone()).is_ok()
259 {
260 error!("vote account not cached: {pubkey}, {account:?}");
261 return Err(Error::VoteAccountNotCached(pubkey));
262 }
263 }
264 Ok(Self {
265 vote_accounts: stakes.vote_accounts.clone(),
266 stake_delegations: stake_delegations.collect::<Result<_, _>>()?,
267 unused: stakes.unused,
268 epoch: stakes.epoch,
269 stake_history: stakes.stake_history.clone(),
270 })
271 }
272
273 pub(crate) fn history(&self) -> &StakeHistory {
274 &self.stake_history
275 }
276
277 fn activate_epoch(&mut self, next_epoch: Epoch, thread_pool: &ThreadPool) {
278 type StakesHashMap = HashMap<Pubkey, u64>;
279 fn merge(mut acc: StakesHashMap, other: StakesHashMap) -> StakesHashMap {
280 if acc.len() < other.len() {
281 return merge(other, acc);
282 }
283 for (key, stake) in other {
284 *acc.entry(key).or_default() += stake;
285 }
286 acc
287 }
288 let stake_delegations: Vec<_> = self.stake_delegations.values().collect();
289 let stake_history_entry = thread_pool.install(|| {
292 stake_delegations
293 .par_iter()
294 .fold(StakeActivationStatus::default, |acc, stake_account| {
295 let delegation = stake_account.delegation();
296 acc + delegation
297 .stake_activating_and_deactivating(self.epoch, Some(&self.stake_history))
298 })
299 .reduce(StakeActivationStatus::default, Add::add)
300 });
301 self.stake_history.add(self.epoch, stake_history_entry);
302 self.epoch = next_epoch;
303 let delegated_stakes = thread_pool.install(|| {
306 stake_delegations
307 .par_iter()
308 .fold(HashMap::default, |mut delegated_stakes, stake_account| {
309 let delegation = stake_account.delegation();
310 let entry = delegated_stakes.entry(delegation.voter_pubkey).or_default();
311 *entry += delegation.stake(self.epoch, Some(&self.stake_history));
312 delegated_stakes
313 })
314 .reduce(HashMap::default, merge)
315 });
316 self.vote_accounts = self
317 .vote_accounts
318 .iter()
319 .map(|(&vote_pubkey, vote_account)| {
320 let delegated_stake = delegated_stakes
321 .get(&vote_pubkey)
322 .copied()
323 .unwrap_or_default();
324 (vote_pubkey, (delegated_stake, vote_account.clone()))
325 })
326 .collect();
327 }
328
329 fn calculate_stake(
331 &self,
332 voter_pubkey: &Pubkey,
333 epoch: Epoch,
334 stake_history: &StakeHistory,
335 ) -> u64 {
336 self.stake_delegations
337 .values()
338 .map(StakeAccount::delegation)
339 .filter(|delegation| &delegation.voter_pubkey == voter_pubkey)
340 .map(|delegation| delegation.stake(epoch, Some(stake_history)))
341 .sum()
342 }
343
344 pub(crate) fn vote_balance_and_staked(&self) -> u64 {
346 let get_stake = |stake_account: &StakeAccount| stake_account.delegation().stake;
347 let get_lamports = |(_, vote_account): (_, &VoteAccount)| vote_account.lamports();
348
349 self.stake_delegations.values().map(get_stake).sum::<u64>()
350 + self.vote_accounts.iter().map(get_lamports).sum::<u64>()
351 }
352
353 fn remove_vote_account(&mut self, vote_pubkey: &Pubkey) {
354 self.vote_accounts.remove(vote_pubkey);
355 }
356
357 fn remove_stake_delegation(&mut self, stake_pubkey: &Pubkey) {
358 if let Some(stake_account) = self.stake_delegations.remove(stake_pubkey) {
359 let removed_delegation = stake_account.delegation();
360 let removed_stake = removed_delegation.stake(self.epoch, Some(&self.stake_history));
361 self.vote_accounts
362 .sub_stake(&removed_delegation.voter_pubkey, removed_stake);
363 }
364 }
365
366 fn upsert_vote_account(&mut self, vote_pubkey: &Pubkey, vote_account: VoteAccount) {
367 debug_assert_ne!(vote_account.lamports(), 0u64);
368 debug_assert!(vote_account.is_deserialized());
369 let stake = match self.vote_accounts.remove(vote_pubkey) {
373 None => self.calculate_stake(vote_pubkey, self.epoch, &self.stake_history),
374 Some((stake, _)) => stake,
375 };
376 let entry = (stake, vote_account);
377 self.vote_accounts.insert(*vote_pubkey, entry);
378 }
379
380 fn upsert_stake_delegation(&mut self, stake_pubkey: Pubkey, stake_account: StakeAccount) {
381 debug_assert_ne!(stake_account.lamports(), 0u64);
382 let delegation = stake_account.delegation();
383 let voter_pubkey = delegation.voter_pubkey;
384 let stake = delegation.stake(self.epoch, Some(&self.stake_history));
385 match self.stake_delegations.insert(stake_pubkey, stake_account) {
386 None => self.vote_accounts.add_stake(&voter_pubkey, stake),
387 Some(old_stake_account) => {
388 let old_delegation = old_stake_account.delegation();
389 let old_voter_pubkey = old_delegation.voter_pubkey;
390 let old_stake = old_delegation.stake(self.epoch, Some(&self.stake_history));
391 if voter_pubkey != old_voter_pubkey || stake != old_stake {
392 self.vote_accounts.sub_stake(&old_voter_pubkey, old_stake);
393 self.vote_accounts.add_stake(&voter_pubkey, stake);
394 }
395 }
396 }
397 }
398
399 pub(crate) fn stake_delegations(&self) -> &ImHashMap<Pubkey, StakeAccount> {
400 &self.stake_delegations
401 }
402
403 pub(crate) fn highest_staked_node(&self) -> Option<Pubkey> {
404 let vote_account = self.vote_accounts.find_max_by_delegated_stake()?;
405 vote_account.node_pubkey()
406 }
407}
408
409impl StakesEnum {
410 pub fn vote_accounts(&self) -> &VoteAccounts {
411 match self {
412 StakesEnum::Accounts(stakes) => stakes.vote_accounts(),
413 StakesEnum::Delegations(stakes) => stakes.vote_accounts(),
414 }
415 }
416
417 pub(crate) fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
418 match self {
419 StakesEnum::Accounts(stakes) => stakes.staked_nodes(),
420 StakesEnum::Delegations(stakes) => stakes.staked_nodes(),
421 }
422 }
423}
424
425impl From<Stakes<StakeAccount>> for Stakes<Delegation> {
426 fn from(stakes: Stakes<StakeAccount>) -> Self {
427 let stake_delegations = stakes
428 .stake_delegations
429 .into_iter()
430 .map(|(pubkey, stake_account)| (pubkey, stake_account.delegation()))
431 .collect();
432 Self {
433 vote_accounts: stakes.vote_accounts,
434 stake_delegations,
435 unused: stakes.unused,
436 epoch: stakes.epoch,
437 stake_history: stakes.stake_history,
438 }
439 }
440}
441
442impl From<Stakes<StakeAccount>> for StakesEnum {
443 fn from(stakes: Stakes<StakeAccount>) -> Self {
444 Self::Accounts(stakes)
445 }
446}
447
448impl From<Stakes<Delegation>> for StakesEnum {
449 fn from(stakes: Stakes<Delegation>) -> Self {
450 Self::Delegations(stakes)
451 }
452}
453
454impl PartialEq<StakesEnum> for StakesEnum {
460 fn eq(&self, other: &StakesEnum) -> bool {
461 match (self, other) {
462 (Self::Accounts(stakes), Self::Accounts(other)) => stakes == other,
463 (Self::Accounts(stakes), Self::Delegations(other)) => {
464 let stakes = Stakes::<Delegation>::from(stakes.clone());
465 &stakes == other
466 }
467 (Self::Delegations(stakes), Self::Accounts(other)) => {
468 let other = Stakes::<Delegation>::from(other.clone());
469 stakes == &other
470 }
471 (Self::Delegations(stakes), Self::Delegations(other)) => stakes == other,
472 }
473 }
474}
475
476pub(crate) mod serde_stakes_enum_compat {
479 use {
480 super::*,
481 serde::{Deserialize, Deserializer, Serialize, Serializer},
482 };
483
484 pub(crate) fn serialize<S>(stakes: &StakesEnum, serializer: S) -> Result<S::Ok, S::Error>
485 where
486 S: Serializer,
487 {
488 match stakes {
489 StakesEnum::Accounts(stakes) => {
490 let stakes = Stakes::<Delegation>::from(stakes.clone());
491 stakes.serialize(serializer)
492 }
493 StakesEnum::Delegations(stakes) => stakes.serialize(serializer),
494 }
495 }
496
497 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Arc<StakesEnum>, D::Error>
498 where
499 D: Deserializer<'de>,
500 {
501 let stakes = Stakes::<Delegation>::deserialize(deserializer)?;
502 Ok(Arc::new(StakesEnum::Delegations(stakes)))
503 }
504}
505
506#[cfg(test)]
507pub(crate) mod tests {
508 use {
509 super::*,
510 rand::Rng,
511 rayon::ThreadPoolBuilder,
512 solana_sdk::{account::WritableAccount, pubkey::Pubkey, rent::Rent, stake},
513 solana_stake_program::stake_state,
514 solana_vote_program::vote_state::{self, VoteState, VoteStateVersions},
515 };
516
517 pub(crate) fn create_staked_node_accounts(
519 stake: u64,
520 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
521 let vote_pubkey = solana_sdk::pubkey::new_rand();
522 let vote_account =
523 vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
524 (
525 (vote_pubkey, vote_account),
526 create_stake_account(stake, &vote_pubkey),
527 )
528 }
529
530 pub(crate) fn create_stake_account(
532 stake: u64,
533 vote_pubkey: &Pubkey,
534 ) -> (Pubkey, AccountSharedData) {
535 let stake_pubkey = solana_sdk::pubkey::new_rand();
536 (
537 stake_pubkey,
538 stake_state::create_account(
539 &stake_pubkey,
540 vote_pubkey,
541 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
542 &Rent::free(),
543 stake,
544 ),
545 )
546 }
547
548 fn create_warming_staked_node_accounts(
549 stake: u64,
550 epoch: Epoch,
551 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
552 let vote_pubkey = solana_sdk::pubkey::new_rand();
553 let vote_account =
554 vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
555 (
556 (vote_pubkey, vote_account),
557 create_warming_stake_account(stake, epoch, &vote_pubkey),
558 )
559 }
560
561 fn create_warming_stake_account(
563 stake: u64,
564 epoch: Epoch,
565 vote_pubkey: &Pubkey,
566 ) -> (Pubkey, AccountSharedData) {
567 let stake_pubkey = solana_sdk::pubkey::new_rand();
568 (
569 stake_pubkey,
570 stake_state::create_account_with_activation_epoch(
571 &stake_pubkey,
572 vote_pubkey,
573 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
574 &Rent::free(),
575 stake,
576 epoch,
577 ),
578 )
579 }
580
581 #[test]
582 fn test_stakes_basic() {
583 for i in 0..4 {
584 let stakes_cache = StakesCache::new(Stakes {
585 epoch: i,
586 ..Stakes::default()
587 });
588
589 let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
590 create_staked_node_accounts(10);
591
592 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
593 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
594 let stake = stake_state::stake_from(&stake_account).unwrap();
595 {
596 let stakes = stakes_cache.stakes();
597 let vote_accounts = stakes.vote_accounts();
598 assert!(vote_accounts.get(&vote_pubkey).is_some());
599 assert_eq!(
600 vote_accounts.get_delegated_stake(&vote_pubkey),
601 stake.stake(i, None)
602 );
603 }
604
605 stake_account.set_lamports(42);
606 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
607 {
608 let stakes = stakes_cache.stakes();
609 let vote_accounts = stakes.vote_accounts();
610 assert!(vote_accounts.get(&vote_pubkey).is_some());
611 assert_eq!(
612 vote_accounts.get_delegated_stake(&vote_pubkey),
613 stake.stake(i, None)
614 ); }
616
617 let (_stake_pubkey, mut stake_account) = create_stake_account(42, &vote_pubkey);
619 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
620 let stake = stake_state::stake_from(&stake_account).unwrap();
621 {
622 let stakes = stakes_cache.stakes();
623 let vote_accounts = stakes.vote_accounts();
624 assert!(vote_accounts.get(&vote_pubkey).is_some());
625 assert_eq!(
626 vote_accounts.get_delegated_stake(&vote_pubkey),
627 stake.stake(i, None)
628 ); }
630
631 stake_account.set_lamports(0);
632 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
633 {
634 let stakes = stakes_cache.stakes();
635 let vote_accounts = stakes.vote_accounts();
636 assert!(vote_accounts.get(&vote_pubkey).is_some());
637 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
638 }
639 }
640 }
641
642 #[test]
643 fn test_stakes_highest() {
644 let stakes_cache = StakesCache::default();
645
646 assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
647
648 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
649 create_staked_node_accounts(10);
650
651 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
652 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
653
654 let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
655 create_staked_node_accounts(20);
656
657 stakes_cache.check_and_store(&vote11_pubkey, &vote11_account);
658 stakes_cache.check_and_store(&stake11_pubkey, &stake11_account);
659
660 let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
661
662 let highest_staked_node = stakes_cache.stakes().highest_staked_node();
663 assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
664 }
665
666 #[test]
667 fn test_stakes_vote_account_disappear_reappear() {
668 let stakes_cache = StakesCache::new(Stakes {
669 epoch: 4,
670 ..Stakes::default()
671 });
672
673 let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
674 create_staked_node_accounts(10);
675
676 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
677 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
678
679 {
680 let stakes = stakes_cache.stakes();
681 let vote_accounts = stakes.vote_accounts();
682 assert!(vote_accounts.get(&vote_pubkey).is_some());
683 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
684 }
685
686 vote_account.set_lamports(0);
687 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
688
689 {
690 let stakes = stakes_cache.stakes();
691 let vote_accounts = stakes.vote_accounts();
692 assert!(vote_accounts.get(&vote_pubkey).is_none());
693 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
694 }
695
696 vote_account.set_lamports(1);
697 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
698
699 {
700 let stakes = stakes_cache.stakes();
701 let vote_accounts = stakes.vote_accounts();
702 assert!(vote_accounts.get(&vote_pubkey).is_some());
703 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
704 }
705
706 let cache_data = vote_account.data().to_vec();
708 let mut pushed = vote_account.data().to_vec();
709 pushed.push(0);
710 vote_account.set_data(pushed);
711 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
712
713 {
714 let stakes = stakes_cache.stakes();
715 let vote_accounts = stakes.vote_accounts();
716 assert!(vote_accounts.get(&vote_pubkey).is_none());
717 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
718 }
719
720 let default_vote_state = VoteState::default();
722 let versioned = VoteStateVersions::new_current(default_vote_state);
723 VoteState::to(&versioned, &mut vote_account).unwrap();
724 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
725
726 {
727 let stakes = stakes_cache.stakes();
728 let vote_accounts = stakes.vote_accounts();
729 assert!(vote_accounts.get(&vote_pubkey).is_none());
730 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
731 }
732
733 vote_account.set_data(cache_data);
734 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
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
744 #[test]
745 fn test_stakes_change_delegate() {
746 let stakes_cache = StakesCache::new(Stakes {
747 epoch: 4,
748 ..Stakes::default()
749 });
750
751 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
752 create_staked_node_accounts(10);
753
754 let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
755 create_staked_node_accounts(10);
756
757 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
758 stakes_cache.check_and_store(&vote_pubkey2, &vote_account2);
759
760 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
762
763 let stake = stake_state::stake_from(&stake_account).unwrap();
764
765 {
766 let stakes = stakes_cache.stakes();
767 let vote_accounts = stakes.vote_accounts();
768 assert!(vote_accounts.get(&vote_pubkey).is_some());
769 assert_eq!(
770 vote_accounts.get_delegated_stake(&vote_pubkey),
771 stake.stake(stakes.epoch, Some(&stakes.stake_history))
772 );
773 assert!(vote_accounts.get(&vote_pubkey2).is_some());
774 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
775 }
776
777 stakes_cache.check_and_store(&stake_pubkey, &stake_account2);
779
780 {
781 let stakes = stakes_cache.stakes();
782 let vote_accounts = stakes.vote_accounts();
783 assert!(vote_accounts.get(&vote_pubkey).is_some());
784 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
785 assert!(vote_accounts.get(&vote_pubkey2).is_some());
786 assert_eq!(
787 vote_accounts.get_delegated_stake(&vote_pubkey2),
788 stake.stake(stakes.epoch, Some(&stakes.stake_history))
789 );
790 }
791 }
792 #[test]
793 fn test_stakes_multiple_stakers() {
794 let stakes_cache = StakesCache::new(Stakes {
795 epoch: 4,
796 ..Stakes::default()
797 });
798
799 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
800 create_staked_node_accounts(10);
801
802 let (stake_pubkey2, stake_account2) = create_stake_account(10, &vote_pubkey);
803
804 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
805
806 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
808 stakes_cache.check_and_store(&stake_pubkey2, &stake_account2);
809
810 {
811 let stakes = stakes_cache.stakes();
812 let vote_accounts = stakes.vote_accounts();
813 assert!(vote_accounts.get(&vote_pubkey).is_some());
814 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
815 }
816 }
817
818 #[test]
819 fn test_activate_epoch() {
820 let stakes_cache = StakesCache::default();
821
822 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
823 create_staked_node_accounts(10);
824
825 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
826 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
827 let stake = stake_state::stake_from(&stake_account).unwrap();
828
829 {
830 let stakes = stakes_cache.stakes();
831 let vote_accounts = stakes.vote_accounts();
832 assert_eq!(
833 vote_accounts.get_delegated_stake(&vote_pubkey),
834 stake.stake(stakes.epoch, Some(&stakes.stake_history))
835 );
836 }
837 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
838 stakes_cache.activate_epoch(3, &thread_pool);
839 {
840 let stakes = stakes_cache.stakes();
841 let vote_accounts = stakes.vote_accounts();
842 assert_eq!(
843 vote_accounts.get_delegated_stake(&vote_pubkey),
844 stake.stake(stakes.epoch, Some(&stakes.stake_history))
845 );
846 }
847 }
848
849 #[test]
850 fn test_stakes_not_delegate() {
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 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
860 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
861
862 {
863 let stakes = stakes_cache.stakes();
864 let vote_accounts = stakes.vote_accounts();
865 assert!(vote_accounts.get(&vote_pubkey).is_some());
866 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
867 }
868
869 stakes_cache.check_and_store(
871 &stake_pubkey,
872 &AccountSharedData::new(1, 0, &stake::program::id()),
873 );
874 {
875 let stakes = stakes_cache.stakes();
876 let vote_accounts = stakes.vote_accounts();
877 assert!(vote_accounts.get(&vote_pubkey).is_some());
878 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
879 }
880 }
881
882 #[test]
883 fn test_vote_balance_and_staked_empty() {
884 let stakes = Stakes::<StakeAccount>::default();
885 assert_eq!(stakes.vote_balance_and_staked(), 0);
886 }
887
888 #[test]
889 fn test_vote_balance_and_staked_normal() {
890 let stakes_cache = StakesCache::default();
891 impl Stakes<StakeAccount> {
892 fn vote_balance_and_warmed_staked(&self) -> u64 {
893 let vote_balance: u64 = self
894 .vote_accounts
895 .iter()
896 .map(|(_pubkey, account)| account.lamports())
897 .sum();
898 let warmed_stake: u64 = self
899 .vote_accounts
900 .delegated_stakes()
901 .map(|(_pubkey, stake)| stake)
902 .sum();
903 vote_balance + warmed_stake
904 }
905 }
906
907 let genesis_epoch = 0;
908 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
909 create_warming_staked_node_accounts(10, genesis_epoch);
910 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
911 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
912
913 {
914 let stakes = stakes_cache.stakes();
915 assert_eq!(stakes.vote_balance_and_staked(), 11);
916 assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
917 }
918
919 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
920 for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
921 stakes_cache.activate_epoch(epoch, &thread_pool);
922 let stakes = stakes_cache.stakes();
925 assert_eq!(stakes.vote_balance_and_staked(), 11);
926 assert_eq!(
927 stakes.vote_balance_and_warmed_staked(),
928 *expected_warmed_stake
929 );
930 }
931 }
932
933 #[test]
934 fn test_serde_stakes_enum_compat() {
935 #[derive(Debug, PartialEq, Deserialize, Serialize)]
936 struct Dummy {
937 head: String,
938 #[serde(with = "serde_stakes_enum_compat")]
939 stakes: Arc<StakesEnum>,
940 tail: String,
941 }
942 let mut rng = rand::thread_rng();
943 let stakes_cache = StakesCache::new(Stakes {
944 unused: rng.gen(),
945 epoch: rng.gen(),
946 ..Stakes::default()
947 });
948 for _ in 0..rng.gen_range(5usize, 10) {
949 let vote_pubkey = solana_sdk::pubkey::new_rand();
950 let vote_account = vote_state::create_account(
951 &vote_pubkey,
952 &solana_sdk::pubkey::new_rand(), rng.gen_range(0, 101), rng.gen_range(0, 1_000_000), );
956 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
957 for _ in 0..rng.gen_range(10usize, 20) {
958 let stake_pubkey = solana_sdk::pubkey::new_rand();
959 let rent = Rent::with_slots_per_epoch(rng.gen());
960 let stake_account = stake_state::create_account(
961 &stake_pubkey, &vote_pubkey,
963 &vote_account,
964 &rent,
965 rng.gen_range(0, 1_000_000), );
967 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
968 }
969 }
970 let stakes: Stakes<StakeAccount> = stakes_cache.stakes().clone();
971 assert!(stakes.vote_accounts.as_ref().len() >= 5);
972 assert!(stakes.stake_delegations.len() >= 50);
973 let dummy = Dummy {
974 head: String::from("dummy-head"),
975 stakes: Arc::new(StakesEnum::from(stakes.clone())),
976 tail: String::from("dummy-tail"),
977 };
978 assert!(dummy.stakes.vote_accounts().as_ref().len() >= 5);
979 let data = bincode::serialize(&dummy).unwrap();
980 let other: Dummy = bincode::deserialize(&data).unwrap();
981 assert_eq!(other, dummy);
982 let stakes = Stakes::<Delegation>::from(stakes);
983 assert!(stakes.vote_accounts.as_ref().len() >= 5);
984 assert!(stakes.stake_delegations.len() >= 50);
985 let other = match &*other.stakes {
986 StakesEnum::Accounts(_) => panic!("wrong type!"),
987 StakesEnum::Delegations(delegations) => delegations,
988 };
989 assert_eq!(other, &stakes)
990 }
991}