Reorganize partitioned epoch rewards runtime code, 1 of 5 (#511)

* Add bank::partitioned_epoch_rewards submodule

* Move helper structs and types to submodule

* Move partitioned-rewards-specific Bank methods to submodule

* Move unit tests into submodule

* Update BankAbiTestWrapperNewer frozen_abi hash
This commit is contained in:
Tyera 2024-04-01 18:05:57 -06:00 committed by GitHub
parent 3f17532b11
commit c29a2392fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 382 additions and 340 deletions

View File

@ -41,6 +41,11 @@ use {
bank::{
builtins::{BuiltinPrototype, BUILTINS},
metrics::*,
partitioned_epoch_rewards::{
CalculateRewardsAndDistributeVoteRewardsResult, EpochRewardCalculateParamInfo,
EpochRewardStatus, PartitionedRewardsCalculation, RewardInterval,
StakeRewardCalculationPartitioned, StakeRewards, VoteRewardsAccounts,
},
},
bank_forks::BankForks,
epoch_rewards_hasher::hash_rewards_into_partitions,
@ -83,7 +88,6 @@ use {
ancestors::{Ancestors, AncestorsForSerialization},
blockhash_queue::BlockhashQueue,
epoch_accounts_hash::EpochAccountsHash,
partitioned_rewards::PartitionedEpochRewardsConfig,
sorted_storages::SortedStorages,
stake_rewards::StakeReward,
storable_accounts::StorableAccounts,
@ -173,7 +177,7 @@ use {
},
},
solana_system_program::{get_system_account_kind, SystemAccountKind},
solana_vote::vote_account::{VoteAccount, VoteAccounts, VoteAccountsHashMap},
solana_vote::vote_account::{VoteAccount, VoteAccountsHashMap},
solana_vote_program::vote_state::VoteState,
std::{
borrow::Cow,
@ -220,6 +224,7 @@ pub mod builtins;
pub mod epoch_accounts_hash_utils;
mod fee_distribution;
mod metrics;
pub(crate) mod partitioned_epoch_rewards;
mod serde_snapshot;
mod sysvar_cache;
#[cfg(test)]
@ -642,27 +647,6 @@ impl AbiExample for OptionalDropCallback {
}
}
#[derive(AbiExample, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct StartBlockHeightAndRewards {
/// the block height of the slot at which rewards distribution began
pub(crate) start_block_height: u64,
/// calculated epoch rewards pending distribution, outer Vec is by partition (one partition per block)
pub(crate) stake_rewards_by_partition: Arc<Vec<StakeRewards>>,
}
/// Represent whether bank is in the reward phase or not.
#[derive(AbiExample, AbiEnumVisitor, Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub(crate) enum EpochRewardStatus {
/// this bank is in the reward phase.
/// Contents are the start point for epoch reward calculation,
/// i.e. parent_slot and parent_block height for the starting
/// block of the current epoch.
Active(StartBlockHeightAndRewards),
/// this bank is outside of the rewarding phase.
#[default]
Inactive,
}
/// Manager for the state of all accounts and programs after processing its entries.
/// AbiExample is needed even without Serialize/Deserialize; actual (de-)serialization
/// are implemented elsewhere for versioning
@ -865,56 +849,6 @@ struct VoteReward {
}
type VoteRewards = DashMap<Pubkey, VoteReward>;
#[derive(Debug, Default)]
struct VoteRewardsAccounts {
/// reward info for each vote account pubkey.
/// This type is used by `update_reward_history()`
rewards: Vec<(Pubkey, RewardInfo)>,
/// corresponds to pubkey in `rewards`
/// Some if account is to be stored.
/// None if to be skipped.
accounts_to_store: Vec<Option<AccountSharedData>>,
}
/// hold reward calc info to avoid recalculation across functions
struct EpochRewardCalculateParamInfo<'a> {
stake_history: StakeHistory,
stake_delegations: Vec<(&'a Pubkey, &'a StakeAccount<Delegation>)>,
cached_vote_accounts: &'a VoteAccounts,
}
/// Hold all results from calculating the rewards for partitioned distribution.
/// This struct exists so we can have a function which does all the calculation with no
/// side effects.
struct PartitionedRewardsCalculation {
vote_account_rewards: VoteRewardsAccounts,
stake_rewards_by_partition: StakeRewardCalculationPartitioned,
old_vote_balance_and_staked: u64,
validator_rewards: u64,
validator_rate: f64,
foundation_rate: f64,
prev_epoch_duration_in_years: f64,
capitalization: u64,
}
/// result of calculating the stake rewards at beginning of new epoch
struct StakeRewardCalculationPartitioned {
/// each individual stake account to reward, grouped by partition
stake_rewards_by_partition: Vec<StakeRewards>,
/// total lamports across all `stake_rewards`
total_stake_rewards_lamports: u64,
}
struct CalculateRewardsAndDistributeVoteRewardsResult {
/// total rewards for the epoch (including both vote rewards and stake rewards)
total_rewards: u64,
/// distributed vote rewards
distributed_rewards: u64,
/// stake rewards that still need to be distributed, grouped by partition
stake_rewards_by_partition: Vec<StakeRewards>,
}
pub(crate) type StakeRewards = Vec<StakeReward>;
#[derive(Debug, Default)]
pub struct NewBankOptions {
@ -950,14 +884,6 @@ struct StakeRewardCalculation {
total_stake_rewards_lamports: u64,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(super) enum RewardInterval {
/// the slot within the epoch is INSIDE the reward distribution interval
InsideInterval,
/// the slot within the epoch is OUTSIDE the reward distribution interval
OutsideInterval,
}
impl Bank {
fn default_with_accounts(accounts: Accounts) -> Self {
let mut bank = Self {
@ -1144,76 +1070,6 @@ impl Bank {
rent_collector.clone_with_epoch(epoch)
}
fn is_partitioned_rewards_feature_enabled(&self) -> bool {
self.feature_set
.is_active(&feature_set::enable_partitioned_epoch_reward::id())
}
pub(crate) fn set_epoch_reward_status_active(
&mut self,
stake_rewards_by_partition: Vec<StakeRewards>,
) {
self.epoch_reward_status = EpochRewardStatus::Active(StartBlockHeightAndRewards {
start_block_height: self.block_height,
stake_rewards_by_partition: Arc::new(stake_rewards_by_partition),
});
}
fn partitioned_epoch_rewards_config(&self) -> &PartitionedEpochRewardsConfig {
&self
.rc
.accounts
.accounts_db
.partitioned_epoch_rewards_config
}
/// # stake accounts to store in one block during partitioned reward interval
fn partitioned_rewards_stake_account_stores_per_block(&self) -> u64 {
self.partitioned_epoch_rewards_config()
.stake_account_stores_per_block
}
/// reward calculation happens synchronously during the first block of the epoch boundary.
/// So, # blocks for reward calculation is 1.
fn get_reward_calculation_num_blocks(&self) -> Slot {
self.partitioned_epoch_rewards_config()
.reward_calculation_num_blocks
}
/// Calculate the number of blocks required to distribute rewards to all stake accounts.
fn get_reward_distribution_num_blocks(&self, rewards: &StakeRewards) -> u64 {
let total_stake_accounts = rewards.len();
if self.epoch_schedule.warmup && self.epoch < self.first_normal_epoch() {
1
} else {
const MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH: u64 = 10;
let num_chunks = solana_accounts_db::accounts_hash::AccountsHasher::div_ceil(
total_stake_accounts,
self.partitioned_rewards_stake_account_stores_per_block() as usize,
) as u64;
// Limit the reward credit interval to 10% of the total number of slots in a epoch
num_chunks.clamp(
1,
(self.epoch_schedule.slots_per_epoch / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH).max(1),
)
}
}
/// Return `RewardInterval` enum for current bank
fn get_reward_interval(&self) -> RewardInterval {
if matches!(self.epoch_reward_status, EpochRewardStatus::Active(_)) {
RewardInterval::InsideInterval
} else {
RewardInterval::OutsideInterval
}
}
/// For testing only
pub fn force_reward_interval_end_for_tests(&mut self) {
self.epoch_reward_status = EpochRewardStatus::Inactive;
}
fn _new_from_parent(
parent: Arc<Bank>,
collector_id: &Pubkey,
@ -1642,13 +1498,6 @@ impl Bank {
}
}
fn force_partition_rewards_in_first_block_of_epoch(&self) -> bool {
self.partitioned_epoch_rewards_config()
.test_enable_partitioned_rewards
&& self.get_reward_calculation_num_blocks() == 0
&& self.partitioned_rewards_stake_account_stores_per_block() == u64::MAX
}
/// Begin the process of calculating and distributing rewards.
/// This process can take multiple slots.
fn begin_partitioned_rewards(
@ -3591,15 +3440,6 @@ impl Bank {
report_partitioned_reward_metrics(self, metrics);
}
/// true if it is ok to run partitioned rewards code.
/// This means the feature is activated or certain testing situations.
fn is_partitioned_rewards_code_enabled(&self) -> bool {
self.is_partitioned_rewards_feature_enabled()
|| self
.partitioned_epoch_rewards_config()
.test_enable_partitioned_rewards
}
/// Helper fn to log epoch_rewards sysvar
fn log_epoch_rewards_sysvar(&self, prefix: &str) {
if let Some(account) = self.get_account(&sysvar::epoch_rewards::id()) {

View File

@ -0,0 +1,369 @@
use {
super::Bank,
crate::{stake_account::StakeAccount, stake_history::StakeHistory},
solana_accounts_db::{
partitioned_rewards::PartitionedEpochRewardsConfig, stake_rewards::StakeReward,
},
solana_sdk::{
account::AccountSharedData, clock::Slot, feature_set, pubkey::Pubkey,
reward_info::RewardInfo, stake::state::Delegation,
},
solana_vote::vote_account::VoteAccounts,
std::sync::Arc,
};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(super) enum RewardInterval {
/// the slot within the epoch is INSIDE the reward distribution interval
InsideInterval,
/// the slot within the epoch is OUTSIDE the reward distribution interval
OutsideInterval,
}
#[derive(AbiExample, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct StartBlockHeightAndRewards {
/// the block height of the slot at which rewards distribution began
pub(crate) start_block_height: u64,
/// calculated epoch rewards pending distribution, outer Vec is by partition (one partition per block)
pub(crate) stake_rewards_by_partition: Arc<Vec<StakeRewards>>,
}
/// Represent whether bank is in the reward phase or not.
#[derive(AbiExample, AbiEnumVisitor, Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub(crate) enum EpochRewardStatus {
/// this bank is in the reward phase.
/// Contents are the start point for epoch reward calculation,
/// i.e. parent_slot and parent_block height for the starting
/// block of the current epoch.
Active(StartBlockHeightAndRewards),
/// this bank is outside of the rewarding phase.
#[default]
Inactive,
}
#[derive(Debug, Default)]
pub(super) struct VoteRewardsAccounts {
/// reward info for each vote account pubkey.
/// This type is used by `update_reward_history()`
pub(super) rewards: Vec<(Pubkey, RewardInfo)>,
/// corresponds to pubkey in `rewards`
/// Some if account is to be stored.
/// None if to be skipped.
pub(super) accounts_to_store: Vec<Option<AccountSharedData>>,
}
/// hold reward calc info to avoid recalculation across functions
pub(super) struct EpochRewardCalculateParamInfo<'a> {
pub(super) stake_history: StakeHistory,
pub(super) stake_delegations: Vec<(&'a Pubkey, &'a StakeAccount<Delegation>)>,
pub(super) cached_vote_accounts: &'a VoteAccounts,
}
/// Hold all results from calculating the rewards for partitioned distribution.
/// This struct exists so we can have a function which does all the calculation with no
/// side effects.
pub(super) struct PartitionedRewardsCalculation {
pub(super) vote_account_rewards: VoteRewardsAccounts,
pub(super) stake_rewards_by_partition: StakeRewardCalculationPartitioned,
pub(super) old_vote_balance_and_staked: u64,
pub(super) validator_rewards: u64,
pub(super) validator_rate: f64,
pub(super) foundation_rate: f64,
pub(super) prev_epoch_duration_in_years: f64,
pub(super) capitalization: u64,
}
/// result of calculating the stake rewards at beginning of new epoch
pub(super) struct StakeRewardCalculationPartitioned {
/// each individual stake account to reward, grouped by partition
pub(super) stake_rewards_by_partition: Vec<StakeRewards>,
/// total lamports across all `stake_rewards`
pub(super) total_stake_rewards_lamports: u64,
}
pub(super) struct CalculateRewardsAndDistributeVoteRewardsResult {
/// total rewards for the epoch (including both vote rewards and stake rewards)
pub(super) total_rewards: u64,
/// distributed vote rewards
pub(super) distributed_rewards: u64,
/// stake rewards that still need to be distributed, grouped by partition
pub(super) stake_rewards_by_partition: Vec<StakeRewards>,
}
pub(crate) type StakeRewards = Vec<StakeReward>;
impl Bank {
pub(super) fn is_partitioned_rewards_feature_enabled(&self) -> bool {
self.feature_set
.is_active(&feature_set::enable_partitioned_epoch_reward::id())
}
pub(crate) fn set_epoch_reward_status_active(
&mut self,
stake_rewards_by_partition: Vec<StakeRewards>,
) {
self.epoch_reward_status = EpochRewardStatus::Active(StartBlockHeightAndRewards {
start_block_height: self.block_height,
stake_rewards_by_partition: Arc::new(stake_rewards_by_partition),
});
}
pub(super) fn partitioned_epoch_rewards_config(&self) -> &PartitionedEpochRewardsConfig {
&self
.rc
.accounts
.accounts_db
.partitioned_epoch_rewards_config
}
/// # stake accounts to store in one block during partitioned reward interval
pub(super) fn partitioned_rewards_stake_account_stores_per_block(&self) -> u64 {
self.partitioned_epoch_rewards_config()
.stake_account_stores_per_block
}
/// reward calculation happens synchronously during the first block of the epoch boundary.
/// So, # blocks for reward calculation is 1.
pub(super) fn get_reward_calculation_num_blocks(&self) -> Slot {
self.partitioned_epoch_rewards_config()
.reward_calculation_num_blocks
}
/// Calculate the number of blocks required to distribute rewards to all stake accounts.
pub(super) fn get_reward_distribution_num_blocks(&self, rewards: &StakeRewards) -> u64 {
let total_stake_accounts = rewards.len();
if self.epoch_schedule.warmup && self.epoch < self.first_normal_epoch() {
1
} else {
const MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH: u64 = 10;
let num_chunks = solana_accounts_db::accounts_hash::AccountsHasher::div_ceil(
total_stake_accounts,
self.partitioned_rewards_stake_account_stores_per_block() as usize,
) as u64;
// Limit the reward credit interval to 10% of the total number of slots in a epoch
num_chunks.clamp(
1,
(self.epoch_schedule.slots_per_epoch / MAX_FACTOR_OF_REWARD_BLOCKS_IN_EPOCH).max(1),
)
}
}
/// Return `RewardInterval` enum for current bank
pub(super) fn get_reward_interval(&self) -> RewardInterval {
if matches!(self.epoch_reward_status, EpochRewardStatus::Active(_)) {
RewardInterval::InsideInterval
} else {
RewardInterval::OutsideInterval
}
}
/// true if it is ok to run partitioned rewards code.
/// This means the feature is activated or certain testing situations.
pub(super) fn is_partitioned_rewards_code_enabled(&self) -> bool {
self.is_partitioned_rewards_feature_enabled()
|| self
.partitioned_epoch_rewards_config()
.test_enable_partitioned_rewards
}
/// For testing only
pub fn force_reward_interval_end_for_tests(&mut self) {
self.epoch_reward_status = EpochRewardStatus::Inactive;
}
pub(super) fn force_partition_rewards_in_first_block_of_epoch(&self) -> bool {
self.partitioned_epoch_rewards_config()
.test_enable_partitioned_rewards
&& self.get_reward_calculation_num_blocks() == 0
&& self.partitioned_rewards_stake_account_stores_per_block() == u64::MAX
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::bank::tests::create_genesis_config,
solana_accounts_db::{
accounts_db::{
AccountShrinkThreshold, AccountsDbConfig, ACCOUNTS_DB_CONFIG_FOR_TESTING,
},
accounts_index::AccountSecondaryIndexes,
partitioned_rewards::TestPartitionedEpochRewards,
},
solana_program_runtime::runtime_config::RuntimeConfig,
solana_sdk::{epoch_schedule::EpochSchedule, native_token::LAMPORTS_PER_SOL},
};
impl Bank {
/// Return the total number of blocks in reward interval (including both calculation and crediting).
pub(in crate::bank) fn get_reward_total_num_blocks(&self, rewards: &StakeRewards) -> u64 {
self.get_reward_calculation_num_blocks()
+ self.get_reward_distribution_num_blocks(rewards)
}
}
#[test]
fn test_force_reward_interval_end() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
let expected_num = 100;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
bank.set_epoch_reward_status_active(vec![stake_rewards]);
assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
bank.force_reward_interval_end_for_tests();
assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
}
#[test]
fn test_is_partitioned_reward_feature_enable() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
assert!(!bank.is_partitioned_rewards_feature_enabled());
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());
assert!(bank.is_partitioned_rewards_feature_enabled());
}
#[test]
fn test_deactivate_epoch_reward_status() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
let expected_num = 100;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
bank.set_epoch_reward_status_active(vec![stake_rewards]);
assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
bank.deactivate_epoch_reward_status();
assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during small epoch
/// The num_credit_blocks should be cap to 10% of the total number of blocks in the epoch.
#[test]
fn test_get_reward_distribution_num_blocks_cap() {
let (mut genesis_config, _mint_keypair) =
create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config.epoch_schedule = EpochSchedule::custom(32, 32, false);
// Config stake reward distribution to be 10 per block
let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone();
accounts_db_config.test_partitioned_epoch_rewards =
TestPartitionedEpochRewards::PartitionedEpochRewardsConfigRewardBlocks {
reward_calculation_num_blocks: 1,
stake_account_stores_per_block: 10,
};
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(),
);
let stake_account_stores_per_block =
bank.partitioned_rewards_stake_account_stores_per_block();
assert_eq!(stake_account_stores_per_block, 10);
let check_num_reward_distribution_blocks =
|num_stakes: u64,
expected_num_reward_distribution_blocks: u64,
expected_num_reward_computation_blocks: u64| {
// Given the short epoch, i.e. 32 slots, we should cap the number of reward distribution blocks to 32/10 = 3.
let stake_rewards = (0..num_stakes)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
assert_eq!(
bank.get_reward_distribution_num_blocks(&stake_rewards),
expected_num_reward_distribution_blocks
);
assert_eq!(
bank.get_reward_calculation_num_blocks(),
expected_num_reward_computation_blocks
);
assert_eq!(
bank.get_reward_total_num_blocks(&stake_rewards),
bank.get_reward_distribution_num_blocks(&stake_rewards)
+ bank.get_reward_calculation_num_blocks(),
);
};
for test_record in [
// num_stakes, expected_num_reward_distribution_blocks, expected_num_reward_computation_blocks
(0, 1, 1),
(1, 1, 1),
(stake_account_stores_per_block, 1, 1),
(2 * stake_account_stores_per_block - 1, 2, 1),
(2 * stake_account_stores_per_block, 2, 1),
(3 * stake_account_stores_per_block - 1, 3, 1),
(3 * stake_account_stores_per_block, 3, 1),
(4 * stake_account_stores_per_block, 3, 1), // cap at 3
(5 * stake_account_stores_per_block, 3, 1), //cap at 3
] {
check_num_reward_distribution_blocks(test_record.0, test_record.1, test_record.2);
}
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during normal epoch gives the expected result
#[test]
fn test_get_reward_distribution_num_blocks_normal() {
solana_logger::setup();
let (mut genesis_config, _mint_keypair) =
create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
let bank = Bank::new_for_tests(&genesis_config);
// Given 8k rewards, it will take 2 blocks to credit all the rewards
let expected_num = 8192;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
assert_eq!(bank.get_reward_distribution_num_blocks(&stake_rewards), 2);
assert_eq!(bank.get_reward_calculation_num_blocks(), 1);
assert_eq!(
bank.get_reward_total_num_blocks(&stake_rewards),
bank.get_reward_distribution_num_blocks(&stake_rewards)
+ bank.get_reward_calculation_num_blocks(),
);
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during warm up epoch gives the expected result.
/// The num_credit_blocks should be 1 during warm up epoch.
#[test]
fn test_get_reward_distribution_num_blocks_warmup() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let bank = Bank::new_for_tests(&genesis_config);
let rewards = vec![];
assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
assert_eq!(bank.get_reward_calculation_num_blocks(), 1);
assert_eq!(
bank.get_reward_total_num_blocks(&rewards),
bank.get_reward_distribution_num_blocks(&rewards)
+ bank.get_reward_calculation_num_blocks(),
);
}
}

View File

@ -3,8 +3,8 @@ mod tests {
use {
crate::{
bank::{
epoch_accounts_hash_utils, test_utils as bank_test_utils, Bank, EpochRewardStatus,
StartBlockHeightAndRewards,
epoch_accounts_hash_utils, partitioned_epoch_rewards::StartBlockHeightAndRewards,
test_utils as bank_test_utils, Bank, EpochRewardStatus,
},
genesis_utils::activate_all_features,
serde_snapshot::{
@ -605,7 +605,7 @@ mod tests {
// This some what long test harness is required to freeze the ABI of
// Bank's serialization due to versioned nature
#[frozen_abi(digest = "7BH2s2Y1yKy396c3ixC4TTyvvpkyenAvWDSiZvY5yb7P")]
#[frozen_abi(digest = "8BVfyLYrPt1ranknjF4sLePjZaZjpKXXrHt4wKf47g3W")]
#[derive(Serialize, AbiExample)]
pub struct BankAbiTestWrapperNewer {
#[serde(serialize_with = "wrapper_newer")]

View File

@ -191,7 +191,7 @@ fn create_genesis_config_no_tx_fee(lamports: u64) -> (GenesisConfig, Keypair) {
(genesis_config, mint_keypair)
}
fn create_genesis_config(lamports: u64) -> (GenesisConfig, Keypair) {
pub(in crate::bank) fn create_genesis_config(lamports: u64) -> (GenesisConfig, Keypair) {
solana_sdk::genesis_config::create_genesis_config(lamports)
}
@ -12225,34 +12225,6 @@ fn test_rewards_point_calculation_empty() {
assert!(point_value.is_none());
}
#[test]
fn test_force_reward_interval_end() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
let expected_num = 100;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
bank.set_epoch_reward_status_active(vec![stake_rewards]);
assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
bank.force_reward_interval_end_for_tests();
assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
}
#[test]
fn test_is_partitioned_reward_feature_enable() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
assert!(!bank.is_partitioned_rewards_feature_enabled());
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());
assert!(bank.is_partitioned_rewards_feature_enabled());
}
/// Test that reward partition range panics when passing out of range partition index
#[test]
#[should_panic(expected = "index out of bounds: the len is 10 but the index is 15")]
@ -12276,24 +12248,6 @@ fn test_get_stake_rewards_partition_range_panic() {
let _range = &stake_rewards_bucket[15];
}
#[test]
fn test_deactivate_epoch_reward_status() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let mut bank = Bank::new_for_tests(&genesis_config);
let expected_num = 100;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
bank.set_epoch_reward_status_active(vec![stake_rewards]);
assert!(bank.get_reward_interval() == RewardInterval::InsideInterval);
bank.deactivate_epoch_reward_status();
assert!(bank.get_reward_interval() == RewardInterval::OutsideInterval);
}
#[test]
fn test_distribute_partitioned_epoch_rewards() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
@ -13286,127 +13240,6 @@ fn test_calc_vote_accounts_to_store_normal() {
}
}
impl Bank {
/// Return the total number of blocks in reward interval (including both calculation and crediting).
fn get_reward_total_num_blocks(&self, rewards: &StakeRewards) -> u64 {
self.get_reward_calculation_num_blocks() + self.get_reward_distribution_num_blocks(rewards)
}
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during normal epoch gives the expected result
#[test]
fn test_get_reward_distribution_num_blocks_normal() {
solana_logger::setup();
let (mut genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
let bank = Bank::new_for_tests(&genesis_config);
// Given 8k rewards, it will take 2 blocks to credit all the rewards
let expected_num = 8192;
let stake_rewards = (0..expected_num)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
assert_eq!(bank.get_reward_distribution_num_blocks(&stake_rewards), 2);
assert_eq!(bank.get_reward_calculation_num_blocks(), 1);
assert_eq!(
bank.get_reward_total_num_blocks(&stake_rewards),
bank.get_reward_distribution_num_blocks(&stake_rewards)
+ bank.get_reward_calculation_num_blocks(),
);
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during small epoch
/// The num_credit_blocks should be cap to 10% of the total number of blocks in the epoch.
#[test]
fn test_get_reward_distribution_num_blocks_cap() {
let (mut genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config.epoch_schedule = EpochSchedule::custom(32, 32, false);
// Config stake reward distribution to be 10 per block
let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone();
accounts_db_config.test_partitioned_epoch_rewards =
TestPartitionedEpochRewards::PartitionedEpochRewardsConfigRewardBlocks {
reward_calculation_num_blocks: 1,
stake_account_stores_per_block: 10,
};
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(),
);
let stake_account_stores_per_block = bank.partitioned_rewards_stake_account_stores_per_block();
assert_eq!(stake_account_stores_per_block, 10);
let check_num_reward_distribution_blocks =
|num_stakes: u64,
expected_num_reward_distribution_blocks: u64,
expected_num_reward_computation_blocks: u64| {
// Given the short epoch, i.e. 32 slots, we should cap the number of reward distribution blocks to 32/10 = 3.
let stake_rewards = (0..num_stakes)
.map(|_| StakeReward::new_random())
.collect::<Vec<_>>();
assert_eq!(
bank.get_reward_distribution_num_blocks(&stake_rewards),
expected_num_reward_distribution_blocks
);
assert_eq!(
bank.get_reward_calculation_num_blocks(),
expected_num_reward_computation_blocks
);
assert_eq!(
bank.get_reward_total_num_blocks(&stake_rewards),
bank.get_reward_distribution_num_blocks(&stake_rewards)
+ bank.get_reward_calculation_num_blocks(),
);
};
for test_record in [
// num_stakes, expected_num_reward_distribution_blocks, expected_num_reward_computation_blocks
(0, 1, 1),
(1, 1, 1),
(stake_account_stores_per_block, 1, 1),
(2 * stake_account_stores_per_block - 1, 2, 1),
(2 * stake_account_stores_per_block, 2, 1),
(3 * stake_account_stores_per_block - 1, 3, 1),
(3 * stake_account_stores_per_block, 3, 1),
(4 * stake_account_stores_per_block, 3, 1), // cap at 3
(5 * stake_account_stores_per_block, 3, 1), //cap at 3
] {
check_num_reward_distribution_blocks(test_record.0, test_record.1, test_record.2);
}
}
/// Test get_reward_distribution_num_blocks, get_reward_calculation_num_blocks, get_reward_total_num_blocks during warm up epoch gives the expected result.
/// The num_credit_blocks should be 1 during warm up epoch.
#[test]
fn test_get_reward_distribution_num_blocks_warmup() {
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
let bank = Bank::new_for_tests(&genesis_config);
let rewards = vec![];
assert_eq!(bank.get_reward_distribution_num_blocks(&rewards), 1);
assert_eq!(bank.get_reward_calculation_num_blocks(), 1);
assert_eq!(
bank.get_reward_total_num_blocks(&rewards),
bank.get_reward_distribution_num_blocks(&rewards)
+ bank.get_reward_calculation_num_blocks(),
);
}
#[test]
fn test_calculate_stake_vote_rewards() {
solana_logger::setup();

View File

@ -1,5 +1,5 @@
use {
crate::bank::StakeRewards,
crate::bank::partitioned_epoch_rewards::StakeRewards,
solana_sdk::{epoch_rewards_hasher::EpochRewardsHasher, hash::Hash},
};

View File

@ -5,7 +5,7 @@ use {
*,
},
crate::{
bank::EpochRewardStatus,
bank::partitioned_epoch_rewards::EpochRewardStatus,
stakes::{serde_stakes_enum_compat, StakesEnum},
},
solana_accounts_db::{accounts_hash::AccountsHash, ancestors::AncestorsForSerialization},