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 let stake_pubkey = solana_sdk::pubkey::new_rand();
525 (
526 (vote_pubkey, vote_account),
527 (
528 stake_pubkey,
529 create_stake_account(stake, &vote_pubkey, &stake_pubkey),
530 ),
531 )
532 }
533
534 pub(crate) fn create_stake_account(
536 stake: u64,
537 vote_pubkey: &Pubkey,
538 stake_pubkey: &Pubkey,
539 ) -> AccountSharedData {
540 stake_state::create_account(
541 stake_pubkey,
542 vote_pubkey,
543 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
544 &Rent::free(),
545 stake,
546 )
547 }
548
549 fn create_warming_staked_node_accounts(
550 stake: u64,
551 epoch: Epoch,
552 ) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
553 let vote_pubkey = solana_sdk::pubkey::new_rand();
554 let vote_account =
555 vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
556 (
557 (vote_pubkey, vote_account),
558 create_warming_stake_account(stake, epoch, &vote_pubkey),
559 )
560 }
561
562 fn create_warming_stake_account(
564 stake: u64,
565 epoch: Epoch,
566 vote_pubkey: &Pubkey,
567 ) -> (Pubkey, AccountSharedData) {
568 let stake_pubkey = solana_sdk::pubkey::new_rand();
569 (
570 stake_pubkey,
571 stake_state::create_account_with_activation_epoch(
572 &stake_pubkey,
573 vote_pubkey,
574 &vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
575 &Rent::free(),
576 stake,
577 epoch,
578 ),
579 )
580 }
581
582 #[test]
583 fn test_stakes_basic() {
584 for i in 0..4 {
585 let stakes_cache = StakesCache::new(Stakes {
586 epoch: i,
587 ..Stakes::default()
588 });
589
590 let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
591 create_staked_node_accounts(10);
592
593 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
594 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
595 let stake = stake_state::stake_from(&stake_account).unwrap();
596 {
597 let stakes = stakes_cache.stakes();
598 let vote_accounts = stakes.vote_accounts();
599 assert!(vote_accounts.get(&vote_pubkey).is_some());
600 assert_eq!(
601 vote_accounts.get_delegated_stake(&vote_pubkey),
602 stake.stake(i, None)
603 );
604 }
605
606 stake_account.set_lamports(42);
607 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
608 {
609 let stakes = stakes_cache.stakes();
610 let vote_accounts = stakes.vote_accounts();
611 assert!(vote_accounts.get(&vote_pubkey).is_some());
612 assert_eq!(
613 vote_accounts.get_delegated_stake(&vote_pubkey),
614 stake.stake(i, None)
615 ); }
617
618 let mut stake_account =
620 create_stake_account(42, &vote_pubkey, &solana_sdk::pubkey::new_rand());
621 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
622 let stake = stake_state::stake_from(&stake_account).unwrap();
623 {
624 let stakes = stakes_cache.stakes();
625 let vote_accounts = stakes.vote_accounts();
626 assert!(vote_accounts.get(&vote_pubkey).is_some());
627 assert_eq!(
628 vote_accounts.get_delegated_stake(&vote_pubkey),
629 stake.stake(i, None)
630 ); }
632
633 stake_account.set_lamports(0);
634 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
635 {
636 let stakes = stakes_cache.stakes();
637 let vote_accounts = stakes.vote_accounts();
638 assert!(vote_accounts.get(&vote_pubkey).is_some());
639 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
640 }
641 }
642 }
643
644 #[test]
645 fn test_stakes_highest() {
646 let stakes_cache = StakesCache::default();
647
648 assert_eq!(stakes_cache.stakes().highest_staked_node(), None);
649
650 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
651 create_staked_node_accounts(10);
652
653 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
654 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
655
656 let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
657 create_staked_node_accounts(20);
658
659 stakes_cache.check_and_store(&vote11_pubkey, &vote11_account);
660 stakes_cache.check_and_store(&stake11_pubkey, &stake11_account);
661
662 let vote11_node_pubkey = vote_state::from(&vote11_account).unwrap().node_pubkey;
663
664 let highest_staked_node = stakes_cache.stakes().highest_staked_node();
665 assert_eq!(highest_staked_node, Some(vote11_node_pubkey));
666 }
667
668 #[test]
669 fn test_stakes_vote_account_disappear_reappear() {
670 let stakes_cache = StakesCache::new(Stakes {
671 epoch: 4,
672 ..Stakes::default()
673 });
674
675 let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
676 create_staked_node_accounts(10);
677
678 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
679 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
680
681 {
682 let stakes = stakes_cache.stakes();
683 let vote_accounts = stakes.vote_accounts();
684 assert!(vote_accounts.get(&vote_pubkey).is_some());
685 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
686 }
687
688 vote_account.set_lamports(0);
689 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
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 vote_account.set_lamports(1);
699 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
700
701 {
702 let stakes = stakes_cache.stakes();
703 let vote_accounts = stakes.vote_accounts();
704 assert!(vote_accounts.get(&vote_pubkey).is_some());
705 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
706 }
707
708 let cache_data = vote_account.data().to_vec();
710 let mut pushed = vote_account.data().to_vec();
711 pushed.push(0);
712 vote_account.set_data(pushed);
713 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
714
715 {
716 let stakes = stakes_cache.stakes();
717 let vote_accounts = stakes.vote_accounts();
718 assert!(vote_accounts.get(&vote_pubkey).is_none());
719 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
720 }
721
722 let default_vote_state = VoteState::default();
724 let versioned = VoteStateVersions::new_current(default_vote_state);
725 vote_state::to(&versioned, &mut vote_account).unwrap();
726 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
727
728 {
729 let stakes = stakes_cache.stakes();
730 let vote_accounts = stakes.vote_accounts();
731 assert!(vote_accounts.get(&vote_pubkey).is_none());
732 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
733 }
734
735 vote_account.set_data(cache_data);
736 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
737
738 {
739 let stakes = stakes_cache.stakes();
740 let vote_accounts = stakes.vote_accounts();
741 assert!(vote_accounts.get(&vote_pubkey).is_some());
742 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
743 }
744 }
745
746 #[test]
747 fn test_stakes_change_delegate() {
748 let stakes_cache = StakesCache::new(Stakes {
749 epoch: 4,
750 ..Stakes::default()
751 });
752
753 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
754 create_staked_node_accounts(10);
755
756 let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
757 create_staked_node_accounts(10);
758
759 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
760 stakes_cache.check_and_store(&vote_pubkey2, &vote_account2);
761
762 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
764
765 let stake = stake_state::stake_from(&stake_account).unwrap();
766
767 {
768 let stakes = stakes_cache.stakes();
769 let vote_accounts = stakes.vote_accounts();
770 assert!(vote_accounts.get(&vote_pubkey).is_some());
771 assert_eq!(
772 vote_accounts.get_delegated_stake(&vote_pubkey),
773 stake.stake(stakes.epoch, Some(&stakes.stake_history))
774 );
775 assert!(vote_accounts.get(&vote_pubkey2).is_some());
776 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey2), 0);
777 }
778
779 stakes_cache.check_and_store(&stake_pubkey, &stake_account2);
781
782 {
783 let stakes = stakes_cache.stakes();
784 let vote_accounts = stakes.vote_accounts();
785 assert!(vote_accounts.get(&vote_pubkey).is_some());
786 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
787 assert!(vote_accounts.get(&vote_pubkey2).is_some());
788 assert_eq!(
789 vote_accounts.get_delegated_stake(&vote_pubkey2),
790 stake.stake(stakes.epoch, Some(&stakes.stake_history))
791 );
792 }
793 }
794 #[test]
795 fn test_stakes_multiple_stakers() {
796 let stakes_cache = StakesCache::new(Stakes {
797 epoch: 4,
798 ..Stakes::default()
799 });
800
801 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
802 create_staked_node_accounts(10);
803
804 let stake_pubkey2 = solana_sdk::pubkey::new_rand();
805 let stake_account2 = create_stake_account(10, &vote_pubkey, &stake_pubkey2);
806
807 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
808
809 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
811 stakes_cache.check_and_store(&stake_pubkey2, &stake_account2);
812
813 {
814 let stakes = stakes_cache.stakes();
815 let vote_accounts = stakes.vote_accounts();
816 assert!(vote_accounts.get(&vote_pubkey).is_some());
817 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 20);
818 }
819 }
820
821 #[test]
822 fn test_activate_epoch() {
823 let stakes_cache = StakesCache::default();
824
825 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
826 create_staked_node_accounts(10);
827
828 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
829 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
830 let stake = stake_state::stake_from(&stake_account).unwrap();
831
832 {
833 let stakes = stakes_cache.stakes();
834 let vote_accounts = stakes.vote_accounts();
835 assert_eq!(
836 vote_accounts.get_delegated_stake(&vote_pubkey),
837 stake.stake(stakes.epoch, Some(&stakes.stake_history))
838 );
839 }
840 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
841 stakes_cache.activate_epoch(3, &thread_pool);
842 {
843 let stakes = stakes_cache.stakes();
844 let vote_accounts = stakes.vote_accounts();
845 assert_eq!(
846 vote_accounts.get_delegated_stake(&vote_pubkey),
847 stake.stake(stakes.epoch, Some(&stakes.stake_history))
848 );
849 }
850 }
851
852 #[test]
853 fn test_stakes_not_delegate() {
854 let stakes_cache = StakesCache::new(Stakes {
855 epoch: 4,
856 ..Stakes::default()
857 });
858
859 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
860 create_staked_node_accounts(10);
861
862 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
863 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
864
865 {
866 let stakes = stakes_cache.stakes();
867 let vote_accounts = stakes.vote_accounts();
868 assert!(vote_accounts.get(&vote_pubkey).is_some());
869 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 10);
870 }
871
872 stakes_cache.check_and_store(
874 &stake_pubkey,
875 &AccountSharedData::new(1, 0, &stake::program::id()),
876 );
877 {
878 let stakes = stakes_cache.stakes();
879 let vote_accounts = stakes.vote_accounts();
880 assert!(vote_accounts.get(&vote_pubkey).is_some());
881 assert_eq!(vote_accounts.get_delegated_stake(&vote_pubkey), 0);
882 }
883 }
884
885 #[test]
886 fn test_vote_balance_and_staked_empty() {
887 let stakes = Stakes::<StakeAccount>::default();
888 assert_eq!(stakes.vote_balance_and_staked(), 0);
889 }
890
891 #[test]
892 fn test_vote_balance_and_staked_normal() {
893 let stakes_cache = StakesCache::default();
894 impl Stakes<StakeAccount> {
895 fn vote_balance_and_warmed_staked(&self) -> u64 {
896 let vote_balance: u64 = self
897 .vote_accounts
898 .iter()
899 .map(|(_pubkey, account)| account.lamports())
900 .sum();
901 let warmed_stake: u64 = self
902 .vote_accounts
903 .delegated_stakes()
904 .map(|(_pubkey, stake)| stake)
905 .sum();
906 vote_balance + warmed_stake
907 }
908 }
909
910 let genesis_epoch = 0;
911 let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
912 create_warming_staked_node_accounts(10, genesis_epoch);
913 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
914 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
915
916 {
917 let stakes = stakes_cache.stakes();
918 assert_eq!(stakes.vote_balance_and_staked(), 11);
919 assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
920 }
921
922 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
923 for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
924 stakes_cache.activate_epoch(epoch, &thread_pool);
925 let stakes = stakes_cache.stakes();
928 assert_eq!(stakes.vote_balance_and_staked(), 11);
929 assert_eq!(
930 stakes.vote_balance_and_warmed_staked(),
931 *expected_warmed_stake
932 );
933 }
934 }
935
936 #[test]
937 fn test_serde_stakes_enum_compat() {
938 #[derive(Debug, PartialEq, Deserialize, Serialize)]
939 struct Dummy {
940 head: String,
941 #[serde(with = "serde_stakes_enum_compat")]
942 stakes: Arc<StakesEnum>,
943 tail: String,
944 }
945 let mut rng = rand::thread_rng();
946 let stakes_cache = StakesCache::new(Stakes {
947 unused: rng.gen(),
948 epoch: rng.gen(),
949 ..Stakes::default()
950 });
951 for _ in 0..rng.gen_range(5usize, 10) {
952 let vote_pubkey = solana_sdk::pubkey::new_rand();
953 let vote_account = vote_state::create_account(
954 &vote_pubkey,
955 &solana_sdk::pubkey::new_rand(), rng.gen_range(0, 101), rng.gen_range(0, 1_000_000), );
959 stakes_cache.check_and_store(&vote_pubkey, &vote_account);
960 for _ in 0..rng.gen_range(10usize, 20) {
961 let stake_pubkey = solana_sdk::pubkey::new_rand();
962 let rent = Rent::with_slots_per_epoch(rng.gen());
963 let stake_account = stake_state::create_account(
964 &stake_pubkey, &vote_pubkey,
966 &vote_account,
967 &rent,
968 rng.gen_range(0, 1_000_000), );
970 stakes_cache.check_and_store(&stake_pubkey, &stake_account);
971 }
972 }
973 let stakes: Stakes<StakeAccount> = stakes_cache.stakes().clone();
974 assert!(stakes.vote_accounts.as_ref().len() >= 5);
975 assert!(stakes.stake_delegations.len() >= 50);
976 let dummy = Dummy {
977 head: String::from("dummy-head"),
978 stakes: Arc::new(StakesEnum::from(stakes.clone())),
979 tail: String::from("dummy-tail"),
980 };
981 assert!(dummy.stakes.vote_accounts().as_ref().len() >= 5);
982 let data = bincode::serialize(&dummy).unwrap();
983 let other: Dummy = bincode::deserialize(&data).unwrap();
984 assert_eq!(other, dummy);
985 let stakes = Stakes::<Delegation>::from(stakes);
986 assert!(stakes.vote_accounts.as_ref().len() >= 5);
987 assert!(stakes.stake_delegations.len() >= 50);
988 let other = match &*other.stakes {
989 StakesEnum::Accounts(_) => panic!("wrong type!"),
990 StakesEnum::Delegations(delegations) => delegations,
991 };
992 assert_eq!(other, &stakes)
993 }
994}