1mod calculation;
2mod distribution;
3mod epoch_rewards_hasher;
4mod sysvar;
5
6use {
7 super::Bank,
8 crate::{
9 inflation_rewards::points::PointValue, reward_info::RewardInfo,
10 stake_account::StakeAccount, stake_history::StakeHistory,
11 },
12 solana_account::{AccountSharedData, ReadableAccount},
13 solana_accounts_db::{
14 stake_rewards::StakeReward,
15 storable_accounts::{AccountForStorage, StorableAccounts},
16 },
17 solana_clock::Slot,
18 solana_pubkey::{Pubkey, PubkeyHasherBuilder},
19 solana_stake_interface::state::{Delegation, Stake},
20 solana_vote::vote_account::VoteAccounts,
21 std::{collections::HashMap, mem::MaybeUninit, sync::Arc},
22};
23
24const REWARD_CALCULATION_NUM_BLOCKS: u64 = 1;
27
28#[derive(Debug, Clone, PartialEq)]
30pub(crate) struct PartitionedStakeReward {
31 pub stake_pubkey: Pubkey,
33 pub inflation: InflationReward,
35 pub block_reward: u64,
37}
38
39#[derive(Debug, Clone, PartialEq)]
41pub(crate) struct InflationReward {
42 pub stake: Stake,
44 pub stake_reward: u64,
46 pub commission_bps: Option<u16>,
52}
53
54#[derive(Debug, Default, PartialEq)]
56pub(crate) struct PartitionedStakeRewards {
57 rewards: Vec<Option<PartitionedStakeReward>>,
59 num_rewards: usize,
61}
62
63impl PartitionedStakeRewards {
64 pub(crate) fn with_capacity(capacity: usize) -> Self {
65 let rewards = Vec::with_capacity(capacity);
66 Self {
67 rewards,
68 num_rewards: 0,
69 }
70 }
71
72 pub(crate) fn num_rewards(&self) -> usize {
74 self.num_rewards
75 }
76
77 pub(crate) fn total_len(&self) -> usize {
79 self.rewards.len()
80 }
81
82 pub(crate) fn get(&self, index: usize) -> Option<&Option<PartitionedStakeReward>> {
83 self.rewards.get(index)
84 }
85
86 pub(crate) fn enumerated_rewards_iter(
87 &self,
88 ) -> impl Iterator<Item = (usize, &PartitionedStakeReward)> {
89 self.rewards
90 .iter()
91 .enumerate()
92 .filter_map(|(index, reward)| reward.as_ref().map(|reward| (index, reward)))
93 }
94
95 fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<Option<PartitionedStakeReward>>] {
96 self.rewards.spare_capacity_mut()
97 }
98
99 unsafe fn assume_init(&mut self, num_stake_rewards: usize, total_len: usize) {
102 debug_assert!(num_stake_rewards <= total_len);
103 unsafe {
104 self.rewards.set_len(total_len);
105 }
106 self.num_rewards = num_stake_rewards;
107 }
108}
109
110#[cfg(test)]
111impl FromIterator<Option<PartitionedStakeReward>> for PartitionedStakeRewards {
112 fn from_iter<T: IntoIterator<Item = Option<PartitionedStakeReward>>>(iter: T) -> Self {
113 let mut len_some: usize = 0;
114 let rewards = Vec::from_iter(iter.into_iter().inspect(|reward| {
115 if reward.is_some() {
116 len_some = len_some.saturating_add(1);
117 }
118 }));
119 Self {
120 rewards,
121 num_rewards: len_some,
122 }
123 }
124}
125
126#[cfg(test)]
127impl FromIterator<PartitionedStakeReward> for PartitionedStakeRewards {
128 fn from_iter<T: IntoIterator<Item = PartitionedStakeReward>>(iter: T) -> Self {
129 let rewards = Vec::from_iter(iter.into_iter().map(Some));
130 let num_rewards = rewards.len();
131 Self {
132 rewards,
133 num_rewards,
134 }
135 }
136}
137
138#[derive(Debug, Clone, PartialEq)]
139pub(crate) struct StartBlockHeightAndRewards {
140 pub(crate) distribution_starting_block_height: u64,
142 pub(crate) all_stake_rewards: Arc<PartitionedStakeRewards>,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub(crate) struct StartBlockHeightAndPartitionedRewards {
148 pub(crate) distribution_starting_block_height: u64,
150
151 pub(crate) all_stake_rewards: Arc<PartitionedStakeRewards>,
153
154 pub(crate) partition_indices: Vec<Vec<usize>>,
158}
159
160#[derive(Debug, Clone, PartialEq, Default)]
162pub(crate) enum EpochRewardStatus {
163 Active(EpochRewardPhase),
168 #[default]
170 Inactive,
171}
172
173#[derive(Debug, Clone, PartialEq)]
174pub(crate) enum EpochRewardPhase {
175 Calculation(StartBlockHeightAndRewards),
176 Distribution(StartBlockHeightAndPartitionedRewards),
177}
178
179#[derive(Debug)]
180#[cfg_attr(test, derive(Clone))]
181pub(super) struct RewardCommission {
182 pub(super) commission_bps: Option<u16>,
185 pub(super) commission_lamports: u64,
186 pub(super) burned_lamports: u64,
187 pub(super) is_vote_account: bool,
188}
189
190pub(super) type RewardCommissions = HashMap<Pubkey, RewardCommission, PubkeyHasherBuilder>;
191
192#[derive(Debug, Default)]
195pub(super) struct RewardLamportAmounts {
196 pub(super) distributed_lamports: u64,
202 pub(super) distributed_to_incinerator_lamports: u64,
208 pub(super) burned_lamports: u64,
210 pub(super) block_rewards: u64,
212}
213
214#[derive(Debug, Default)]
215pub(super) struct EpochBoundaryAccounts {
216 pub(super) accounts_with_rewards: Vec<(Pubkey, RewardInfo, AccountSharedData)>,
218 pub(super) swept_vote_accounts: Vec<(Pubkey, AccountSharedData)>,
220 pub(super) amounts: RewardLamportAmounts,
222}
223
224pub(super) struct EpochBoundaryAccountsStorable<'a> {
226 pub slot: Slot,
227 pub epoch_boundary_accounts: &'a EpochBoundaryAccounts,
228}
229
230impl<'a> EpochBoundaryAccountsStorable<'a> {
231 fn get_unchecked(&self, index: usize) -> (&Pubkey, &AccountSharedData) {
232 let num_accounts_with_rewards = self.epoch_boundary_accounts.accounts_with_rewards.len();
233 if index >= num_accounts_with_rewards {
234 let (pubkey, account) = &self.epoch_boundary_accounts.swept_vote_accounts
235 [index - num_accounts_with_rewards];
236 (pubkey, account)
237 } else {
238 let (pubkey, _, account) = &self.epoch_boundary_accounts.accounts_with_rewards[index];
239 (pubkey, account)
240 }
241 }
242}
243
244impl<'a> StorableAccounts<'a> for EpochBoundaryAccountsStorable<'a> {
245 fn account<Ret>(
246 &self,
247 index: usize,
248 mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
249 ) -> Ret {
250 let (pubkey, account) = self.get_unchecked(index);
251 callback((pubkey, account).into())
252 }
253
254 fn account_for_geyser<Ret>(
255 &self,
256 index: usize,
257 mut callback: impl for<'local> FnMut(&'local Pubkey, &'local AccountSharedData) -> Ret,
258 ) -> Ret {
259 let (pubkey, account) = self.get_unchecked(index);
260 callback(pubkey, account)
261 }
262
263 fn is_zero_lamport(&self, index: usize) -> bool {
264 self.get_unchecked(index).1.lamports() == 0
265 }
266
267 fn data_len(&self, index: usize) -> usize {
268 self.get_unchecked(index).1.data().len()
269 }
270
271 fn pubkey(&self, index: usize) -> &Pubkey {
272 self.get_unchecked(index).0
273 }
274
275 fn slot(&self, _index: usize) -> Slot {
276 self.target_slot()
277 }
278
279 fn target_slot(&self) -> Slot {
280 self.slot
281 }
282
283 fn len(&self) -> usize {
284 self.epoch_boundary_accounts.accounts_with_rewards.len()
285 + self.epoch_boundary_accounts.swept_vote_accounts.len()
286 }
287}
288
289#[derive(Debug, Default)]
290pub(super) struct StakeRewardCalculation {
292 stake_rewards: Arc<PartitionedStakeRewards>,
294 total_stake_rewards_lamports: u64,
296}
297
298#[derive(Debug)]
299struct CalculateValidatorRewardsResult {
300 reward_commissions: RewardCommissions,
301 stake_reward_calculation: StakeRewardCalculation,
302 point_value: PointValue,
303}
304
305impl Default for CalculateValidatorRewardsResult {
306 fn default() -> Self {
307 Self {
308 reward_commissions: RewardCommissions::default(),
309 stake_reward_calculation: StakeRewardCalculation::default(),
310 point_value: PointValue {
311 points: 0,
312 rewards: 0,
313 },
314 }
315 }
316}
317
318pub(super) struct CachedVoteAccounts<'a> {
319 pub(super) snapshot_epoch_vote_accounts: Option<&'a VoteAccounts>,
325 pub(super) rewarded_epoch_vote_accounts: Option<&'a VoteAccounts>,
329 pub(super) distribution_epoch_vote_accounts: &'a VoteAccounts,
332}
333
334pub(super) struct EpochRewardCalculateParamInfo<'a> {
336 pub(super) stake_history: StakeHistory,
337 pub(super) stake_delegations: Vec<(&'a Pubkey, &'a StakeAccount<Delegation>)>,
338 pub(super) cached_vote_accounts: CachedVoteAccounts<'a>,
339}
340
341#[derive(Debug)]
345pub(super) struct PartitionedRewardsCalculation {
346 reward_commissions: RewardCommissions,
347 stake_rewards: StakeRewardCalculation,
348 capitalization: u64,
349 point_value: PointValue,
350 num_filtered_vote_accounts: usize,
355}
356
357pub(crate) type StakeRewards = Vec<StakeReward>;
358
359#[derive(Debug, PartialEq)]
360pub struct KeyedRewardsAndNumPartitions {
361 pub keyed_rewards: Vec<(Pubkey, RewardInfo)>,
362 pub num_partitions: Option<u64>,
363}
364
365impl KeyedRewardsAndNumPartitions {
366 pub fn should_record(&self) -> bool {
367 !self.keyed_rewards.is_empty() || self.num_partitions.is_some()
368 }
369}
370
371impl Bank {
372 pub fn get_rewards_and_num_partitions(&self) -> KeyedRewardsAndNumPartitions {
373 let keyed_rewards = self.rewards.read().unwrap().clone();
374 let epoch_rewards_sysvar = self.get_epoch_rewards_sysvar();
375 let epoch_schedule = self.epoch_schedule();
378 let parent_epoch = epoch_schedule.get_epoch(self.parent_slot());
379 let is_first_block_in_epoch = self.epoch() > parent_epoch;
380
381 let num_partitions = (epoch_rewards_sysvar.active && is_first_block_in_epoch)
382 .then_some(epoch_rewards_sysvar.num_partitions);
383 KeyedRewardsAndNumPartitions {
384 keyed_rewards,
385 num_partitions,
386 }
387 }
388
389 pub(crate) fn set_epoch_reward_status_calculation(
390 &mut self,
391 distribution_starting_block_height: u64,
392 stake_rewards: Arc<PartitionedStakeRewards>,
393 ) {
394 self.epoch_reward_status =
395 EpochRewardStatus::Active(EpochRewardPhase::Calculation(StartBlockHeightAndRewards {
396 distribution_starting_block_height,
397 all_stake_rewards: stake_rewards,
398 }));
399 }
400
401 pub(crate) fn set_epoch_reward_status_distribution(
402 &mut self,
403 distribution_starting_block_height: u64,
404 all_stake_rewards: Arc<PartitionedStakeRewards>,
405 partition_indices: Vec<Vec<usize>>,
406 ) {
407 self.epoch_reward_status = EpochRewardStatus::Active(EpochRewardPhase::Distribution(
408 StartBlockHeightAndPartitionedRewards {
409 distribution_starting_block_height,
410 all_stake_rewards,
411 partition_indices,
412 },
413 ));
414 }
415
416 pub(super) fn partitioned_rewards_stake_account_stores_per_block(&self) -> u64 {
418 self.partitioned_rewards_stake_account_stores_per_block
419 }
420
421 pub(super) fn get_reward_distribution_num_blocks(
423 &self,
424 rewards: &PartitionedStakeRewards,
425 ) -> u64 {
426 let total_stake_accounts = rewards.num_rewards();
427 if self.epoch_schedule.warmup && self.epoch < self.first_normal_epoch() {
428 1
429 } else {
430 const MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH: u64 = 10;
431 let num_chunks = total_stake_accounts
432 .div_ceil(self.partitioned_rewards_stake_account_stores_per_block() as usize)
433 as u64;
434
435 num_chunks.clamp(
437 1,
438 (self.epoch_schedule.slots_per_epoch / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH).max(1),
439 )
440 }
441 }
442
443 pub fn force_reward_interval_end_for_tests(&mut self) {
445 self.epoch_reward_status = EpochRewardStatus::Inactive;
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use {
452 super::*,
453 crate::{
454 alpenglow_epoch_type::RewardEpochDelegatedStakes,
455 bank::{
456 NewEpochBundle, RewardsMetrics, SlotLeader, null_tracer,
457 tests::create_genesis_config,
458 },
459 bank_forks::BankForks,
460 genesis_utils::{
461 GenesisConfigInfo, ValidatorVoteKeypairs, activate_all_features_alpenglow,
462 create_genesis_config_with_leader, create_genesis_config_with_vote_accounts,
463 deactivate_features,
464 },
465 runtime_config::RuntimeConfig,
466 stake_utils,
467 sysvar_account::from_account,
468 },
469 assert_matches::assert_matches,
470 rand::Rng,
471 rayon::ThreadPoolBuilder,
472 solana_account::{
473 Account, ReadableAccount, WritableAccount, state_traits::StateMutWincode as _,
474 },
475 solana_accounts_db::{
476 accounts_db::{ACCOUNTS_DB_CONFIG_FOR_TESTING, AccountsDbConfig},
477 partitioned_rewards::PartitionedEpochRewardsConfig,
478 },
479 solana_clock::Epoch,
480 solana_epoch_schedule::EpochSchedule,
481 solana_hash::Hash,
482 solana_keypair::Keypair,
483 solana_native_token::LAMPORTS_PER_SOL,
484 solana_rent::Rent,
485 solana_reward_info::RewardType,
486 solana_signer::Signer,
487 solana_stake_interface::{stake_flags::StakeFlags, state::StakeStateV2},
488 solana_system_transaction as system_transaction,
489 solana_sysvar::epoch_rewards::{self, EpochRewards},
490 solana_vote::vote_transaction,
491 solana_vote_interface::state::{MAX_LOCKOUT_HISTORY, VoteStateV4, VoteStateVersions},
492 solana_vote_program::vote_state::{self, TowerSync, handler::VoteStateHandler},
493 std::{
494 collections::HashSet,
495 sync::{Arc, RwLock},
496 },
497 };
498
499 impl PartitionedStakeReward {
500 pub fn new_random() -> Self {
501 let mut rng = rand::rng();
502 let stake_reward = rng.random_range(1..200);
503 Self {
504 stake_pubkey: Pubkey::new_unique(),
505 inflation: InflationReward {
506 stake: Stake {
507 delegation: Delegation {
508 voter_pubkey: Pubkey::new_unique(),
509 stake: rng.random_range(1..200) + stake_reward,
510 activation_epoch: 0,
511 deactivation_epoch: u64::MAX,
512 ..Default::default()
513 },
514 credits_observed: rng.random_range(1..200),
515 },
516 stake_reward,
517 commission_bps: None,
518 },
519 block_reward: rng.random_range(0..10_000_000_000),
520 }
521 }
522
523 pub fn new_with_lamport_amounts(stake_reward: u64, block_reward: u64, stake: u64) -> Self {
524 Self {
525 stake_pubkey: Pubkey::new_unique(),
526 inflation: InflationReward {
527 stake: Stake {
528 delegation: Delegation {
529 voter_pubkey: Pubkey::new_unique(),
530 stake: stake + stake_reward,
531 activation_epoch: 0,
532 deactivation_epoch: u64::MAX,
533 ..Default::default()
534 },
535 credits_observed: 0,
536 },
537 stake_reward,
538 commission_bps: None,
539 },
540 block_reward,
541 }
542 }
543 }
544
545 pub fn build_partitioned_stake_rewards(
546 stake_rewards: &PartitionedStakeRewards,
547 partition_indices: &[Vec<usize>],
548 ) -> Vec<PartitionedStakeRewards> {
549 partition_indices
550 .iter()
551 .map(|partition_index| {
552 partition_index
555 .iter()
556 .map(|&index| stake_rewards.get(index).unwrap().clone())
557 .collect::<PartitionedStakeRewards>()
558 })
559 .collect::<Vec<_>>()
560 }
561
562 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
563 enum RewardInterval {
564 InsideInterval,
566 OutsideInterval,
568 }
569
570 impl Bank {
571 fn get_reward_interval(&self) -> RewardInterval {
573 if matches!(self.epoch_reward_status, EpochRewardStatus::Active(_)) {
574 RewardInterval::InsideInterval
575 } else {
576 RewardInterval::OutsideInterval
577 }
578 }
579
580 fn is_calculated(&self) -> bool {
581 matches!(
582 self.epoch_reward_status,
583 EpochRewardStatus::Active(EpochRewardPhase::Calculation(_))
584 )
585 }
586
587 fn is_partitioned(&self) -> bool {
588 matches!(
589 self.epoch_reward_status,
590 EpochRewardStatus::Active(EpochRewardPhase::Distribution(_))
591 )
592 }
593
594 fn get_epoch_rewards_from_cache(
595 &self,
596 parent_hash: &Hash,
597 ) -> Option<Arc<PartitionedRewardsCalculation>> {
598 self.epoch_rewards_calculation_cache
599 .lock()
600 .unwrap()
601 .get(parent_hash)
602 .cloned()
603 }
604
605 fn get_epoch_rewards_cache_len(&self) -> usize {
606 self.epoch_rewards_calculation_cache.lock().unwrap().len()
607 }
608 }
609
610 pub(super) const SLOTS_PER_EPOCH: u64 = 32;
611
612 pub(super) struct RewardBank {
613 pub(super) bank: Arc<Bank>,
614 pub(super) voters: Vec<Pubkey>,
615 pub(super) stakers: Vec<Pubkey>,
616 }
617
618 pub(super) fn create_default_reward_bank(
620 expected_num_delegations: usize,
621 advance_num_slots: u64,
622 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
623 create_reward_bank(
624 expected_num_delegations,
625 PartitionedEpochRewardsConfig::default().stake_account_stores_per_block,
626 advance_num_slots,
627 )
628 }
629
630 pub(super) fn create_reward_bank(
631 expected_num_delegations: usize,
632 stake_account_stores_per_block: u64,
633 advance_num_slots: u64,
634 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
635 create_reward_bank_with_specific_stakes(
636 vec![2_000_000_000; expected_num_delegations],
637 stake_account_stores_per_block,
638 advance_num_slots,
639 )
640 }
641
642 pub(super) fn create_reward_bank_with_specific_stakes(
643 stakes: Vec<u64>,
644 stake_account_stores_per_block: u64,
645 advance_num_slots: u64,
646 ) -> (RewardBank, Arc<RwLock<BankForks>>) {
647 let features_to_deactivate = crate::slot_params::slot_time_feature_ids().to_vec();
650 let validator_keypairs = (0..stakes.len())
651 .map(|_| ValidatorVoteKeypairs::new_rand())
652 .collect::<Vec<_>>();
653
654 let GenesisConfigInfo {
655 mut genesis_config, ..
656 } = create_genesis_config_with_vote_accounts(1_000_000_000, &validator_keypairs, stakes);
657 genesis_config.epoch_schedule = EpochSchedule::new(SLOTS_PER_EPOCH);
658 deactivate_features(&mut genesis_config, &features_to_deactivate);
659
660 let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING;
661 accounts_db_config.partitioned_epoch_rewards_config =
662 PartitionedEpochRewardsConfig::new_for_test(stake_account_stores_per_block);
663
664 let bank = Bank::new_from_genesis(
665 &genesis_config,
666 Arc::new(RuntimeConfig::default()),
667 Vec::new(),
668 None,
669 accounts_db_config,
670 None,
671 None,
672 Arc::default(),
673 None,
674 None,
675 );
676
677 populate_vote_accounts_with_votes(
680 &bank,
681 validator_keypairs.iter().map(|k| k.vote_keypair.pubkey()),
682 0,
683 );
684
685 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
688 let bank = Bank::new_from_parent_with_bank_forks(
689 &bank_forks,
690 bank,
691 SlotLeader::default(),
692 advance_num_slots,
693 );
694
695 (
696 RewardBank {
697 bank,
698 voters: validator_keypairs
699 .iter()
700 .map(|k| k.vote_keypair.pubkey())
701 .collect(),
702 stakers: validator_keypairs
703 .iter()
704 .map(|k| k.stake_keypair.pubkey())
705 .collect(),
706 },
707 bank_forks,
708 )
709 }
710
711 pub(super) fn modify_vote_state(
712 bank: &Bank,
713 vote_address: &Pubkey,
714 modify_fn: &dyn Fn(&mut VoteStateV4),
715 ) {
716 let mut vote_account = bank.get_account_with_fixed_root(vote_address).unwrap();
717 let vote_state_versions: VoteStateVersions = vote_account.state().unwrap();
718 let VoteStateVersions::V4(mut vote_state) = vote_state_versions else {
719 panic!("unexpected version");
720 };
721
722 modify_fn(&mut vote_state);
723
724 vote_account
725 .set_state(&VoteStateVersions::V4(vote_state))
726 .unwrap();
727 bank.store_account(vote_address, &vote_account);
728 }
729
730 pub(super) fn add_vote_and_stake_accounts(
731 bank: &Bank,
732 voters: &mut HashSet<Pubkey>,
733 stakers: &mut HashSet<Pubkey>,
734 stake_lamports: u64,
735 vote_lamports: u64,
736 ) -> Pubkey {
737 add_vote_and_stake_accounts_with_activation_and_deactivation_epochs(
738 bank,
739 voters,
740 stakers,
741 stake_lamports,
742 vote_lamports,
743 Epoch::MAX,
744 Epoch::MAX,
745 )
746 }
747
748 pub(super) fn add_vote_and_stake_accounts_with_activation_and_deactivation_epochs(
749 bank: &Bank,
750 voters: &mut HashSet<Pubkey>,
751 stakers: &mut HashSet<Pubkey>,
752 stake_lamports: u64,
753 vote_lamports: u64,
754 activation_epoch: Epoch,
755 deactivation_epoch: Epoch,
756 ) -> Pubkey {
757 let ((vote_address, mut vote_account), (stake_address, stake_account)) =
758 crate::stakes::tests::create_staked_node_accounts_with_activation_and_deactivation_epochs(
759 stake_lamports,
760 &bank.rent_collector.rent,
761 activation_epoch,
762 deactivation_epoch,
763 );
764 vote_account.checked_add_lamports(vote_lamports).unwrap();
765 bank.store_account_and_update_capitalization(&vote_address, &vote_account);
766 bank.store_account_and_update_capitalization(&stake_address, &stake_account);
767 voters.insert(vote_address);
768 stakers.insert(stake_address);
769
770 vote_address
771 }
772
773 #[derive(Debug)]
776 pub(super) struct BlockRevenueSharingCases {
777 pub(super) commission_collector: Pubkey,
779 pub(super) self_collector: Pubkey,
781 pub(super) external_collector: Pubkey,
783 pub(super) zero_stake: Pubkey,
785 pub(super) non_vat_self_collector: Pubkey,
788 pub(super) non_vat_external_collector: Pubkey,
791 pub(super) deactivating: Pubkey,
793 pub(super) activating: Pubkey,
798 }
799
800 impl BlockRevenueSharingCases {
801 fn distributed(&self) -> [Pubkey; 2] {
807 [self.self_collector, self.external_collector]
808 }
809
810 fn swept(&self) -> [Pubkey; 6] {
814 [
815 self.self_collector,
816 self.external_collector,
817 self.zero_stake,
818 self.non_vat_self_collector,
819 self.non_vat_external_collector,
820 self.deactivating,
821 ]
822 }
823
824 fn burned(&self) -> [Pubkey; 4] {
827 [
828 self.zero_stake,
829 self.non_vat_self_collector,
830 self.non_vat_external_collector,
831 self.deactivating,
832 ]
833 }
834 }
835
836 pub(super) fn create_reward_bank_for_block_revenue_sharing(
837 stake_lamports: u64,
838 vote_lamports: u64,
839 pending_delegator_rewards: u64,
840 stake_account_stores_per_block: u64,
841 ) -> (RewardBank, Arc<RwLock<BankForks>>, BlockRevenueSharingCases) {
842 let GenesisConfigInfo {
843 mut genesis_config, ..
844 } = create_genesis_config_with_leader(
845 1_000_000 * LAMPORTS_PER_SOL,
846 &Pubkey::new_unique(),
847 42 * LAMPORTS_PER_SOL,
848 );
849 activate_all_features_alpenglow(&mut genesis_config);
850
851 let features_to_deactivate = crate::slot_params::slot_time_feature_ids().to_vec();
854 deactivate_features(&mut genesis_config, &features_to_deactivate);
855
856 genesis_config.rent = Rent::default();
857 genesis_config.epoch_schedule = EpochSchedule::without_warmup();
858 let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING;
859 accounts_db_config.partitioned_epoch_rewards_config =
860 PartitionedEpochRewardsConfig::new_for_test(stake_account_stores_per_block);
861
862 let bank = Bank::new_from_genesis(
863 &genesis_config,
864 Arc::new(RuntimeConfig::default()),
865 Vec::new(),
866 None,
867 accounts_db_config,
868 None,
869 None,
870 Arc::default(),
871 None,
872 None,
873 );
874
875 let commission_collector = Pubkey::new_unique();
876 {
877 let mut commission_account = AccountSharedData::default();
878 commission_account.set_lamports(genesis_config.rent.minimum_balance(0));
879 bank.store_account_and_update_capitalization(
880 &commission_collector,
881 &commission_account,
882 );
883 }
884
885 let mut voters = HashSet::new();
886 let mut stakers = HashSet::new();
887
888 let self_collector = add_vote_and_stake_accounts(
890 &bank,
891 &mut voters,
892 &mut stakers,
893 stake_lamports,
894 vote_lamports + pending_delegator_rewards,
895 );
896 modify_vote_state(&bank, &self_collector, &|vote_state| {
897 vote_state.pending_delegator_rewards = pending_delegator_rewards;
898 });
899
900 let external_collector = add_vote_and_stake_accounts(
902 &bank,
903 &mut voters,
904 &mut stakers,
905 stake_lamports,
906 vote_lamports + pending_delegator_rewards,
907 );
908 modify_vote_state(&bank, &external_collector, &|vote_state| {
909 vote_state.pending_delegator_rewards = pending_delegator_rewards;
910 vote_state.inflation_rewards_collector = commission_collector;
911 });
912
913 let zero_stake = add_vote_and_stake_accounts(
915 &bank,
916 &mut voters,
917 &mut stakers,
918 0,
919 vote_lamports + pending_delegator_rewards,
920 );
921 modify_vote_state(&bank, &zero_stake, &|vote_state| {
922 vote_state.pending_delegator_rewards = pending_delegator_rewards;
923 });
924
925 let non_vat_self_collector = add_vote_and_stake_accounts(
927 &bank,
928 &mut voters,
929 &mut stakers,
930 stake_lamports,
931 pending_delegator_rewards,
932 );
933 modify_vote_state(&bank, &non_vat_self_collector, &|vote_state| {
934 vote_state.pending_delegator_rewards = pending_delegator_rewards;
935 });
936
937 let non_vat_external_collector = add_vote_and_stake_accounts(
939 &bank,
940 &mut voters,
941 &mut stakers,
942 stake_lamports,
943 pending_delegator_rewards,
944 );
945 modify_vote_state(&bank, &non_vat_external_collector, &|vote_state| {
946 vote_state.pending_delegator_rewards = pending_delegator_rewards;
947 vote_state.inflation_rewards_collector = commission_collector;
948 });
949
950 let activating = add_vote_and_stake_accounts_with_activation_and_deactivation_epochs(
952 &bank,
953 &mut voters,
954 &mut stakers,
955 stake_lamports,
956 vote_lamports + pending_delegator_rewards,
957 bank.epoch(),
958 Epoch::MAX,
959 );
960 modify_vote_state(&bank, &activating, &|vote_state| {
961 vote_state.pending_delegator_rewards = pending_delegator_rewards;
962 });
963
964 let deactivating = add_vote_and_stake_accounts_with_activation_and_deactivation_epochs(
966 &bank,
967 &mut voters,
968 &mut stakers,
969 stake_lamports,
970 vote_lamports + pending_delegator_rewards,
971 Epoch::MAX,
972 bank.epoch(),
973 );
974 modify_vote_state(&bank, &deactivating, &|vote_state| {
975 vote_state.pending_delegator_rewards = pending_delegator_rewards;
976 });
977
978 let commission = 0;
980 populate_vote_accounts_with_votes(&bank, voters.iter().copied(), commission);
981
982 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
984 let slots_per_epoch = bank.epoch_schedule().slots_per_epoch;
985 let bank = Bank::new_from_parent_with_bank_forks(
986 &bank_forks,
987 bank,
988 SlotLeader::default(),
989 slots_per_epoch - 1,
990 );
991
992 (
993 RewardBank {
994 bank,
995 voters: voters.into_iter().collect(),
996 stakers: stakers.into_iter().collect(),
997 },
998 bank_forks,
999 BlockRevenueSharingCases {
1000 commission_collector,
1001 self_collector,
1002 external_collector,
1003 zero_stake,
1004 non_vat_self_collector,
1005 non_vat_external_collector,
1006 deactivating,
1007 activating,
1008 },
1009 )
1010 }
1011
1012 pub(super) fn populate_vote_accounts_with_votes(
1013 bank: &Bank,
1014 vote_pubkeys: impl IntoIterator<Item = Pubkey>,
1015 commission: u8,
1016 ) {
1017 for vote_pubkey in vote_pubkeys {
1018 let mut vote_account = bank
1019 .get_account(&vote_pubkey)
1020 .unwrap_or_else(|| panic!("missing vote account {vote_pubkey:?}"));
1021 let mut vote_state = VoteStateHandler::new_v4(
1022 VoteStateV4::deserialize(vote_account.data(), &vote_pubkey).unwrap(),
1023 );
1024 vote_state.set_commission(commission);
1025 for i in 0..MAX_LOCKOUT_HISTORY + 42 {
1026 vote_state::process_slot_vote_unchecked(&mut vote_state, i as u64);
1027 let versioned = VoteStateVersions::V4(Box::new(vote_state.as_ref_v4().clone()));
1028 vote_account.set_state(&versioned).unwrap();
1029 }
1030 bank.store_account_and_update_capitalization(&vote_pubkey, &vote_account);
1031 }
1032 }
1033
1034 #[test]
1035 fn test_force_reward_interval_end() {
1036 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1037 let mut bank = Bank::new_for_tests(&genesis_config);
1038
1039 let expected_num = 100;
1040
1041 let stake_rewards = (0..expected_num)
1042 .map(|_| Some(PartitionedStakeReward::new_random()))
1043 .collect::<PartitionedStakeRewards>();
1044
1045 let partition_indices = vec![(0..expected_num).collect()];
1046
1047 bank.set_epoch_reward_status_distribution(
1048 bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
1049 Arc::new(stake_rewards),
1050 partition_indices,
1051 );
1052 assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
1053
1054 bank.force_reward_interval_end_for_tests();
1055 assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
1056 }
1057
1058 #[test]
1061 fn test_get_reward_distribution_num_blocks_cap() {
1062 let (mut genesis_config, _mint_keypair) =
1063 create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1064 genesis_config.epoch_schedule = EpochSchedule::custom(32, 32, false);
1065
1066 let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING;
1068 accounts_db_config.partitioned_epoch_rewards_config =
1069 PartitionedEpochRewardsConfig::new_for_test(10);
1070
1071 let bank = Bank::new_from_genesis(
1072 &genesis_config,
1073 Arc::new(RuntimeConfig::default()),
1074 Vec::new(),
1075 None,
1076 accounts_db_config,
1077 None,
1078 Some(SlotLeader::new_unique()),
1079 Arc::default(),
1080 None,
1081 None,
1082 );
1083
1084 let stake_account_stores_per_block =
1085 bank.partitioned_rewards_stake_account_stores_per_block();
1086 assert_eq!(stake_account_stores_per_block, 10);
1087
1088 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
1089 let bank =
1090 Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 1);
1091 assert_eq!(
1092 bank.partitioned_rewards_stake_account_stores_per_block(),
1093 stake_account_stores_per_block
1094 );
1095
1096 let check_num_reward_distribution_blocks =
1097 |num_stakes: u64, expected_num_reward_distribution_blocks: u64| {
1098 let stake_rewards = (0..num_stakes)
1100 .map(|_| Some(PartitionedStakeReward::new_random()))
1101 .collect::<PartitionedStakeRewards>();
1102
1103 assert_eq!(
1104 bank.get_reward_distribution_num_blocks(&stake_rewards),
1105 expected_num_reward_distribution_blocks
1106 );
1107 };
1108
1109 for test_record in [
1110 (0, 1),
1112 (1, 1),
1113 (stake_account_stores_per_block, 1),
1114 (2 * stake_account_stores_per_block - 1, 2),
1115 (2 * stake_account_stores_per_block, 2),
1116 (3 * stake_account_stores_per_block - 1, 3),
1117 (3 * stake_account_stores_per_block, 3),
1118 (4 * stake_account_stores_per_block, 3), (5 * stake_account_stores_per_block, 3), ] {
1121 check_num_reward_distribution_blocks(test_record.0, test_record.1);
1122 }
1123 }
1124
1125 #[test]
1127 fn test_get_reward_distribution_num_blocks_normal() {
1128 agave_logger::setup();
1129 let (mut genesis_config, _mint_keypair) =
1130 create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1131 genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
1132
1133 let bank = Bank::new_for_tests(&genesis_config);
1134
1135 let expected_num = 8192;
1137 let stake_rewards = (0..expected_num)
1138 .map(|_| Some(PartitionedStakeReward::new_random()))
1139 .collect::<PartitionedStakeRewards>();
1140
1141 assert_eq!(bank.get_reward_distribution_num_blocks(&stake_rewards), 2);
1142 }
1143
1144 #[test]
1147 fn test_get_reward_distribution_num_blocks_warmup() {
1148 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1149
1150 let bank = Bank::new_for_tests(&genesis_config);
1151 let rewards = PartitionedStakeRewards::default();
1152 assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
1153 }
1154
1155 #[test]
1161 fn test_get_reward_distribution_num_blocks_none() {
1162 let rewards_all = 8192;
1163 let expected_rewards_some = 6144;
1164
1165 let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
1166 let bank = Bank::new_for_tests(&genesis_config);
1167
1168 let rewards = (0..rewards_all)
1169 .map(|i| {
1170 if i % 4 == 0 {
1171 None
1172 } else {
1173 Some(PartitionedStakeReward::new_random())
1174 }
1175 })
1176 .collect::<PartitionedStakeRewards>();
1177 assert_eq!(rewards.rewards.len(), rewards_all);
1178 assert_eq!(rewards.num_rewards(), expected_rewards_some);
1179
1180 assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
1181 }
1182
1183 #[test]
1184 fn test_rewards_computation_and_partitioned_distribution_one_block() {
1185 agave_logger::setup();
1186
1187 let starting_slot = SLOTS_PER_EPOCH - 1;
1188 let (
1189 RewardBank {
1190 bank: mut previous_bank,
1191 ..
1192 },
1193 bank_forks,
1194 ) = create_default_reward_bank(100, starting_slot - 1);
1195
1196 for slot in starting_slot..=(2 * SLOTS_PER_EPOCH) + 2 {
1198 let pre_cap = previous_bank.capitalization();
1199 let curr_bank = Bank::new_from_parent_with_bank_forks(
1200 bank_forks.as_ref(),
1201 previous_bank.clone(),
1202 SlotLeader::default(),
1203 slot,
1204 );
1205 let post_cap = curr_bank.capitalization();
1206
1207 if slot % SLOTS_PER_EPOCH == 0 {
1208 assert_matches!(
1211 curr_bank.get_reward_interval(),
1212 RewardInterval::InsideInterval
1213 );
1214
1215 assert!(curr_bank.is_calculated());
1216
1217 assert!(
1219 curr_bank
1220 .get_epoch_rewards_from_cache(&curr_bank.parent_hash)
1221 .is_some()
1222 );
1223 assert_eq!(post_cap, pre_cap);
1224
1225 let _ = bank_forks.write().unwrap().set_root(slot, None, None);
1228 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 0);
1229 } else if slot == SLOTS_PER_EPOCH + 1 {
1230 assert_matches!(
1236 curr_bank.get_reward_interval(),
1237 RewardInterval::OutsideInterval
1238 );
1239 let account = curr_bank
1240 .get_account(&solana_sysvar::epoch_rewards::id())
1241 .unwrap();
1242 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
1243 from_account(&account).unwrap();
1244 assert_eq!(post_cap, pre_cap + epoch_rewards.distributed_rewards);
1245 } else {
1246 assert_matches!(
1251 curr_bank.get_reward_interval(),
1252 RewardInterval::OutsideInterval
1253 );
1254
1255 assert_eq!(post_cap, pre_cap);
1257 }
1258 if slot >= SLOTS_PER_EPOCH {
1261 let epoch_rewards_lamports =
1262 curr_bank.get_balance(&solana_sysvar::epoch_rewards::id());
1263 assert!(epoch_rewards_lamports > 0);
1264 }
1265 previous_bank = curr_bank;
1266 }
1267 }
1268
1269 #[test]
1272 fn test_rewards_computation_and_partitioned_distribution_multi_blocks() {
1273 agave_logger::setup();
1274
1275 let starting_slot = SLOTS_PER_EPOCH - 1;
1276 let (
1277 RewardBank {
1278 bank: mut previous_bank,
1279 ..
1280 },
1281 bank_forks,
1282 ) = create_reward_bank(100, 50, starting_slot - 1);
1283 let mut starting_hash = None;
1284 let mut reward_distribution_completion_slot = None;
1285
1286 for slot in starting_slot..=SLOTS_PER_EPOCH.saturating_mul(2) {
1288 let pre_cap = previous_bank.capitalization();
1289
1290 let pre_sysvar_account = previous_bank
1291 .get_account(&solana_sysvar::epoch_rewards::id())
1292 .unwrap_or_default();
1293 let pre_epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
1294 from_account(&pre_sysvar_account).unwrap_or_default();
1295 let pre_distributed_rewards = pre_epoch_rewards.distributed_rewards;
1296 let curr_bank = Bank::new_from_parent_with_bank_forks(
1297 bank_forks.as_ref(),
1298 previous_bank.clone(),
1299 SlotLeader::default(),
1300 slot,
1301 );
1302 let post_cap = curr_bank.capitalization();
1303
1304 if slot < SLOTS_PER_EPOCH {
1305 assert_matches!(
1307 curr_bank.get_reward_interval(),
1308 RewardInterval::OutsideInterval
1309 );
1310 assert_eq!(post_cap, pre_cap);
1311 } else if slot == SLOTS_PER_EPOCH {
1312 assert_matches!(
1315 curr_bank.get_reward_interval(),
1316 RewardInterval::InsideInterval
1317 );
1318
1319 assert!(curr_bank.is_calculated());
1321
1322 assert!(
1324 curr_bank
1325 .get_epoch_rewards_from_cache(&curr_bank.parent_hash)
1326 .is_some()
1327 );
1328 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 1);
1329 starting_hash = Some(curr_bank.parent_hash);
1330
1331 let account = curr_bank
1334 .get_account(&solana_sysvar::epoch_rewards::id())
1335 .unwrap();
1336 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
1337 from_account(&account).unwrap();
1338 reward_distribution_completion_slot =
1339 Some(SLOTS_PER_EPOCH + epoch_rewards.num_partitions);
1340 } else if slot
1341 < reward_distribution_completion_slot
1342 .expect("epoch boundary must set completion slot")
1343 {
1344 assert_matches!(
1346 curr_bank.get_reward_interval(),
1347 RewardInterval::InsideInterval
1348 );
1349 assert!(curr_bank.is_partitioned());
1350
1351 let account = curr_bank
1352 .get_account(&solana_sysvar::epoch_rewards::id())
1353 .unwrap();
1354 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
1355 from_account(&account).unwrap();
1356 assert_eq!(
1357 post_cap,
1358 pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards
1359 );
1360
1361 if slot == SLOTS_PER_EPOCH + 1 {
1362 assert!(
1365 curr_bank
1366 .get_epoch_rewards_from_cache(&starting_hash.unwrap())
1367 .is_some()
1368 );
1369 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 1);
1370
1371 let _ = bank_forks.write().unwrap().set_root(slot - 1, None, None);
1374 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 0);
1375 }
1376 } else if slot
1377 == reward_distribution_completion_slot
1378 .expect("epoch boundary must set completion slot")
1379 {
1380 assert_matches!(
1384 curr_bank.get_reward_interval(),
1385 RewardInterval::OutsideInterval
1386 );
1387
1388 let account = curr_bank
1389 .get_account(&solana_sysvar::epoch_rewards::id())
1390 .unwrap();
1391 let epoch_rewards: solana_sysvar::epoch_rewards::EpochRewards =
1392 from_account(&account).unwrap();
1393 assert_eq!(
1394 post_cap,
1395 pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards
1396 );
1397 } else {
1398 assert_matches!(
1401 curr_bank.get_reward_interval(),
1402 RewardInterval::OutsideInterval
1403 );
1404 assert_eq!(post_cap, pre_cap);
1405 break;
1406 }
1407 previous_bank = curr_bank;
1408 }
1409 }
1410
1411 #[test]
1413 fn test_rewards_period_system_transfer() {
1414 let validator_vote_keypairs = ValidatorVoteKeypairs::new_rand();
1415 let validator_keypairs = vec![&validator_vote_keypairs];
1416 let GenesisConfigInfo {
1417 mut genesis_config,
1418 mint_keypair,
1419 ..
1420 } = create_genesis_config_with_vote_accounts(
1421 1_000_000_000,
1422 &validator_keypairs,
1423 vec![1_000_000_000; 1],
1424 );
1425
1426 let vote_key = validator_keypairs[0].vote_keypair.pubkey();
1428 let vote_account = genesis_config
1429 .accounts
1430 .iter()
1431 .find(|(address, _)| **address == vote_key)
1432 .map(|(_, account)| account)
1433 .unwrap()
1434 .clone();
1435
1436 let new_stake_signer = Keypair::new();
1437 let new_stake_address = new_stake_signer.pubkey();
1438 let new_stake_account = Account::from(stake_utils::create_stake_account(
1439 &new_stake_address,
1440 &vote_key,
1441 &vote_account.into(),
1442 &genesis_config.rent,
1443 2_000_000_000,
1444 ));
1445 genesis_config
1446 .accounts
1447 .extend(vec![(new_stake_address, new_stake_account)]);
1448
1449 let (mut previous_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
1450 let num_slots_in_epoch = previous_bank.get_slots_in_epoch(previous_bank.epoch());
1451 assert_eq!(num_slots_in_epoch, 32);
1452
1453 let transfer_amount = 5_000;
1454
1455 for slot in 1..=num_slots_in_epoch + 2 {
1456 let bank = Bank::new_from_parent_with_bank_forks(
1457 bank_forks.as_ref(),
1458 previous_bank.clone(),
1459 SlotLeader::default(),
1460 slot,
1461 );
1462
1463 let tower_sync = TowerSync::new_from_slot(slot - 1, previous_bank.hash());
1466 let vote = vote_transaction::new_tower_sync_transaction(
1467 tower_sync,
1468 previous_bank.last_blockhash(),
1469 &validator_vote_keypairs.node_keypair,
1470 &validator_vote_keypairs.vote_keypair,
1471 &validator_vote_keypairs.vote_keypair,
1472 None,
1473 );
1474 bank.process_transaction(&vote).unwrap();
1475
1476 let system_tx = system_transaction::transfer(
1478 &mint_keypair,
1479 &new_stake_address,
1480 transfer_amount,
1481 bank.last_blockhash(),
1482 );
1483 let system_result = bank.process_transaction(&system_tx);
1484
1485 assert!(system_result.is_ok());
1487
1488 bank.register_unique_recent_blockhash_for_test();
1492 previous_bank = bank;
1493 }
1494 }
1495
1496 #[test]
1497 fn test_get_rewards_and_partitions() {
1498 let starting_slot = SLOTS_PER_EPOCH - 1;
1499 let num_rewards = 100;
1500 let stake_account_stores_per_block = 50;
1501 let (RewardBank { bank, .. }, bank_forks) =
1502 create_reward_bank(num_rewards, stake_account_stores_per_block, starting_slot);
1503
1504 assert_eq!(
1507 bank.get_rewards_and_num_partitions(),
1508 KeyedRewardsAndNumPartitions {
1509 keyed_rewards: vec![],
1510 num_partitions: None,
1511 }
1512 );
1513
1514 let epoch_boundary_bank = Bank::new_from_parent_with_bank_forks(
1515 bank_forks.as_ref(),
1516 bank,
1517 SlotLeader::default(),
1518 SLOTS_PER_EPOCH,
1519 );
1520 let KeyedRewardsAndNumPartitions {
1522 keyed_rewards,
1523 num_partitions,
1524 } = epoch_boundary_bank.get_rewards_and_num_partitions();
1525 for (_pubkey, reward) in keyed_rewards.iter() {
1526 assert_eq!(reward.reward_type, RewardType::Voting);
1527 }
1528 assert_eq!(keyed_rewards.len(), num_rewards);
1529 let expected_num_partitions = (num_rewards as u64)
1530 .div_ceil(epoch_boundary_bank.partitioned_rewards_stake_account_stores_per_block())
1531 .clamp(1, (SLOTS_PER_EPOCH / 10).max(1));
1532 assert_eq!(num_partitions, Some(expected_num_partitions));
1533
1534 let mut total_staking_rewards = 0;
1535 let mut previous_bank = epoch_boundary_bank;
1536 for partition_index in 0..expected_num_partitions {
1537 let partition_bank = Arc::new(Bank::new_from_parent(
1538 previous_bank,
1539 SlotLeader::default(),
1540 SLOTS_PER_EPOCH + partition_index + 1,
1541 ));
1542 let KeyedRewardsAndNumPartitions {
1546 keyed_rewards,
1547 num_partitions,
1548 } = partition_bank.get_rewards_and_num_partitions();
1549 for (_pubkey, reward) in keyed_rewards.iter() {
1550 assert_eq!(reward.reward_type, RewardType::Staking);
1551 }
1552 total_staking_rewards += keyed_rewards.len();
1553 assert_eq!(num_partitions, None);
1554 previous_bank = partition_bank;
1555 }
1556
1557 assert_eq!(total_staking_rewards, num_rewards);
1559 let bank = Bank::new_from_parent(
1560 previous_bank,
1561 SlotLeader::default(),
1562 SLOTS_PER_EPOCH + expected_num_partitions + 1,
1563 );
1564 assert_eq!(
1567 bank.get_rewards_and_num_partitions(),
1568 KeyedRewardsAndNumPartitions {
1569 keyed_rewards: vec![],
1570 num_partitions: None,
1571 }
1572 );
1573 }
1574
1575 #[test]
1576 fn test_rewards_and_partitions_should_record() {
1577 let reward = RewardInfo {
1578 reward_type: RewardType::Voting,
1579 lamports: 55,
1580 post_balance: 5555,
1581 commission_bps: Some(500),
1582 };
1583
1584 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1585 keyed_rewards: vec![],
1586 num_partitions: None,
1587 };
1588 assert!(!rewards_and_partitions.should_record());
1589
1590 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1591 keyed_rewards: vec![(Pubkey::new_unique(), reward)],
1592 num_partitions: None,
1593 };
1594 assert!(rewards_and_partitions.should_record());
1595
1596 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1597 keyed_rewards: vec![],
1598 num_partitions: Some(42),
1599 };
1600 assert!(rewards_and_partitions.should_record());
1601
1602 let rewards_and_partitions = KeyedRewardsAndNumPartitions {
1603 keyed_rewards: vec![(Pubkey::new_unique(), reward)],
1604 num_partitions: Some(42),
1605 };
1606 assert!(rewards_and_partitions.should_record());
1607 }
1608
1609 #[test]
1610 fn test_reward_epoch_delegated_stakes_excludes_vat() {
1611 let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
1612 activate_all_features_alpenglow(&mut genesis_config);
1613 let bank = Bank::new_for_tests(&genesis_config);
1614 let first_normal_slot = bank.epoch_schedule().first_normal_slot;
1615 let slots_per_epoch = bank.epoch_schedule().slots_per_epoch;
1616
1617 let stake_amount = 10_000;
1618 {
1619 let stakes = bank.stakes_cache.stakes();
1620 assert!(stakes.vote_accounts().as_ref().is_empty());
1621 assert!(stakes.stake_delegations().is_empty());
1622 }
1623
1624 let ((non_vat_vote_address, non_vat_vote_account), (stake_address, stake_account)) =
1625 crate::stakes::tests::create_staked_node_accounts(
1626 stake_amount,
1627 &bank.rent_collector.rent,
1628 );
1629
1630 bank.store_account(&non_vat_vote_address, &non_vat_vote_account);
1631 bank.store_account(&stake_address, &stake_account);
1632
1633 let ((vat_vote_address, mut vat_vote_account), (stake_address, stake_account)) =
1634 crate::stakes::tests::create_staked_node_accounts(
1635 stake_amount,
1636 &bank.rent_collector.rent,
1637 );
1638 vat_vote_account.set_lamports(bank.vat_to_burn_per_epoch() * 5);
1639
1640 bank.store_account(&vat_vote_address, &vat_vote_account);
1641 bank.store_account(&stake_address, &stake_account);
1642
1643 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
1645 let bank = Bank::new_from_parent_with_bank_forks(
1646 &bank_forks,
1647 bank,
1648 SlotLeader::default(),
1649 first_normal_slot,
1650 );
1651 drop(bank_forks); let mut bank = Arc::into_inner(bank).unwrap();
1653
1654 let (
1655 (unstaked_vat_vote_address, mut unstaked_vat_vote_account),
1656 (stake_address, mut stake_account),
1657 ) = crate::stakes::tests::create_staked_node_accounts(
1658 stake_amount,
1659 &bank.rent_collector.rent,
1660 );
1661 unstaked_vat_vote_account.set_lamports(bank.vat_to_burn_per_epoch() * 5);
1662 {
1663 let state: StakeStateV2 = stake_account.state().unwrap();
1666 let meta = state.meta().unwrap();
1667 let mut stake = state.stake().unwrap();
1668 stake.delegation.activation_epoch = bank.epoch();
1669
1670 stake_account
1671 .set_state(&StakeStateV2::Stake(meta, stake, StakeFlags::empty()))
1672 .expect("set_state");
1673 }
1674 bank.store_account(&unstaked_vat_vote_address, &unstaked_vat_vote_account);
1675 bank.store_account(&stake_address, &stake_account);
1676
1677 {
1678 let stakes = bank.stakes_cache.stakes();
1679 assert_eq!(stakes.vote_accounts().len(), 3);
1680 assert_eq!(stakes.stake_delegations().len(), 3);
1681 }
1682
1683 bank.slot = bank.slot() + slots_per_epoch;
1685 bank.epoch += 1;
1686
1687 let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
1689 let mut rewards_metrics = RewardsMetrics::default();
1690 let NewEpochBundle {
1691 stake_history: _,
1692 unfiltered_distribution_vote_accounts,
1693 delegated_stakes: _,
1694 reward_epoch_delegated_stakes,
1695 inert_stake_delegations: _,
1696 filtered_distribution_vote_accounts,
1697 rewards_calculation: _,
1698 calculate_activated_stake_time_us: _,
1699 update_rewards_with_thread_pool_time_us: _,
1700 } = bank.compute_new_epoch_caches_and_rewards(
1701 &thread_pool,
1702 bank.epoch() - 1,
1703 null_tracer(),
1704 &mut rewards_metrics,
1705 );
1706 unfiltered_distribution_vote_accounts
1707 .get(&non_vat_vote_address)
1708 .unwrap();
1709 unfiltered_distribution_vote_accounts
1710 .get(&vat_vote_address)
1711 .unwrap();
1712 unfiltered_distribution_vote_accounts
1713 .get(&unstaked_vat_vote_address)
1714 .unwrap();
1715
1716 assert!(
1717 filtered_distribution_vote_accounts
1718 .get(&non_vat_vote_address)
1719 .is_none()
1720 );
1721 filtered_distribution_vote_accounts
1722 .get(&unstaked_vat_vote_address)
1723 .unwrap();
1724 filtered_distribution_vote_accounts
1725 .get(&vat_vote_address)
1726 .unwrap();
1727
1728 assert!(
1730 !reward_epoch_delegated_stakes
1731 .delegated_stakes
1732 .contains_key(&non_vat_vote_address)
1733 );
1734 assert!(
1735 reward_epoch_delegated_stakes
1736 .delegated_stakes
1737 .contains_key(&vat_vote_address)
1738 );
1739
1740 assert_eq!(
1744 *reward_epoch_delegated_stakes
1745 .delegated_stakes
1746 .get(&unstaked_vat_vote_address)
1747 .unwrap(),
1748 0
1749 );
1750
1751 bank.slot = bank.slot() - slots_per_epoch;
1753 bank.epoch -= 1;
1754 let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
1755 let bank = Bank::new_from_parent_with_bank_forks(
1756 &bank_forks,
1757 bank,
1758 SlotLeader::default(),
1759 first_normal_slot + slots_per_epoch,
1760 );
1761 let fetched_reward_epoch_delegated_stakes = RewardEpochDelegatedStakes::get(&bank).unwrap();
1762 assert_eq!(
1763 fetched_reward_epoch_delegated_stakes,
1764 reward_epoch_delegated_stakes
1765 );
1766 }
1767
1768 fn epoch_boundary_accounts_for_test(
1769 account_with_rewards_addresses: &[Pubkey],
1770 swept_vote_account_addresses: &[Pubkey],
1771 ) -> EpochBoundaryAccounts {
1772 let reward_info = RewardInfo {
1773 reward_type: RewardType::Staking,
1774 lamports: 0,
1775 post_balance: 0,
1776 commission_bps: None,
1777 };
1778 EpochBoundaryAccounts {
1779 accounts_with_rewards: account_with_rewards_addresses
1780 .iter()
1781 .map(|address| (*address, reward_info, AccountSharedData::default()))
1782 .collect(),
1783 swept_vote_accounts: swept_vote_account_addresses
1784 .iter()
1785 .map(|address| (*address, AccountSharedData::default()))
1786 .collect(),
1787 ..Default::default()
1788 }
1789 }
1790
1791 #[test]
1792 fn test_epoch_boundary_accounts_storable_get() {
1793 let first_len = 10;
1794 let account_with_rewards_addresses = std::iter::repeat_with(Pubkey::new_unique)
1795 .take(first_len)
1796 .collect::<Vec<_>>();
1797 let second_len = 2;
1798 let swept_vote_account_addresses = std::iter::repeat_with(Pubkey::new_unique)
1799 .take(second_len)
1800 .collect::<Vec<_>>();
1801
1802 let accounts = epoch_boundary_accounts_for_test(
1803 &account_with_rewards_addresses,
1804 &swept_vote_account_addresses,
1805 );
1806 let storable_accounts = EpochBoundaryAccountsStorable {
1807 slot: 0,
1808 epoch_boundary_accounts: &accounts,
1809 };
1810 assert_eq!(
1811 *storable_accounts.get_unchecked(0).0,
1812 account_with_rewards_addresses[0]
1813 );
1814 assert_eq!(
1815 *storable_accounts.get_unchecked(first_len - 1).0,
1816 account_with_rewards_addresses[first_len - 1]
1817 );
1818 assert_eq!(
1819 *storable_accounts.get_unchecked(first_len).0,
1820 swept_vote_account_addresses[0]
1821 );
1822 assert_eq!(
1823 *storable_accounts
1824 .get_unchecked(first_len + second_len - 1)
1825 .0,
1826 swept_vote_account_addresses[second_len - 1]
1827 );
1828 assert_eq!(
1829 *storable_accounts
1830 .get_unchecked(first_len + second_len - 1)
1831 .0,
1832 swept_vote_account_addresses[second_len - 1]
1833 );
1834 assert_eq!(storable_accounts.len(), first_len + second_len);
1835 }
1836
1837 #[test]
1838 #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")]
1839 fn test_epoch_boundary_accounts_storable_get_past_range() {
1840 let first_len = 10;
1841 let account_with_rewards_addresses = std::iter::repeat_with(Pubkey::new_unique)
1842 .take(first_len)
1843 .collect::<Vec<_>>();
1844 let second_len = 2;
1845 let swept_vote_account_addresses = std::iter::repeat_with(Pubkey::new_unique)
1846 .take(second_len)
1847 .collect::<Vec<_>>();
1848
1849 let accounts = epoch_boundary_accounts_for_test(
1850 &account_with_rewards_addresses,
1851 &swept_vote_account_addresses,
1852 );
1853 let storable_accounts = EpochBoundaryAccountsStorable {
1854 slot: 0,
1855 epoch_boundary_accounts: &accounts,
1856 };
1857 let _ = storable_accounts.get_unchecked(first_len + second_len);
1858 }
1859
1860 #[test]
1861 fn test_block_rewards_computation_and_partitioned_distribution() {
1862 agave_logger::setup();
1863
1864 let stake_lamports = 1_000_000_000;
1865 let vote_lamports = 10_000_000_000; let pending_delegator_rewards = 1_000_000;
1867 let stores_per_block = 2; let (
1869 RewardBank {
1870 bank: mut previous_bank,
1871 voters,
1872 stakers,
1873 },
1874 bank_forks,
1875 cases,
1876 ) = create_reward_bank_for_block_revenue_sharing(
1877 stake_lamports,
1878 vote_lamports,
1879 pending_delegator_rewards,
1880 stores_per_block,
1881 );
1882
1883 let starting_slot = previous_bank.slot();
1885 let slots_per_epoch = previous_bank.epoch_schedule().slots_per_epoch;
1886 assert_eq!(starting_slot + 1, slots_per_epoch);
1887
1888 let pre_unswept = previous_bank.get_balance(&cases.activating);
1890
1891 let completion_slot = slots_per_epoch + 3;
1892 for slot in slots_per_epoch..=completion_slot {
1893 let pre_cap = previous_bank.capitalization();
1894 let pre_sysvar_account = previous_bank
1895 .get_account(&epoch_rewards::id())
1896 .unwrap_or_default();
1897 let pre_epoch_rewards: EpochRewards =
1898 from_account(&pre_sysvar_account).unwrap_or_default();
1899 let pre_distributed_rewards = pre_epoch_rewards.distributed_rewards;
1900
1901 let curr_bank = Bank::new_from_parent_with_bank_forks(
1902 bank_forks.as_ref(),
1903 previous_bank.clone(),
1904 SlotLeader::default(),
1905 slot,
1906 );
1907 let post_cap = curr_bank.capitalization();
1908 let rent = &curr_bank.rent_collector.rent;
1909
1910 if slot == slots_per_epoch {
1911 assert_matches!(
1914 curr_bank.get_reward_interval(),
1915 RewardInterval::InsideInterval
1916 );
1917 assert!(curr_bank.is_calculated());
1918
1919 for vote_address in voters.iter() {
1921 let vote_account = curr_bank.get_account_with_fixed_root(vote_address).unwrap();
1922 let vote_state_versions: VoteStateVersions = vote_account.state().unwrap();
1923 let VoteStateVersions::V4(vote_state) = vote_state_versions else {
1924 panic!("unexpected version");
1925 };
1926 assert_eq!(vote_state.pending_delegator_rewards, 0);
1927 }
1928
1929 let epoch_rewards_sysvar = curr_bank.get_account(&epoch_rewards::id()).unwrap();
1932 let block_rewards_swept = pending_delegator_rewards * cases.swept().len() as u64;
1933 assert_eq!(
1934 epoch_rewards_sysvar.lamports(),
1935 block_rewards_swept + rent.minimum_balance(epoch_rewards_sysvar.data().len())
1936 );
1937 assert_eq!(
1938 pre_unswept - curr_bank.vat_to_burn_per_epoch(),
1939 curr_bank.get_balance(&cases.activating)
1940 );
1941
1942 let _ = bank_forks.write().unwrap().set_root(slot, None, None);
1945 assert_eq!(curr_bank.get_epoch_rewards_cache_len(), 0);
1946 } else if slot == slots_per_epoch + 1 {
1947 assert_matches!(
1949 curr_bank.get_reward_interval(),
1950 RewardInterval::InsideInterval
1951 );
1952 assert!(curr_bank.is_partitioned());
1953
1954 let account = curr_bank.get_account(&epoch_rewards::id()).unwrap();
1955 let epoch_rewards: EpochRewards = from_account(&account).unwrap();
1956 assert_eq!(
1957 post_cap,
1958 pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards
1959 );
1960
1961 assert!(account.lamports() > rent.minimum_balance(account.data().len()));
1963 } else if slot == slots_per_epoch + 2 {
1964 assert_matches!(
1966 curr_bank.get_reward_interval(),
1967 RewardInterval::OutsideInterval
1968 );
1969 let account = curr_bank.get_account(&epoch_rewards::id()).unwrap();
1970 let epoch_rewards: EpochRewards = from_account(&account).unwrap();
1971 let burned = pending_delegator_rewards * cases.burned().len() as u64;
1972 assert_eq!(
1973 post_cap,
1974 pre_cap + epoch_rewards.distributed_rewards - burned
1975 );
1976
1977 for stake_address in stakers.iter() {
1978 let stake_account = curr_bank
1979 .get_account_with_fixed_root(stake_address)
1980 .unwrap();
1981 let stake_state: StakeStateV2 = stake_account.state().unwrap();
1982 let delegation = stake_state.delegation().unwrap();
1983 if cases.distributed().contains(&delegation.voter_pubkey) {
1985 assert_eq!(
1986 stake_account.lamports(),
1987 delegation.stake
1988 + rent.minimum_balance(StakeStateV2::size_of())
1989 + pending_delegator_rewards
1990 );
1991 } else {
1992 assert_eq!(
1993 stake_account.lamports(),
1994 delegation.stake + rent.minimum_balance(StakeStateV2::size_of())
1995 );
1996 }
1997 }
1998 } else if slot == completion_slot {
1999 assert_matches!(
2001 curr_bank.get_reward_interval(),
2002 RewardInterval::OutsideInterval
2003 );
2004
2005 assert_eq!(post_cap, pre_cap);
2007
2008 let account = curr_bank.get_account(&epoch_rewards::id()).unwrap();
2010 assert_eq!(
2011 account.lamports(),
2012 rent.minimum_balance(account.data().len())
2013 );
2014 }
2015 previous_bank = curr_bank;
2016 }
2017 }
2018}