diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 27f288ce8e..06259db705 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -49,6 +49,7 @@ pub mod inline_spl_token_2022; pub mod loader_utils; pub mod message_processor; pub mod non_circulating_supply; +pub mod partitioned_rewards; pub mod prioritization_fee; pub mod prioritization_fee_cache; mod pubkey_bins; diff --git a/runtime/src/partitioned_rewards.rs b/runtime/src/partitioned_rewards.rs new file mode 100644 index 0000000000..125580e0a0 --- /dev/null +++ b/runtime/src/partitioned_rewards.rs @@ -0,0 +1,85 @@ +//! Code related to partitioned rewards distribution +//! +use solana_sdk::clock::Slot; + +#[allow(dead_code)] +#[derive(Debug)] +/// Configuration options for partitioned epoch rewards. +/// This struct allows various forms of testing, especially prior to feature activation. +pub(crate) struct PartitionedEpochRewardsConfig { + /// Number of blocks for reward calculation and storing vote accounts. + /// Distributing rewards to stake accounts begins AFTER this many blocks. + pub(crate) reward_calculation_num_blocks: Slot, + /// number of stake accounts to store in one block during partititioned reward interval + /// if force_one_slot_partitioned_rewards, this will usually be 1 + pub(crate) stake_account_stores_per_block: Slot, + /// if true, end of epoch bank rewards will force using partitioned rewards distribution. + /// see `set_test_enable_partitioned_rewards` + pub(crate) test_enable_partitioned_rewards: bool, + /// if true, end of epoch non-partitioned bank rewards will test the partitioned rewards distribution vote and stake accounts + /// This has a significant performance impact on the first slot in each new epoch. + pub(crate) test_compare_partitioned_epoch_rewards: bool, +} + +impl Default for PartitionedEpochRewardsConfig { + fn default() -> Self { + Self { + /// reward calculation happens synchronously during the first block of the epoch boundary. + /// So, # blocks for reward calculation is 1. + reward_calculation_num_blocks: 1, + /// # stake accounts to store in one block during partitioned reward interval + /// Target to store 64 rewards per entry/tick in a block. A block has a minimum of 64 + /// entries/tick. This gives 4096 total rewards to store in one block. + /// This constant affects consensus. + stake_account_stores_per_block: 4096, + test_enable_partitioned_rewards: false, + test_compare_partitioned_epoch_rewards: false, + } + } +} + +#[derive(Debug, Default, Clone, Copy)] +pub enum TestPartitionedEpochRewards { + #[default] + None, + CompareResults, + ForcePartitionedEpochRewardsInOneBlock, +} + +#[allow(dead_code)] +impl PartitionedEpochRewardsConfig { + pub(crate) fn new(test: TestPartitionedEpochRewards) -> Self { + match test { + TestPartitionedEpochRewards::None => Self::default(), + TestPartitionedEpochRewards::CompareResults => { + Self::set_test_compare_partitioned_epoch_rewards() + } + TestPartitionedEpochRewards::ForcePartitionedEpochRewardsInOneBlock => { + Self::set_test_enable_partitioned_rewards() + } + } + } + + /// All rewards will be distributed in the first block in the epoch, maching + /// consensus for the non-partitioned rewards, but running all the partitioned rewards + /// code. + fn set_test_enable_partitioned_rewards() -> Self { + Self { + reward_calculation_num_blocks: 0, + stake_account_stores_per_block: u64::MAX, + test_enable_partitioned_rewards: true, + // irrelevant if we are not running old code path + test_compare_partitioned_epoch_rewards: false, + } + } + + /// All rewards will be distributed in the first block in the epoch as normal. + /// Then, the partitioned rewards code will calculate expected results and compare to + /// the old code path's results. + fn set_test_compare_partitioned_epoch_rewards() -> Self { + Self { + test_compare_partitioned_epoch_rewards: true, + ..PartitionedEpochRewardsConfig::default() + } + } +}