diff --git a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs index 5590a4af8d2bb4..67cd54944d4d57 100644 --- a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs +++ b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs @@ -603,143 +603,28 @@ mod tests { crate::{ bank::{ null_tracer, - partitioned_epoch_rewards::{EpochRewardStatus, StartBlockHeightAndRewards}, - tests::{create_genesis_config, new_bank_from_parent_with_bank_forks}, + partitioned_epoch_rewards::{ + tests::{ + create_default_reward_bank, create_reward_bank, RewardBank, SLOTS_PER_EPOCH, + }, + EpochRewardStatus, StartBlockHeightAndRewards, + }, + tests::create_genesis_config, VoteReward, }, - genesis_utils::{ - create_genesis_config_with_vote_accounts, GenesisConfigInfo, ValidatorVoteKeypairs, - }, - runtime_config::RuntimeConfig, stake_account::StakeAccount, stakes::Stakes, }, rayon::ThreadPoolBuilder, - solana_accounts_db::{ - accounts_db::{ - AccountShrinkThreshold, AccountsDbConfig, ACCOUNTS_DB_CONFIG_FOR_TESTING, - }, - accounts_index::AccountSecondaryIndexes, - partitioned_rewards::{PartitionedEpochRewardsConfig, TestPartitionedEpochRewards}, - }, solana_sdk::{ account::{accounts_equal, ReadableAccount, WritableAccount}, - epoch_schedule::EpochSchedule, native_token::{sol_to_lamports, LAMPORTS_PER_SOL}, reward_type::RewardType, - signature::Signer, stake::state::Delegation, - vote::state::{VoteStateVersions, MAX_LOCKOUT_HISTORY}, }, - solana_vote_program::vote_state, std::sync::{Arc, RwLockReadGuard}, }; - const SLOTS_PER_EPOCH: u64 = 32; - - struct RewardBank { - bank: Arc, - voters: Vec, - stakers: Vec, - } - - /// Helper functions to create a bank that pays some rewards - fn create_default_reward_bank( - expected_num_delegations: usize, - advance_num_slots: u64, - ) -> RewardBank { - create_reward_bank( - expected_num_delegations, - PartitionedEpochRewardsConfig::default().stake_account_stores_per_block, - advance_num_slots, - ) - } - - fn create_reward_bank( - expected_num_delegations: usize, - stake_account_stores_per_block: u64, - advance_num_slots: u64, - ) -> RewardBank { - let validator_keypairs = (0..expected_num_delegations) - .map(|_| ValidatorVoteKeypairs::new_rand()) - .collect::>(); - - let GenesisConfigInfo { - mut genesis_config, .. - } = create_genesis_config_with_vote_accounts( - 1_000_000_000, - &validator_keypairs, - vec![2_000_000_000; expected_num_delegations], - ); - genesis_config.epoch_schedule = EpochSchedule::new(SLOTS_PER_EPOCH); - - let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone(); - accounts_db_config.test_partitioned_epoch_rewards = - TestPartitionedEpochRewards::PartitionedEpochRewardsConfigRewardBlocks { - stake_account_stores_per_block, - }; - - let bank = Bank::new_with_paths( - &genesis_config, - Arc::new(RuntimeConfig::default()), - Vec::new(), - None, - None, - AccountSecondaryIndexes::default(), - AccountShrinkThreshold::default(), - false, - Some(accounts_db_config), - None, - Some(Pubkey::new_unique()), - Arc::default(), - ); - - // Fill bank_forks with banks with votes landing in the next slot - // Create enough banks such that vote account will root - for validator_vote_keypairs in &validator_keypairs { - let vote_id = validator_vote_keypairs.vote_keypair.pubkey(); - let mut vote_account = bank.get_account(&vote_id).unwrap(); - // generate some rewards - let mut vote_state = Some(vote_state::from(&vote_account).unwrap()); - for i in 0..MAX_LOCKOUT_HISTORY + 42 { - if let Some(v) = vote_state.as_mut() { - vote_state::process_slot_vote_unchecked(v, i as u64) - } - let versioned = VoteStateVersions::Current(Box::new(vote_state.take().unwrap())); - vote_state::to(&versioned, &mut vote_account).unwrap(); - match versioned { - VoteStateVersions::Current(v) => { - vote_state = Some(*v); - } - _ => panic!("Has to be of type Current"), - }; - } - bank.store_account_and_update_capitalization(&vote_id, &vote_account); - } - - // Advance some num slots; usually to the next epoch boundary to update - // EpochStakes - let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); - let bank = new_bank_from_parent_with_bank_forks( - &bank_forks, - bank, - &Pubkey::default(), - advance_num_slots, - ); - - RewardBank { - bank, - voters: validator_keypairs - .iter() - .map(|k| k.vote_keypair.pubkey()) - .collect(), - stakers: validator_keypairs - .iter() - .map(|k| k.stake_keypair.pubkey()) - .collect(), - } - } - #[test] fn test_store_vote_accounts_partitioned() { let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL); diff --git a/runtime/src/bank/partitioned_epoch_rewards/mod.rs b/runtime/src/bank/partitioned_epoch_rewards/mod.rs index b87ee5f1e797ee..f374b127050aef 100644 --- a/runtime/src/bank/partitioned_epoch_rewards/mod.rs +++ b/runtime/src/bank/partitioned_epoch_rewards/mod.rs @@ -292,6 +292,111 @@ mod tests { } } + pub(super) const SLOTS_PER_EPOCH: u64 = 32; + + pub(super) struct RewardBank { + pub(super) bank: Arc, + pub(super) voters: Vec, + pub(super) stakers: Vec, + } + + /// Helper functions to create a bank that pays some rewards + pub(super) fn create_default_reward_bank( + expected_num_delegations: usize, + advance_num_slots: u64, + ) -> RewardBank { + create_reward_bank( + expected_num_delegations, + PartitionedEpochRewardsConfig::default().stake_account_stores_per_block, + advance_num_slots, + ) + } + + pub(super) fn create_reward_bank( + expected_num_delegations: usize, + stake_account_stores_per_block: u64, + advance_num_slots: u64, + ) -> RewardBank { + let validator_keypairs = (0..expected_num_delegations) + .map(|_| ValidatorVoteKeypairs::new_rand()) + .collect::>(); + + let GenesisConfigInfo { + mut genesis_config, .. + } = create_genesis_config_with_vote_accounts( + 1_000_000_000, + &validator_keypairs, + vec![2_000_000_000; expected_num_delegations], + ); + genesis_config.epoch_schedule = EpochSchedule::new(SLOTS_PER_EPOCH); + + let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone(); + accounts_db_config.test_partitioned_epoch_rewards = + TestPartitionedEpochRewards::PartitionedEpochRewardsConfigRewardBlocks { + stake_account_stores_per_block, + }; + + let bank = Bank::new_with_paths( + &genesis_config, + Arc::new(RuntimeConfig::default()), + Vec::new(), + None, + None, + AccountSecondaryIndexes::default(), + AccountShrinkThreshold::default(), + false, + Some(accounts_db_config), + None, + Some(Pubkey::new_unique()), + Arc::default(), + ); + + // Fill bank_forks with banks with votes landing in the next slot + // Create enough banks such that vote account will root + for validator_vote_keypairs in &validator_keypairs { + let vote_id = validator_vote_keypairs.vote_keypair.pubkey(); + let mut vote_account = bank.get_account(&vote_id).unwrap(); + // generate some rewards + let mut vote_state = Some(vote_state::from(&vote_account).unwrap()); + for i in 0..MAX_LOCKOUT_HISTORY + 42 { + if let Some(v) = vote_state.as_mut() { + vote_state::process_slot_vote_unchecked(v, i as u64) + } + let versioned = VoteStateVersions::Current(Box::new(vote_state.take().unwrap())); + vote_state::to(&versioned, &mut vote_account).unwrap(); + match versioned { + VoteStateVersions::Current(v) => { + vote_state = Some(*v); + } + _ => panic!("Has to be of type Current"), + }; + } + bank.store_account_and_update_capitalization(&vote_id, &vote_account); + } + + // Advance some num slots; usually to the next epoch boundary to update + // EpochStakes + let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); + let bank = new_bank_from_parent_with_bank_forks( + &bank_forks, + bank, + &Pubkey::default(), + advance_num_slots, + ); + + RewardBank { + bank, + voters: validator_keypairs + .iter() + .map(|k| k.vote_keypair.pubkey()) + .collect(), + stakers: validator_keypairs + .iter() + .map(|k| k.stake_keypair.pubkey()) + .collect(), + } + } + #[test] fn test_force_reward_interval_end() { let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL); @@ -420,79 +525,34 @@ mod tests { fn test_rewards_computation_and_partitioned_distribution_one_block() { solana_logger::setup(); - // setup the expected number of stake delegations - let expected_num_delegations = 100; - - let validator_keypairs = (0..expected_num_delegations) - .map(|_| ValidatorVoteKeypairs::new_rand()) - .collect::>(); - - let GenesisConfigInfo { - mut genesis_config, .. - } = create_genesis_config_with_vote_accounts( - 1_000_000_000, - &validator_keypairs, - vec![2_000_000_000; expected_num_delegations], - ); - let slots_per_epoch = 32; - genesis_config.epoch_schedule = EpochSchedule::new(slots_per_epoch); - - let bank0 = Bank::new_for_tests(&genesis_config); - let num_slots_in_epoch = bank0.get_slots_in_epoch(bank0.epoch()); - assert_eq!(num_slots_in_epoch, slots_per_epoch); - - let mut previous_bank = Arc::new(Bank::new_from_parent( - Arc::new(bank0), - &Pubkey::default(), - 1, - )); + let starting_slot = SLOTS_PER_EPOCH - 1; + let RewardBank { + bank: mut previous_bank, + .. + } = create_default_reward_bank(100, starting_slot - 1); // simulate block progress - for slot in 2..=(2 * slots_per_epoch) + 2 { + for slot in starting_slot..=(2 * SLOTS_PER_EPOCH) + 2 { let pre_cap = previous_bank.capitalization(); let curr_bank = Bank::new_from_parent(previous_bank, &Pubkey::default(), slot); let post_cap = curr_bank.capitalization(); - // Fill banks with banks with votes landing in the next slot - // Create enough banks such that vote account will root - for validator_vote_keypairs in validator_keypairs.iter() { - let vote_id = validator_vote_keypairs.vote_keypair.pubkey(); - let mut vote_account = curr_bank.get_account(&vote_id).unwrap(); - // generate some rewards - let mut vote_state = Some(vote_state::from(&vote_account).unwrap()); - for i in 0..MAX_LOCKOUT_HISTORY + 42 { - if let Some(v) = vote_state.as_mut() { - vote_state::process_slot_vote_unchecked(v, i as u64) - } - let versioned = - VoteStateVersions::Current(Box::new(vote_state.take().unwrap())); - vote_state::to(&versioned, &mut vote_account).unwrap(); - match versioned { - VoteStateVersions::Current(v) => { - vote_state = Some(*v); - } - _ => panic!("Has to be of type Current"), - }; - } - curr_bank.store_account_and_update_capitalization(&vote_id, &vote_account); - } - - if slot % num_slots_in_epoch == 0 { - // This is the first block of epoch 1. Reward computation should happen in this block. + if slot % SLOTS_PER_EPOCH == 0 { + // This is the first block of the epoch. Reward computation should happen in this block. // assert reward compute status activated at epoch boundary assert_matches!( curr_bank.get_reward_interval(), RewardInterval::InsideInterval ); - if slot == num_slots_in_epoch { + if slot == SLOTS_PER_EPOCH { // cap should increase because of new epoch rewards assert!(post_cap > pre_cap); } else { assert_eq!(post_cap, pre_cap); } - } else if slot == num_slots_in_epoch + 1 { - // 1. when curr_slot == num_slots_in_epoch + 1, the 2nd block of + } else if slot == SLOTS_PER_EPOCH + 1 { + // 1. when curr_slot == SLOTS_PER_EPOCH + 1, the 2nd block of // epoch 1, reward distribution should happen in this block. // however, all stake rewards are paid at this block therefore // reward_status should have transitioned to inactive. The cap @@ -509,7 +569,7 @@ mod tests { solana_sdk::account::from_account(&account).unwrap(); assert_eq!(post_cap, pre_cap + epoch_rewards.distributed_rewards); } else { - // 2. when curr_slot == num_slots_in_epoch+2, the 3rd block of + // 2. when curr_slot == SLOTS_PER_EPOCH + 2, the 3rd block of // epoch 1 (or any other slot). reward distribution should have // already completed. Therefore, reward_status should stay // inactive and cap should stay the same. @@ -523,7 +583,7 @@ mod tests { } // EpochRewards sysvar is created in the first block of epoch 1. // Ensure the sysvar persists thereafter. - if slot >= num_slots_in_epoch { + if slot >= SLOTS_PER_EPOCH { let epoch_rewards_lamports = curr_bank.get_balance(&solana_sdk::sysvar::epoch_rewards::id()); assert!(epoch_rewards_lamports > 0); @@ -537,57 +597,14 @@ mod tests { fn test_rewards_computation_and_partitioned_distribution_two_blocks() { solana_logger::setup(); - // Set up the expected number of stake delegations 100 - let expected_num_delegations = 100; - - let validator_keypairs = (0..expected_num_delegations) - .map(|_| ValidatorVoteKeypairs::new_rand()) - .collect::>(); - - let GenesisConfigInfo { - mut genesis_config, .. - } = create_genesis_config_with_vote_accounts( - 1_000_000_000, - &validator_keypairs, - vec![2_000_000_000; expected_num_delegations], - ); - genesis_config.epoch_schedule = EpochSchedule::custom(32, 32, false); - - // Config stake reward distribution to be 50 per block - // We will need two blocks for reward distribution. And we can assert that the expected bank - // capital changes before/during/after reward distribution. - let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone(); - accounts_db_config.test_partitioned_epoch_rewards = - TestPartitionedEpochRewards::PartitionedEpochRewardsConfigRewardBlocks { - stake_account_stores_per_block: 50, - }; - - let bank0 = Bank::new_with_paths( - &genesis_config, - Arc::new(RuntimeConfig::default()), - Vec::new(), - None, - None, - AccountSecondaryIndexes::default(), - AccountShrinkThreshold::default(), - false, - Some(accounts_db_config), - None, - None, - Arc::default(), - ); - - let num_slots_in_epoch = bank0.get_slots_in_epoch(bank0.epoch()); - assert_eq!(num_slots_in_epoch, 32); - - let mut previous_bank = Arc::new(Bank::new_from_parent( - Arc::new(bank0), - &Pubkey::default(), - 1, - )); + let starting_slot = SLOTS_PER_EPOCH - 1; + let RewardBank { + bank: mut previous_bank, + .. + } = create_reward_bank(100, 50, starting_slot - 1); // simulate block progress - for slot in 2..=num_slots_in_epoch + 3 { + for slot in starting_slot..=SLOTS_PER_EPOCH + 3 { let pre_cap = previous_bank.capitalization(); let pre_sysvar_account = previous_bank @@ -600,31 +617,7 @@ mod tests { let curr_bank = Bank::new_from_parent(previous_bank, &Pubkey::default(), slot); let post_cap = curr_bank.capitalization(); - // Fill banks with banks with votes landing in the next slot - // Create enough banks such that vote account will root - for validator_vote_keypairs in validator_keypairs.iter() { - let vote_id = validator_vote_keypairs.vote_keypair.pubkey(); - let mut vote_account = curr_bank.get_account(&vote_id).unwrap(); - // generate some rewards - let mut vote_state = Some(vote_state::from(&vote_account).unwrap()); - for i in 0..MAX_LOCKOUT_HISTORY + 42 { - if let Some(v) = vote_state.as_mut() { - vote_state::process_slot_vote_unchecked(v, i as u64) - } - let versioned = - VoteStateVersions::Current(Box::new(vote_state.take().unwrap())); - vote_state::to(&versioned, &mut vote_account).unwrap(); - match versioned { - VoteStateVersions::Current(v) => { - vote_state = Some(*v); - } - _ => panic!("Has to be of type Current"), - }; - } - curr_bank.store_account_and_update_capitalization(&vote_id, &vote_account); - } - - if slot == num_slots_in_epoch { + if slot == SLOTS_PER_EPOCH { // This is the first block of epoch 1. Reward computation should happen in this block. // assert reward compute status activated at epoch boundary assert_matches!( @@ -634,8 +627,8 @@ mod tests { // cap should increase because of new epoch rewards assert!(post_cap > pre_cap); - } else if slot == num_slots_in_epoch + 1 { - // When curr_slot == num_slots_in_epoch + 1, the 2nd block of + } else if slot == SLOTS_PER_EPOCH + 1 { + // When curr_slot == SLOTS_PER_EPOCH + 1, the 2nd block of // epoch 1, reward distribution should happen in this block. The // cap should increase accordingly. assert_matches!( @@ -652,8 +645,8 @@ mod tests { post_cap, pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards ); - } else if slot == num_slots_in_epoch + 2 { - // When curr_slot == num_slots_in_epoch + 2, the 3nd block of + } else if slot == SLOTS_PER_EPOCH + 2 { + // When curr_slot == SLOTS_PER_EPOCH + 2, the 3nd block of // epoch 1, reward distribution should happen in this block. // however, all stake rewards are paid at the this block // therefore reward_status should have transitioned to inactive. @@ -673,7 +666,7 @@ mod tests { pre_cap + epoch_rewards.distributed_rewards - pre_distributed_rewards ); } else { - // When curr_slot == num_slots_in_epoch + 3, the 4th block of + // When curr_slot == SLOTS_PER_EPOCH + 3, the 4th block of // epoch 1 (or any other slot). reward distribution should have // already completed. Therefore, reward_status should stay // inactive and cap should stay the same.