diff --git a/account-decoder/src/parse_sysvar.rs b/account-decoder/src/parse_sysvar.rs index 35746949c7f9ef..e308dfe07f9c36 100644 --- a/account-decoder/src/parse_sysvar.rs +++ b/account-decoder/src/parse_sysvar.rs @@ -372,9 +372,11 @@ mod test { ); let epoch_rewards = EpochRewards { + distribution_starting_block_height: 42, total_rewards: 100, distributed_rewards: 20, - distribution_complete_block_height: 42, + active: true, + ..EpochRewards::default() }; let epoch_rewards_sysvar = create_account_for_test(&epoch_rewards); assert_eq!( diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 4a166fa1cf9996..0d51e599d9366c 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -3317,16 +3317,20 @@ mod tests { src_rent.burn_percent = 3; let mut src_rewards = create_filled_type::(false); + src_rewards.distribution_starting_block_height = 42; + src_rewards.num_partitions = 2; + src_rewards.parent_blockhash = Hash::new(&[3; 32]); + src_rewards.total_points = 4; src_rewards.total_rewards = 100; src_rewards.distributed_rewards = 10; - src_rewards.distribution_complete_block_height = 42; + src_rewards.active = true; let mut sysvar_cache = SysvarCache::default(); sysvar_cache.set_clock(src_clock.clone()); sysvar_cache.set_epoch_schedule(src_epochschedule.clone()); sysvar_cache.set_fees(src_fees.clone()); sysvar_cache.set_rent(src_rent.clone()); - sysvar_cache.set_epoch_rewards(src_rewards); + sysvar_cache.set_epoch_rewards(src_rewards.clone()); let transaction_accounts = vec![ ( @@ -3519,10 +3523,14 @@ mod tests { assert_eq!(got_rewards, src_rewards); let mut clean_rewards = create_filled_type::(true); + clean_rewards.distribution_starting_block_height = + src_rewards.distribution_starting_block_height; + clean_rewards.num_partitions = src_rewards.num_partitions; + clean_rewards.parent_blockhash = src_rewards.parent_blockhash; + clean_rewards.total_points = src_rewards.total_points; clean_rewards.total_rewards = src_rewards.total_rewards; clean_rewards.distributed_rewards = src_rewards.distributed_rewards; - clean_rewards.distribution_complete_block_height = - src_rewards.distribution_complete_block_height; + clean_rewards.active = src_rewards.active; assert!(are_bytes_equal(&got_rewards, &clean_rewards)); } } diff --git a/programs/sbf/rust/sysvar/tests/lib.rs b/programs/sbf/rust/sysvar/tests/lib.rs index b99b89e75c9648..ffa2f625b6d03d 100644 --- a/programs/sbf/rust/sysvar/tests/lib.rs +++ b/programs/sbf/rust/sysvar/tests/lib.rs @@ -27,9 +27,11 @@ async fn test_sysvars() { ); let epoch_rewards = epoch_rewards::EpochRewards { + distribution_starting_block_height: 42, total_rewards: 100, distributed_rewards: 50, - distribution_complete_block_height: 42, + active: true, + ..epoch_rewards::EpochRewards::default() }; program_test.add_sysvar_account(epoch_rewards::id(), &epoch_rewards); let (mut banks_client, payer, recent_blockhash) = program_test.start().await; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index cfcd32ef7ff456..c8afb7406164ae 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1673,13 +1673,12 @@ impl Bank { let slot = self.slot(); let credit_start = self.block_height() + self.get_reward_calculation_num_blocks(); - let credit_end_exclusive = credit_start + stake_rewards_by_partition.len() as u64; self.set_epoch_reward_status_active(stake_rewards_by_partition); // create EpochRewards sysvar that holds the balance of undistributed rewards with - // (total_rewards, distributed_rewards, credit_end_exclusive), total capital will increase by (total_rewards - distributed_rewards) - self.create_epoch_rewards_sysvar(total_rewards, distributed_rewards, credit_end_exclusive); + // (total_rewards, distributed_rewards, credit_start), total capital will increase by (total_rewards - distributed_rewards) + self.create_epoch_rewards_sysvar(total_rewards, distributed_rewards, credit_start); datapoint_info!( "epoch-rewards-status-update", @@ -3615,14 +3614,16 @@ impl Bank { &self, total_rewards: u64, distributed_rewards: u64, - distribution_complete_block_height: u64, + distribution_starting_block_height: u64, ) { assert!(self.is_partitioned_rewards_code_enabled()); let epoch_rewards = sysvar::epoch_rewards::EpochRewards { total_rewards, distributed_rewards, - distribution_complete_block_height, + distribution_starting_block_height, + active: true, + ..sysvar::epoch_rewards::EpochRewards::default() }; self.update_sysvar_account(&sysvar::epoch_rewards::id(), |account| { diff --git a/runtime/src/bank/sysvar_cache.rs b/runtime/src/bank/sysvar_cache.rs index 3e0f9a93ddef2a..e45f64e96aaf3f 100644 --- a/runtime/src/bank/sysvar_cache.rs +++ b/runtime/src/bank/sysvar_cache.rs @@ -6,7 +6,7 @@ mod tests { use { super::*, solana_sdk::{ - feature_set, genesis_config::create_genesis_config, pubkey::Pubkey, + feature_set, genesis_config::create_genesis_config, hash::Hash, pubkey::Pubkey, sysvar::epoch_rewards::EpochRewards, }, std::sync::Arc, @@ -121,14 +121,18 @@ mod tests { // inject a reward sysvar for test bank1.activate_feature(&feature_set::enable_partitioned_epoch_reward::id()); let expected_epoch_rewards = EpochRewards { + distribution_starting_block_height: 42, + num_partitions: 0, + parent_blockhash: Hash::default(), + total_points: 0, total_rewards: 100, distributed_rewards: 10, - distribution_complete_block_height: 42, + active: true, }; bank1.create_epoch_rewards_sysvar( expected_epoch_rewards.total_rewards, expected_epoch_rewards.distributed_rewards, - expected_epoch_rewards.distribution_complete_block_height, + expected_epoch_rewards.distribution_starting_block_height, ); bank1 diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index bdacbb1304a028..0fdfb968149bbd 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -12715,9 +12715,13 @@ fn test_epoch_rewards_sysvar() { // create epoch rewards sysvar let expected_epoch_rewards = sysvar::epoch_rewards::EpochRewards { + distribution_starting_block_height: 42, + num_partitions: 0, + parent_blockhash: Hash::default(), + total_points: 0, total_rewards, distributed_rewards: 10, - distribution_complete_block_height: 42, + active: true, }; bank.create_epoch_rewards_sysvar(total_rewards, 10, 42); @@ -12732,9 +12736,13 @@ fn test_epoch_rewards_sysvar() { assert_eq!(account.lamports(), total_rewards - 20); let epoch_rewards: sysvar::epoch_rewards::EpochRewards = from_account(&account).unwrap(); let expected_epoch_rewards = sysvar::epoch_rewards::EpochRewards { + distribution_starting_block_height: 42, + num_partitions: 0, + parent_blockhash: Hash::default(), + total_points: 0, total_rewards, distributed_rewards: 20, - distribution_complete_block_height: 42, + active: true, }; assert_eq!(epoch_rewards, expected_epoch_rewards); diff --git a/sdk/program/src/epoch_rewards.rs b/sdk/program/src/epoch_rewards.rs index e3229486e0e0dc..b0628ecd2eff2a 100644 --- a/sdk/program/src/epoch_rewards.rs +++ b/sdk/program/src/epoch_rewards.rs @@ -6,20 +6,37 @@ //! //! [`sysvar::epoch_rewards`]: crate::sysvar::epoch_rewards -use std::ops::AddAssign; +use {crate::hash::Hash, solana_sdk_macro::CloneZeroed, std::ops::AddAssign}; -#[repr(C)] -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone, Copy, AbiExample)] +#[repr(C, align(16))] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, AbiExample, CloneZeroed)] pub struct EpochRewards { - /// total rewards for the current epoch, in lamports + /// The starting block height of the rewards distribution in the current + /// epoch + pub distribution_starting_block_height: u64, + + /// Number of partitions in the rewards distribution in the current epoch, + /// used to generate an EpochRewardsHasher + pub num_partitions: u64, + + /// The blockhash of the parent block of the first block in the epoch, used + /// to seed an EpochRewardsHasher + pub parent_blockhash: Hash, + + /// The total rewards points calculated for the current epoch, where points + /// equals the sum of (delegated stake * credits observed) for all + /// delegations + pub total_points: u128, + + /// The total rewards for the current epoch, in lamports pub total_rewards: u64, - /// distributed rewards for the current epoch, in lamports + /// The rewards currently distributed for the current epoch, in lamports pub distributed_rewards: u64, - /// distribution of all staking rewards for the current - /// epoch will be completed at this block height - pub distribution_complete_block_height: u64, + /// Whether the rewards period (including calculation and distribution) is + /// active + pub active: bool, } impl EpochRewards { @@ -38,12 +55,13 @@ mod tests { pub fn new( total_rewards: u64, distributed_rewards: u64, - distribution_complete_block_height: u64, + distribution_starting_block_height: u64, ) -> Self { Self { total_rewards, distributed_rewards, - distribution_complete_block_height, + distribution_starting_block_height, + ..Self::default() } } } @@ -54,7 +72,7 @@ mod tests { assert_eq!(epoch_rewards.total_rewards, 100); assert_eq!(epoch_rewards.distributed_rewards, 0); - assert_eq!(epoch_rewards.distribution_complete_block_height, 64); + assert_eq!(epoch_rewards.distribution_starting_block_height, 64); } #[test] diff --git a/sdk/program/src/sysvar/epoch_rewards.rs b/sdk/program/src/sysvar/epoch_rewards.rs index 1d43fbad8a9b08..c8aa7bfbbc88e3 100755 --- a/sdk/program/src/sysvar/epoch_rewards.rs +++ b/sdk/program/src/sysvar/epoch_rewards.rs @@ -1,11 +1,19 @@ //! Epoch rewards for current epoch //! //! The _epoch rewards_ sysvar provides access to the [`EpochRewards`] type, -//! which tracks the progress of epoch rewards distribution. It includes the -//! - total rewards for the current epoch, in lamports -//! - rewards for the current epoch distributed so far, in lamports -//! - distribution completed block height, i.e. distribution of all staking rewards for the current -//! epoch will be completed at this block height +//! which tracks whether the rewards period (including calculation and +//! distribution) is in progress, as well as the details needed to resume +//! distribution when starting from a snapshot during the rewards period. The +//! sysvar is repopulated at the start of the first block of each epoch. +//! Therefore, the sysvar contains data about the current epoch until a new +//! epoch begins. Fields in the sysvar include: +//! - distribution starting block height +//! - the number of partitions in the distribution +//! - the parent-blockhash seed used to generate the partition hasher +//! - the total rewards points calculated for the epoch +//! - total rewards for epoch, in lamports +//! - rewards for the epoch distributed so far, in lamports +//! - whether the rewards period is active //! //! [`EpochRewards`] implements [`Sysvar::get`] and can be loaded efficiently without //! passing the sysvar account ID to the program. @@ -43,9 +51,16 @@ //! # //! # use solana_program::sysvar::SysvarId; //! # let p = EpochRewards::id(); -//! # let l = &mut 1120560; -//! # let d = &mut vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0]; -//! # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0); +//! # let l = &mut 1559040; +//! # let epoch_rewards = EpochRewards { +//! # distribution_starting_block_height: 42, +//! # total_rewards: 100, +//! # distributed_rewards: 10, +//! # active: true, +//! # ..EpochRewards::default() +//! # }; +//! # let mut d: Vec = bincode::serialize(&epoch_rewards).unwrap(); +//! # let a = AccountInfo::new(&p, false, false, l, &mut d, &p, false, 0); //! # let accounts = &[a.clone(), a]; //! # process_instruction( //! # &Pubkey::new_unique(), @@ -86,9 +101,16 @@ //! # //! # use solana_program::sysvar::SysvarId; //! # let p = EpochRewards::id(); -//! # let l = &mut 1120560; -//! # let d = &mut vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0]; -//! # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0); +//! # let l = &mut 1559040; +//! # let epoch_rewards = EpochRewards { +//! # distribution_starting_block_height: 42, +//! # total_rewards: 100, +//! # distributed_rewards: 10, +//! # active: true, +//! # ..EpochRewards::default() +//! # }; +//! # let mut d: Vec = bincode::serialize(&epoch_rewards).unwrap(); +//! # let a = AccountInfo::new(&p, false, false, l, &mut d, &p, false, 0); //! # let accounts = &[a.clone(), a]; //! # process_instruction( //! # &Pubkey::new_unique(), @@ -109,9 +131,17 @@ //! # use anyhow::Result; //! # //! fn print_sysvar_epoch_rewards(client: &RpcClient) -> Result<()> { +//! # let epoch_rewards = EpochRewards { +//! # distribution_starting_block_height: 42, +//! # total_rewards: 100, +//! # distributed_rewards: 10, +//! # active: true, +//! # ..EpochRewards::default() +//! # }; +//! # let data: Vec = bincode::serialize(&epoch_rewards)?; //! # client.set_get_account_response(epoch_rewards::ID, Account { //! # lamports: 1120560, -//! # data: vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0], +//! # data, //! # owner: solana_sdk::system_program::ID, //! # executable: false, //! # rent_epoch: 307,