1mod calculation;
2mod distribution;
3mod epoch_rewards_hasher;
4mod sysvar;
5
6use {
7 super::Bank,
8 crate::{
9 inflation_rewards::points::PointValue, stake_account::StakeAccount,
10 stake_history::StakeHistory,
11 },
12 solana_account::{AccountSharedData, ReadableAccount},
13 solana_accounts_db::{
14 partitioned_rewards::PartitionedEpochRewardsConfig,
15 stake_rewards::StakeReward,
16 storable_accounts::{AccountForStorage, StorableAccounts},
17 },
18 solana_clock::Slot,
19 solana_pubkey::Pubkey,
20 solana_reward_info::RewardInfo,
21 solana_stake_interface::state::{Delegation, Stake},
22 solana_vote::vote_account::VoteAccounts,
23 std::{mem::MaybeUninit, sync::Arc},
24};
25
26const REWARD_CALCULATION_NUM_BLOCKS: u64 = 1;
29
30#[derive(Debug, Clone, PartialEq)]
31pub(crate) struct PartitionedStakeReward {
32 pub stake_pubkey: Pubkey,
34 pub stake: Stake,
36 pub stake_reward: u64,
38 pub commission: u8,
40}
41
42#[derive(Debug, Default, PartialEq)]
44pub(crate) struct PartitionedStakeRewards {
45 rewards: Vec<Option<PartitionedStakeReward>>,
47 num_rewards: usize,
49}
50
51impl PartitionedStakeRewards {
52 pub(crate) fn with_capacity(capacity: usize) -> Self {
53 let rewards = Vec::with_capacity(capacity);
54 Self {
55 rewards,
56 num_rewards: 0,
57 }
58 }
59
60 pub(crate) fn num_rewards(&self) -> usize {
62 self.num_rewards
63 }
64
65 pub(crate) fn total_len(&self) -> usize {
67 self.rewards.len()
68 }
69
70 pub(crate) fn get(&self, index: usize) -> Option<&Option<PartitionedStakeReward>> {
71 self.rewards.get(index)
72 }
73
74 pub(crate) fn enumerated_rewards_iter(
75 &self,
76 ) -> impl Iterator<Item = (usize, &PartitionedStakeReward)> {
77 self.rewards
78 .iter()
79 .enumerate()
80 .filter_map(|(index, reward)| Some((index, reward.as_ref()?)))
81 }
82
83 fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<Option<PartitionedStakeReward>>] {
84 self.rewards.spare_capacity_mut()
85 }
86
87 unsafe fn assume_init(&mut self, num_stake_rewards: usize) {
88 self.rewards.set_len(self.rewards.capacity());
89 self.num_rewards = num_stake_rewards;
90 }
91}
92
93#[cfg(test)]
94impl FromIterator<Option<PartitionedStakeReward>> for PartitionedStakeRewards {
95 fn from_iter<T: IntoIterator<Item = Option<PartitionedStakeReward>>>(iter: T) -> Self {
96 let mut len_some: usize = 0;
97 let rewards = Vec::from_iter(iter.into_iter().inspect(|reward| {
98 if reward.is_some() {
99 len_some = len_some.saturating_add(1);
100 }
101 }));
102 Self {
103 rewards,
104 num_rewards: len_some,
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq)]
110pub(crate) struct StartBlockHeightAndRewards {
111 pub(crate) distribution_starting_block_height: u64,
113 pub(crate) all_stake_rewards: Arc<PartitionedStakeRewards>,
115}
116
117#[derive(Debug, Clone, PartialEq)]
118pub(crate) struct StartBlockHeightAndPartitionedRewards {
119 pub(crate) distribution_starting_block_height: u64,
121
122 pub(crate) all_stake_rewards: Arc<PartitionedStakeRewards>,
124
125 pub(crate) partition_indices: Vec<Vec<usize>>,
129}
130
131#[derive(Debug, Clone, PartialEq, Default)]
133pub(crate) enum EpochRewardStatus {
134 Active(EpochRewardPhase),
139 #[default]
141 Inactive,
142}
143
144#[derive(Debug, Clone, PartialEq)]
145pub(crate) enum EpochRewardPhase {
146 Calculation(StartBlockHeightAndRewards),
147 Distribution(StartBlockHeightAndPartitionedRewards),
148}
149
150#[derive(Debug, Default)]
151pub(super) struct VoteRewardsAccounts {
152 pub(super) accounts_with_rewards: Vec<(Pubkey, RewardInfo, AccountSharedData)>,
154 pub(super) total_vote_rewards_lamports: u64,
156}
157
158pub(super) struct VoteRewardsAccountsStorable<'a> {
160 pub slot: Slot,
161 pub vote_rewards_accounts: &'a VoteRewardsAccounts,
162}
163
164impl<'a> StorableAccounts<'a> for VoteRewardsAccountsStorable<'a> {
165 fn account<Ret>(
166 &self,
167 index: usize,
168 mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
169 ) -> Ret {
170 let (pubkey, _, account) = &self.vote_rewards_accounts.accounts_with_rewards[index];
171 callback((pubkey, account).into())
172 }
173
174 fn is_zero_lamport(&self, index: usize) -> bool {
175 self.vote_rewards_accounts.accounts_with_rewards[index]
176 .2
177 .lamports()
178 == 0
179 }
180
181 fn data_len(&self, index: usize) -> usize {
182 self.vote_rewards_accounts.accounts_with_rewards[index]
183 .2
184 .data()
185 .len()
186 }
187
188 fn pubkey(&self, index: usize) -> &Pubkey {
189 &self.vote_rewards_accounts.accounts_with_rewards[index].0
190 }
191
192 fn slot(&self, _index: usize) -> Slot {
193 self.target_slot()
194 }
195
196 fn target_slot(&self) -> Slot {
197 self.slot
198 }
199
200 fn len(&self) -> usize {
201 self.vote_rewards_accounts.accounts_with_rewards.len()
202 }
203}
204
205#[derive(Debug, Default)]
206pub(super) struct StakeRewardCalculation {
208 stake_rewards: Arc<PartitionedStakeRewards>,
210 total_stake_rewards_lamports: u64,
212}
213
214#[derive(Debug)]
215struct CalculateValidatorRewardsResult {
216 vote_rewards_accounts: VoteRewardsAccounts,
217 stake_reward_calculation: StakeRewardCalculation,
218 point_value: PointValue,
219}
220
221impl Default for CalculateValidatorRewardsResult {
222 fn default() -> Self {
223 Self {
224 vote_rewards_accounts: VoteRewardsAccounts::default(),
225 stake_reward_calculation: StakeRewardCalculation::default(),
226 point_value: PointValue {
227 points: 0,
228 rewards: 0,
229 },
230 }
231 }
232}
233
234pub(super) struct EpochRewardCalculateParamInfo<'a> {
236 pub(super) stake_history: StakeHistory,
237 pub(super) stake_delegations: Vec<(&'a Pubkey, &'a StakeAccount<Delegation>)>,
238 pub(super) cached_vote_accounts: &'a VoteAccounts,
239}
240
241#[derive(Debug)]
245pub(super) struct PartitionedRewardsCalculation {
246 pub(super) vote_account_rewards: VoteRewardsAccounts,
247 pub(super) stake_rewards: StakeRewardCalculation,
248 pub(super) validator_rate: f64,
249 pub(super) foundation_rate: f64,
250 pub(super) prev_epoch_duration_in_years: f64,
251 pub(super) capitalization: u64,
252 point_value: PointValue,
253}
254
255pub(super) struct CalculateRewardsAndDistributeVoteRewardsResult {
256 pub(super) distributed_rewards: u64,
258 pub(super) point_value: PointValue,
263 pub(super) stake_rewards: Arc<PartitionedStakeRewards>,
265}
266
267pub(crate) type StakeRewards = Vec<StakeReward>;
268
269#[derive(Debug, PartialEq)]
270pub struct KeyedRewardsAndNumPartitions {
271 pub keyed_rewards: Vec<(Pubkey, RewardInfo)>,
272 pub num_partitions: Option<u64>,
273}
274
275impl KeyedRewardsAndNumPartitions {
276 pub fn should_record(&self) -> bool {
277 !self.keyed_rewards.is_empty() || self.num_partitions.is_some()
278 }
279}
280
281impl Bank {
282 pub fn get_rewards_and_num_partitions(&self) -> KeyedRewardsAndNumPartitions {
283 let keyed_rewards = self.rewards.read().unwrap().clone();
284 let epoch_rewards_sysvar = self.get_epoch_rewards_sysvar();
285 let epoch_schedule = self.epoch_schedule();
288 let parent_epoch = epoch_schedule.get_epoch(self.parent_slot());
289 let is_first_block_in_epoch = self.epoch() > parent_epoch;
290
291 let num_partitions = (epoch_rewards_sysvar.active && is_first_block_in_epoch)
292 .then_some(epoch_rewards_sysvar.num_partitions);
293 KeyedRewardsAndNumPartitions {
294 keyed_rewards,
295 num_partitions,
296 }
297 }
298
299 pub(crate) fn set_epoch_reward_status_calculation(
300 &mut self,
301 distribution_starting_block_height: u64,
302 stake_rewards: Arc<PartitionedStakeRewards>,
303 ) {
304 self.epoch_reward_status =
305 EpochRewardStatus::Active(EpochRewardPhase::Calculation(StartBlockHeightAndRewards {
306 distribution_starting_block_height,
307 all_stake_rewards: stake_rewards,
308 }));
309 }
310
311 pub(crate) fn set_epoch_reward_status_distribution(
312 &mut self,
313 distribution_starting_block_height: u64,
314 all_stake_rewards: Arc<PartitionedStakeRewards>,
315 partition_indices: Vec<Vec<usize>>,
316 ) {
317 self.epoch_reward_status = EpochRewardStatus::Active(EpochRewardPhase::Distribution(
318 StartBlockHeightAndPartitionedRewards {
319 distribution_starting_block_height,
320 all_stake_rewards,
321 partition_indices,
322 },
323 ));
324 }
325
326 pub(super) fn partitioned_epoch_rewards_config(&self) -> &PartitionedEpochRewardsConfig {
327 &self
328 .rc
329 .accounts
330 .accounts_db
331 .partitioned_epoch_rewards_config
332 }
333
334 pub(super) fn partitioned_rewards_stake_account_stores_per_block(&self) -> u64 {
336 self.partitioned_epoch_rewards_config()
337 .stake_account_stores_per_block
338 }
339
340 pub(super) fn get_reward_distribution_num_blocks(
342 &self,
343 rewards: &PartitionedStakeRewards,
344 ) -> u64 {
345 let total_stake_accounts = rewards.num_rewards();
346 if self.epoch_schedule.warmup && self.epoch < self.first_normal_epoch() {
347 1
348 } else {
349 const MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH: u64 = 10;
350 let num_chunks = total_stake_accounts
351 .div_ceil(self.partitioned_rewards_stake_account_stores_per_block() as usize)
352 as u64;
353
354 num_chunks.clamp(
356 1,
357 (self.epoch_schedule.slots_per_epoch / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH).max(1),
358 )
359 }
360 }
361
362 pub fn force_reward_interval_end_for_tests(&mut self) {
364 self.epoch_reward_status = EpochRewardStatus::Inactive;
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use {
371 super::*,
372 crate::{
373 bank::tests::{create_genesis_config, new_bank_from_parent_with_bank_forks},
374 bank_forks::BankForks,
375 genesis_utils::{
376 create_genesis_config_with_vote_accounts, GenesisConfigInfo, ValidatorVoteKeypairs,
377 },
378 runtime_config::RuntimeConfig,
379 },
380 assert_matches::assert_matches,
381 solana_account::{state_traits::StateMut, Account},
382 solana_accounts_db::accounts_db::{AccountsDbConfig, ACCOUNTS_DB_CONFIG_FOR_TESTING},
383 solana_epoch_schedule::EpochSchedule,
384 solana_hash::Hash,
385 solana_keypair::Keypair,
386 solana_native_token::LAMPORTS_PER_SOL,
387 solana_reward_info::RewardType,
388 solana_signer::Signer,
389 solana_stake_interface::{error::StakeError, state::StakeStateV2},
390 solana_system_transaction as system_transaction,
391 solana_transaction::Transaction,
392 solana_vote::vote_transaction,
393 solana_vote_interface::state::{VoteStateVersions, MAX_LOCKOUT_HISTORY},
394 solana_vote_program::vote_state::{self, TowerSync},
395 std::sync::{Arc, RwLock},
396 };
397
398 impl PartitionedStakeReward {
399 fn maybe_from(stake_reward: &StakeReward) -> Option<Self> {
400 if let Ok(StakeStateV2::Stake(_meta, stake, _flags)) =
401 stake_reward.stake_account.state()
402 {
403 Some(Self {
404 stake_pubkey: stake_reward.stake_pubkey,
405 stake,
406 stake_reward: stake_reward.stake_reward_info.lamports as u64,
407 commission: stake_reward.stake_reward_info.commission.unwrap(),
408 })
409 } else {
410 None
411 }
412 }
413
414 pub fn new_random() -> Self {
415 Self::maybe_from(&StakeReward::new_random()).unwrap()
416 }
417 }
418
419 pub fn build_partitioned_stake_rewards(
420 stake_rewards: &PartitionedStakeRewards,
421 partition_indices: &[Vec<usize>],
422 ) -> Vec<PartitionedStakeRewards> {
423 partition_indices
424 .iter()
425 .map(|partition_index| {
426 partition_index
429 .iter()
430 .map(|&index| stake_rewards.get(index).unwrap().clone())
431 .collect::<PartitionedStakeRewards>()
432 })
433 .collect::<Vec<_>>()
434 }
435
436 pub fn convert_rewards(
437 stake_rewards: impl IntoIterator<Item = StakeReward>,
438 ) -> PartitionedStakeRewards {
439 stake_rewards
440 .into_iter()
441 .map(|stake_reward| Some(PartitionedStakeReward::maybe_from(&stake_reward).unwrap()))
442 .collect()
443 }
444
445 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
446 enum RewardInterval {
447 InsideInterval,
449 OutsideInterval,
451 }
452
453 impl Bank {
454 fn get_reward_interval(&self) -> RewardInterval {
456 if matches!(self.epoch_reward_status, EpochRewardStatus::Active(_)) {
457 RewardInterval::InsideInterval
458 } else {
459 RewardInterval::OutsideInterval
460 }
461 }
462
463 fn is_calculated(&self) -> bool {
464 matches!(
465 self.epoch_reward_status,
466 EpochRewardStatus::Active(EpochRewardPhase::Calculation(_))
467 )
468 }
469
470 fn is_partitioned(&self) -> bool {
471 matches!(
472 self.epoch_reward_status,
473 EpochRewardStatus::Active(EpochRewardPhase::Distribution(_))
474 )
475 }
476
477 fn get_epoch_rewards_from_cache(
478 &self,
479 parent_hash: &Hash,
480 ) -> Option<Arc<PartitionedRewardsCalculation>> {
481 self.epoch_rewards_calculation_cache
482 .lock()
483 .unwrap()
484 .get(parent_hash)
485 .cloned()
486 }
487
488 fn get_epoch_rewards_cache_len(&self) -> usize {
489 self.epoch_rewards_calculation_cache.lock().unwrap().len()
490 }
491 }
492
493 pub(super) const SLOTS_PER_EPOCH: u64 = 32;
494
495 pub(super) struct RewardBank {
496 pub(super) bank: Arc<Bank>,
497 pub(super) voters: Vec<Pubkey>,
498 pub(super) stakers: Vec<Pubkey>,
499 }
500
501 pub(super) fn create_default_reward_bank(
503 expected_num_delegations: usize,
504 advance_num_slots: u64,
505 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
506 create_reward_bank(
507 expected_num_delegations,
508 PartitionedEpochRewardsConfig::default().stake_account_stores_per_block,
509 advance_num_slots,
510 )
511 }
512
513 pub(super) fn create_reward_bank(
514 expected_num_delegations: usize,
515 stake_account_stores_per_block: u64,
516 advance_num_slots: u64,
517 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
518 create_reward_bank_with_specific_stakes(
519 vec![2_000_000_000; expected_num_delegations],
520 stake_account_stores_per_block,
521 advance_num_slots,
522 )
523 }
524
525 pub(super) fn create_reward_bank_with_specific_stakes(
526 stakes: Vec<u64>,
527 stake_account_stores_per_block: u64,
528 advance_num_slots: u64,
529 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
530 let validator_keypairs = (0..stakes.len())
531 .map(|_| ValidatorVoteKeypairs::new_rand())
532 .collect::<Vec<_>>();
533
534 let GenesisConfigInfo {
535 mut genesis_config, ..
536 } = create_genesis_config_with_vote_accounts(1_000_000_000, &validator_keypairs, stakes);
537 genesis_config.epoch_schedule = EpochSchedule::new(SLOTS_PER_EPOCH);
538
539 let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone();
540 accounts_db_config.partitioned_epoch_rewards_config =
541 PartitionedEpochRewardsConfig::new_for_test(stake_account_stores_per_block);
542
543 let bank = Bank::new_with_paths(
544 &genesis_config,
545 Arc::new(RuntimeConfig::default()),
546 Vec::new(),
547 None,
548 None,
549 false,
550 Some(accounts_db_config),
551 None,
552 Some(Pubkey::new_unique()),
553 Arc::default(),
554 None,
555 None,
556 );
557
558 for validator_vote_keypairs in &validator_keypairs {
561 let vote_id = validator_vote_keypairs.vote_keypair.pubkey();
562 let mut vote_account = bank.get_account(&vote_id).unwrap();
563 let mut vote_state = Some(vote_state::from(&vote_account).unwrap());
565 for i in 0..MAX_LOCKOUT_HISTORY + 42 {
566 if let Some(v) = vote_state.as_mut() {
567 vote_state::process_slot_vote_unchecked(v, i as u64)
568 }
569 let versioned = VoteStateVersions::V3(Box::new(vote_state.take().unwrap()));
570 vote_state::to(&versioned, &mut vote_account).unwrap();
571 match versioned {
572 VoteStateVersions::V3(v) => {
573 vote_state = Some(*v);
574 }
575 _ => panic!("Has to be of type Current"),
576 };
577 }
578 bank.store_account_and_update_capitalization(&vote_id, &vote_account);
579 }
580
581 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
584 let bank = new_bank_from_parent_with_bank_forks(
585 &bank_forks,
586 bank,
587 &Pubkey::default(),
588 advance_num_slots,
589 );
590
591 (
592 RewardBank {
593 bank,
594 voters: validator_keypairs
595 .iter()
596 .map(|k| k.vote_keypair.pubkey())
597 .collect(),
598 stakers: validator_keypairs
599 .iter()
600 .map(|k| k.stake_keypair.pubkey())
601 .collect(),
602 },
603 bank_forks,
604 )
605 }
606
607 #[test]
608 fn test_force_reward_interval_end() {
609 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
610 let mut bank = Bank::new_for_tests(&genesis_config);
611
612 let expected_num = 100;
613
614 let stake_rewards = (0..expected_num)
615 .map(|_| Some(PartitionedStakeReward::new_random()))
616 .collect::<PartitionedStakeRewards>();
617
618 let partition_indices = vec![(0..expected_num).collect()];
619
620 bank.set_epoch_reward_status_distribution(
621 bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
622 Arc::new(stake_rewards),
623 partition_indices,
624 );
625 assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
626
627 bank.force_reward_interval_end_for_tests();
628 assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
629 }
630
631 #[test]
634 fn test_get_reward_distribution_num_blocks_cap() {
635 let (mut genesis_config, _mint_keypair) =
636 create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
637 genesis_config.epoch_schedule = EpochSchedule::custom(32, 32, false);
638
639 let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone();
641 accounts_db_config.partitioned_epoch_rewards_config =
642 PartitionedEpochRewardsConfig::new_for_test(10);
643
644 let bank = Bank::new_with_paths(
645 &genesis_config,
646 Arc::new(RuntimeConfig::default()),
647 Vec::new(),
648 None,
649 None,
650 false,
651 Some(accounts_db_config),
652 None,
653 Some(Pubkey::new_unique()),
654 Arc::default(),
655 None,
656 None,
657 );
658
659 let stake_account_stores_per_block =
660 bank.partitioned_rewards_stake_account_stores_per_block();
661 assert_eq!(stake_account_stores_per_block, 10);
662
663 let check_num_reward_distribution_blocks =
664 |num_stakes: u64, expected_num_reward_distribution_blocks: u64| {
665 let stake_rewards = (0..num_stakes)
667 .map(|_| Some(PartitionedStakeReward::new_random()))
668 .collect::<PartitionedStakeRewards>();
669
670 assert_eq!(
671 bank.get_reward_distribution_num_blocks(&stake_rewards),
672 expected_num_reward_distribution_blocks
673 );
674 };
675
676 for test_record in [
677 (0, 1),
679 (1, 1),
680 (stake_account_stores_per_block, 1),
681 (2 * stake_account_stores_per_block - 1, 2),
682 (2 * stake_account_stores_per_block, 2),
683 (3 * stake_account_stores_per_block - 1, 3),
684 (3 * stake_account_stores_per_block, 3),
685 (4 * stake_account_stores_per_block, 3), (5 * stake_account_stores_per_block, 3), ] {
688 check_num_reward_distribution_blocks(test_record.0, test_record.1);
689 }
690 }
691
692 #[test]
694 fn test_get_reward_distribution_num_blocks_normal() {
695 solana_logger::setup();
696 let (mut genesis_config, _mint_keypair) =
697 create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
698 genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
699
700 let bank = Bank::new_for_tests(&genesis_config);
701
702 let expected_num = 8192;
704 let stake_rewards = (0..expected_num)
705 .map(|_| Some(PartitionedStakeReward::new_random()))
706 .collect::<PartitionedStakeRewards>();
707
708 assert_eq!(bank.get_reward_distribution_num_blocks(&stake_rewards), 2);
709 }
710
711 #[test]
714 fn test_get_reward_distribution_num_blocks_warmup() {
715 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
716
717 let bank = Bank::new_for_tests(&genesis_config);
718 let rewards = PartitionedStakeRewards::default();
719 assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
720 }
721
722 #[test]
728 fn test_get_reward_distribution_num_blocks_none() {
729 let rewards_all = 8192;
730 let expected_rewards_some = 6144;
731 let rewards = (0..rewards_all)
732 .map(|i| {
733 if i % 4 == 0 {
734 None
735 } else {
736 Some(PartitionedStakeReward::new_random())
737 }
738 })
739 .collect::<PartitionedStakeRewards>();
740 assert_eq!(rewards.rewards.len(), rewards_all);
741 assert_eq!(rewards.num_rewards(), expected_rewards_some);
742
743 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
744 let bank = Bank::new_for_tests(&genesis_config);
745 assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
746 }
747
748 #[test]
749 fn test_rewards_computation_and_partitioned_distribution_one_block() {
750 solana_logger::setup();
751
752 let starting_slot = SLOTS_PER_EPOCH - 1;
753 let (
754 RewardBank {
755 bank: mut previous_bank,
756 ..
757 },
758 bank_forks,
759 ) = create_default_reward_bank(100, starting_slot - 1);
760
761 for slot in starting_slot..=(2 * SLOTS_PER_EPOCH) + 2 {
763 let pre_cap = previous_bank.capitalization();
764 let curr_bank = new_bank_from_parent_with_bank_forks(
765 bank_forks.as_ref(),
766 previous_bank.clone(),
767 &Pubkey::default(),
768 slot,
769 );
770 let post_cap = curr_bank.capitalization();
771
772 if slot % SLOTS_PER_EPOCH == 0 {
773 assert_matches!(
776 curr_bank.get_reward_interval(),
777 RewardInterval::InsideInterval
778 );
779
780 assert!(curr_bank.is_calculated());
781
782 assert!(curr_bank
784 .get_epoch_rewards_from_cache(&curr_bank.parent_hash)
785 .is_some());
786 assert_eq!(post_cap, pre_cap);
787
788 let _ = bank_forks.write().unwrap().set_root(slot, None, None);
791 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 0);
792 } else if slot == SLOTS_PER_EPOCH + 1 {
793 assert_matches!(
799 curr_bank.get_reward_interval(),
800 RewardInterval::OutsideInterval
801 );
802 let account = curr_bank
803 .get_account(&solana_sysvar::epoch_rewards::id())
804 .unwrap();
805 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
806 solana_account::from_account(&account).unwrap();
807 assert_eq!(post_cap, pre_cap + epoch_rewards.distributed_rewards);
808 } else {
809 assert_matches!(
814 curr_bank.get_reward_interval(),
815 RewardInterval::OutsideInterval
816 );
817
818 assert_eq!(post_cap, pre_cap);
820 }
821 if slot >= SLOTS_PER_EPOCH {
824 let epoch_rewards_lamports =
825 curr_bank.get_balance(&solana_sysvar::epoch_rewards::id());
826 assert!(epoch_rewards_lamports > 0);
827 }
828 previous_bank = curr_bank;
829 }
830 }
831
832 #[test]
834 fn test_rewards_computation_and_partitioned_distribution_two_blocks() {
835 solana_logger::setup();
836
837 let starting_slot = SLOTS_PER_EPOCH - 1;
838 let (
839 RewardBank {
840 bank: mut previous_bank,
841 ..
842 },
843 bank_forks,
844 ) = create_reward_bank(100, 50, starting_slot - 1);
845 let mut starting_hash = None;
846
847 for slot in starting_slot..=SLOTS_PER_EPOCH + 3 {
849 let pre_cap = previous_bank.capitalization();
850
851 let pre_sysvar_account = previous_bank
852 .get_account(&solana_sysvar::epoch_rewards::id())
853 .unwrap_or_default();
854 let pre_epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
855 solana_account::from_account(&pre_sysvar_account).unwrap_or_default();
856 let pre_distributed_rewards = pre_epoch_rewards.distributed_rewards;
857 let curr_bank = new_bank_from_parent_with_bank_forks(
858 bank_forks.as_ref(),
859 previous_bank.clone(),
860 &Pubkey::default(),
861 slot,
862 );
863 let post_cap = curr_bank.capitalization();
864
865 if slot == SLOTS_PER_EPOCH {
866 assert_matches!(
869 curr_bank.get_reward_interval(),
870 RewardInterval::InsideInterval
871 );
872
873 assert!(curr_bank.is_calculated());
875
876 assert!(curr_bank
878 .get_epoch_rewards_from_cache(&curr_bank.parent_hash)
879 .is_some());
880 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 1);
881 starting_hash = Some(curr_bank.parent_hash);
882 } else if slot == SLOTS_PER_EPOCH + 1 {
883 assert_matches!(
887 curr_bank.get_reward_interval(),
888 RewardInterval::InsideInterval
889 );
890
891 assert!(curr_bank
894 .get_epoch_rewards_from_cache(&starting_hash.unwrap())
895 .is_some());
896 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 1);
897
898 assert!(curr_bank.is_partitioned());
900
901 let account = curr_bank
902 .get_account(&solana_sysvar::epoch_rewards::id())
903 .unwrap();
904 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
905 solana_account::from_account(&account).unwrap();
906 assert_eq!(
907 post_cap,
908 pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards
909 );
910
911 let _ = bank_forks.write().unwrap().set_root(slot - 1, None, None);
914 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 0);
915 } else if slot == SLOTS_PER_EPOCH + 2 {
916 assert_matches!(
922 curr_bank.get_reward_interval(),
923 RewardInterval::OutsideInterval
924 );
925
926 let account = curr_bank
927 .get_account(&solana_sysvar::epoch_rewards::id())
928 .unwrap();
929 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
930 solana_account::from_account(&account).unwrap();
931 assert_eq!(
932 post_cap,
933 pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards
934 );
935 } else {
936 assert_matches!(
941 curr_bank.get_reward_interval(),
942 RewardInterval::OutsideInterval
943 );
944
945 assert_eq!(post_cap, pre_cap);
947 }
948 previous_bank = curr_bank;
949 }
950 }
951
952 #[test]
956 fn test_program_execution_restricted_for_stake_account_in_reward_period() {
957 use solana_transaction_error::TransactionError::InstructionError;
958
959 let validator_vote_keypairs = ValidatorVoteKeypairs::new_rand();
960 let validator_keypairs = vec![&validator_vote_keypairs];
961 let GenesisConfigInfo {
962 mut genesis_config,
963 mint_keypair,
964 ..
965 } = create_genesis_config_with_vote_accounts(
966 1_000_000_000,
967 &validator_keypairs,
968 vec![1_000_000_000; 1],
969 );
970
971 let vote_key = validator_keypairs[0].vote_keypair.pubkey();
973 let vote_account = genesis_config
974 .accounts
975 .iter()
976 .find(|(&address, _)| address == vote_key)
977 .map(|(_, account)| account)
978 .unwrap()
979 .clone();
980
981 let new_stake_signer = Keypair::new();
982 let new_stake_address = new_stake_signer.pubkey();
983 let new_stake_account = Account::from(solana_stake_program::stake_state::create_account(
984 &new_stake_address,
985 &vote_key,
986 &vote_account.into(),
987 &genesis_config.rent,
988 2_000_000_000,
989 ));
990 genesis_config
991 .accounts
992 .extend(vec![(new_stake_address, new_stake_account)]);
993
994 let (mut previous_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
995 let num_slots_in_epoch = previous_bank.get_slots_in_epoch(previous_bank.epoch());
996 assert_eq!(num_slots_in_epoch, 32);
997
998 let transfer_amount = 5_000;
999
1000 for slot in 1..=num_slots_in_epoch + 2 {
1001 let bank = new_bank_from_parent_with_bank_forks(
1002 bank_forks.as_ref(),
1003 previous_bank.clone(),
1004 &Pubkey::default(),
1005 slot,
1006 );
1007
1008 let tower_sync = TowerSync::new_from_slot(slot - 1, previous_bank.hash());
1011 let vote = vote_transaction::new_tower_sync_transaction(
1012 tower_sync,
1013 previous_bank.last_blockhash(),
1014 &validator_vote_keypairs.node_keypair,
1015 &validator_vote_keypairs.vote_keypair,
1016 &validator_vote_keypairs.vote_keypair,
1017 None,
1018 );
1019 bank.process_transaction(&vote).unwrap();
1020
1021 let system_tx = system_transaction::transfer(
1023 &mint_keypair,
1024 &new_stake_address,
1025 transfer_amount,
1026 bank.last_blockhash(),
1027 );
1028 let system_result = bank.process_transaction(&system_tx);
1029
1030 assert!(system_result.is_ok());
1032
1033 let stake_ix = solana_stake_interface::instruction::withdraw(
1035 &new_stake_address,
1036 &new_stake_address,
1037 &mint_keypair.pubkey(),
1038 transfer_amount,
1039 None,
1040 );
1041 let stake_tx = Transaction::new_signed_with_payer(
1042 &[stake_ix],
1043 Some(&mint_keypair.pubkey()),
1044 &[&mint_keypair, &new_stake_signer],
1045 bank.last_blockhash(),
1046 );
1047 let stake_result = bank.process_transaction(&stake_tx);
1048
1049 if slot == num_slots_in_epoch {
1050 assert_eq!(
1054 stake_result,
1055 Err(InstructionError(0, StakeError::EpochRewardsActive.into()))
1056 );
1057 } else {
1058 assert!(stake_result.is_ok());
1061 }
1062
1063 bank.register_unique_recent_blockhash_for_test();
1067 previous_bank = bank;
1068 }
1069 }
1070
1071 #[test]
1072 fn test_get_rewards_and_partitions() {
1073 let starting_slot = SLOTS_PER_EPOCH - 1;
1074 let num_rewards = 100;
1075 let stake_account_stores_per_block = 50;
1076 let (RewardBank { bank, .. }, _) =
1077 create_reward_bank(num_rewards, stake_account_stores_per_block, starting_slot);
1078
1079 assert_eq!(
1082 bank.get_rewards_and_num_partitions(),
1083 KeyedRewardsAndNumPartitions {
1084 keyed_rewards: vec![],
1085 num_partitions: None,
1086 }
1087 );
1088
1089 let epoch_boundary_bank = Arc::new(Bank::new_from_parent(
1090 bank,
1091 &Pubkey::default(),
1092 SLOTS_PER_EPOCH,
1093 ));
1094 let KeyedRewardsAndNumPartitions {
1096 keyed_rewards,
1097 num_partitions,
1098 } = epoch_boundary_bank.get_rewards_and_num_partitions();
1099 for (_pubkey, reward) in keyed_rewards.iter() {
1100 assert_eq!(reward.reward_type, RewardType::Voting);
1101 }
1102 assert_eq!(keyed_rewards.len(), num_rewards);
1103 assert_eq!(
1104 num_partitions,
1105 Some(num_rewards as u64 / stake_account_stores_per_block)
1106 );
1107
1108 let mut total_staking_rewards = 0;
1109
1110 let partition0_bank = Arc::new(Bank::new_from_parent(
1111 epoch_boundary_bank,
1112 &Pubkey::default(),
1113 SLOTS_PER_EPOCH + 1,
1114 ));
1115 let KeyedRewardsAndNumPartitions {
1118 keyed_rewards,
1119 num_partitions,
1120 } = partition0_bank.get_rewards_and_num_partitions();
1121 for (_pubkey, reward) in keyed_rewards.iter() {
1122 assert_eq!(reward.reward_type, RewardType::Staking);
1123 }
1124 total_staking_rewards += keyed_rewards.len();
1125 assert_eq!(num_partitions, None);
1126
1127 let partition1_bank = Arc::new(Bank::new_from_parent(
1128 partition0_bank,
1129 &Pubkey::default(),
1130 SLOTS_PER_EPOCH + 2,
1131 ));
1132 let KeyedRewardsAndNumPartitions {
1135 keyed_rewards,
1136 num_partitions,
1137 } = partition1_bank.get_rewards_and_num_partitions();
1138 for (_pubkey, reward) in keyed_rewards.iter() {
1139 assert_eq!(reward.reward_type, RewardType::Staking);
1140 }
1141 total_staking_rewards += keyed_rewards.len();
1142 assert_eq!(num_partitions, None);
1143
1144 assert_eq!(total_staking_rewards, num_rewards);
1146
1147 let bank = Bank::new_from_parent(partition1_bank, &Pubkey::default(), SLOTS_PER_EPOCH + 3);
1148 assert_eq!(
1151 bank.get_rewards_and_num_partitions(),
1152 KeyedRewardsAndNumPartitions {
1153 keyed_rewards: vec![],
1154 num_partitions: None,
1155 }
1156 );
1157 }
1158
1159 #[test]
1160 fn test_rewards_and_partitions_should_record() {
1161 let reward = RewardInfo {
1162 reward_type: RewardType::Voting,
1163 lamports: 55,
1164 post_balance: 5555,
1165 commission: Some(5),
1166 };
1167
1168 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1169 keyed_rewards: vec![],
1170 num_partitions: None,
1171 };
1172 assert!(!rewards_and_partitions.should_record());
1173
1174 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1175 keyed_rewards: vec![(Pubkey::new_unique(), reward)],
1176 num_partitions: None,
1177 };
1178 assert!(rewards_and_partitions.should_record());
1179
1180 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1181 keyed_rewards: vec![],
1182 num_partitions: Some(42),
1183 };
1184 assert!(rewards_and_partitions.should_record());
1185
1186 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1187 keyed_rewards: vec![(Pubkey::new_unique(), reward)],
1188 num_partitions: Some(42),
1189 };
1190 assert!(rewards_and_partitions.should_record());
1191 }
1192}