Skip to content

Commit

Permalink
Move sysvar submodule; reorg partitioned epoch rewards runtime code, …
Browse files Browse the repository at this point in the history
…2 of 5 (#520)

* Add sysvar sub-submodule

* Move sysvar methods to sub-submodule

* Move unit test to sysvar sub-submodule

* Add new partitioned_epoch_rewards::sysvar method

* Remove superfluous method
  • Loading branch information
CriesofCarrots authored Apr 2, 2024
1 parent c29a239 commit 16c684e
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 156 deletions.
93 changes: 6 additions & 87 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1477,27 +1477,6 @@ impl Bank {
);
}

/// partitioned reward distribution is complete.
/// So, deactivate the epoch rewards sysvar.
fn deactivate_epoch_reward_status(&mut self) {
assert!(matches!(
self.epoch_reward_status,
EpochRewardStatus::Active(_)
));
self.epoch_reward_status = EpochRewardStatus::Inactive;
if let Some(account) = self.get_account(&sysvar::epoch_rewards::id()) {
if account.lamports() > 0 {
info!(
"burning {} extra lamports in EpochRewards sysvar account at slot {}",
account.lamports(),
self.slot()
);
self.log_epoch_rewards_sysvar("burn");
self.burn_and_purge_account(&sysvar::epoch_rewards::id(), account);
}
}
}

/// Begin the process of calculating and distributing rewards.
/// This process can take multiple slots.
fn begin_partitioned_rewards(
Expand Down Expand Up @@ -1571,7 +1550,12 @@ impl Bank {
("start_block_height", start_block_height, i64),
);

self.deactivate_epoch_reward_status();
assert!(matches!(
self.epoch_reward_status,
EpochRewardStatus::Active(_)
));
self.epoch_reward_status = EpochRewardStatus::Inactive;
self.destroy_epoch_rewards_sysvar();
}
}

Expand Down Expand Up @@ -3440,71 +3424,6 @@ impl Bank {
report_partitioned_reward_metrics(self, metrics);
}

/// 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()) {
let epoch_rewards: sysvar::epoch_rewards::EpochRewards =
from_account(&account).unwrap();
info!(
"{prefix} epoch_rewards sysvar: {:?}",
(account.lamports(), epoch_rewards)
);
} else {
info!("{prefix} epoch_rewards sysvar: none");
}
}

/// Create EpochRewards sysvar with calculated rewards
fn create_epoch_rewards_sysvar(
&self,
total_rewards: u64,
distributed_rewards: 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_starting_block_height,
active: true,
..sysvar::epoch_rewards::EpochRewards::default()
};

self.update_sysvar_account(&sysvar::epoch_rewards::id(), |account| {
let mut inherited_account_fields =
self.inherit_specially_retained_account_fields(account);

assert!(total_rewards >= distributed_rewards);
// set the account lamports to the undistributed rewards
inherited_account_fields.0 = total_rewards - distributed_rewards;
create_account(&epoch_rewards, inherited_account_fields)
});

self.log_epoch_rewards_sysvar("create");
}

/// Update EpochRewards sysvar with distributed rewards
fn update_epoch_rewards_sysvar(&self, distributed: u64) {
assert!(self.is_partitioned_rewards_code_enabled());

let mut epoch_rewards: sysvar::epoch_rewards::EpochRewards =
from_account(&self.get_account(&sysvar::epoch_rewards::id()).unwrap()).unwrap();
epoch_rewards.distribute(distributed);

self.update_sysvar_account(&sysvar::epoch_rewards::id(), |account| {
let mut inherited_account_fields =
self.inherit_specially_retained_account_fields(account);

let lamports = inherited_account_fields.0;
assert!(lamports >= distributed);
inherited_account_fields.0 = lamports - distributed;
create_account(&epoch_rewards, inherited_account_fields)
});

self.log_epoch_rewards_sysvar("update");
}

fn update_recent_blockhashes_locked(&self, locked_blockhash_queue: &BlockhashQueue) {
#[allow(deprecated)]
self.update_sysvar_account(&sysvar::recent_blockhashes::id(), |account| {
Expand Down
20 changes: 2 additions & 18 deletions runtime/src/bank/partitioned_epoch_rewards/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod sysvar;

use {
super::Bank,
crate::{stake_account::StakeAccount, stake_history::StakeHistory},
Expand Down Expand Up @@ -232,24 +234,6 @@ mod tests {
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]
Expand Down
154 changes: 154 additions & 0 deletions runtime/src/bank/partitioned_epoch_rewards/sysvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use {
super::Bank,
log::info,
solana_sdk::{
account::{
create_account_shared_data_with_fields as create_account, from_account, ReadableAccount,
},
sysvar,
},
};

impl Bank {
/// 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()) {
let epoch_rewards: sysvar::epoch_rewards::EpochRewards =
from_account(&account).unwrap();
info!(
"{prefix} epoch_rewards sysvar: {:?}",
(account.lamports(), epoch_rewards)
);
} else {
info!("{prefix} epoch_rewards sysvar: none");
}
}

/// Create EpochRewards sysvar with calculated rewards
pub(in crate::bank) fn create_epoch_rewards_sysvar(
&self,
total_rewards: u64,
distributed_rewards: 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_starting_block_height,
active: true,
..sysvar::epoch_rewards::EpochRewards::default()
};

self.update_sysvar_account(&sysvar::epoch_rewards::id(), |account| {
let mut inherited_account_fields =
self.inherit_specially_retained_account_fields(account);

assert!(total_rewards >= distributed_rewards);
// set the account lamports to the undistributed rewards
inherited_account_fields.0 = total_rewards - distributed_rewards;
create_account(&epoch_rewards, inherited_account_fields)
});

self.log_epoch_rewards_sysvar("create");
}

/// Update EpochRewards sysvar with distributed rewards
pub(in crate::bank) fn update_epoch_rewards_sysvar(&self, distributed: u64) {
assert!(self.is_partitioned_rewards_code_enabled());

let mut epoch_rewards: sysvar::epoch_rewards::EpochRewards =
from_account(&self.get_account(&sysvar::epoch_rewards::id()).unwrap()).unwrap();
epoch_rewards.distribute(distributed);

self.update_sysvar_account(&sysvar::epoch_rewards::id(), |account| {
let mut inherited_account_fields =
self.inherit_specially_retained_account_fields(account);

let lamports = inherited_account_fields.0;
assert!(lamports >= distributed);
inherited_account_fields.0 = lamports - distributed;
create_account(&epoch_rewards, inherited_account_fields)
});

self.log_epoch_rewards_sysvar("update");
}

pub(in crate::bank) fn destroy_epoch_rewards_sysvar(&self) {
if let Some(account) = self.get_account(&sysvar::epoch_rewards::id()) {
if account.lamports() > 0 {
info!(
"burning {} extra lamports in EpochRewards sysvar account at slot {}",
account.lamports(),
self.slot()
);
self.log_epoch_rewards_sysvar("burn");
self.burn_and_purge_account(&sysvar::epoch_rewards::id(), account);
}
}
}
}

#[cfg(test)]
mod tests {
use {
super::*,
crate::bank::tests::create_genesis_config,
solana_sdk::{
epoch_schedule::EpochSchedule, feature_set, hash::Hash, native_token::LAMPORTS_PER_SOL,
},
};

/// Test `EpochRewards` sysvar creation, distribution, and burning.
/// This test covers the following epoch_rewards_sysvar bank member functions, i.e.
/// `create_epoch_rewards_sysvar`, `update_epoch_rewards_sysvar`, `burn_and_purge_account`.
#[test]
fn test_epoch_rewards_sysvar() {
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 mut bank = Bank::new_for_tests(&genesis_config);
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());

let total_rewards = 1_000_000_000; // a large rewards so that the sysvar account is rent-exempted.

// 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,
active: true,
};

bank.create_epoch_rewards_sysvar(total_rewards, 10, 42);
let account = bank.get_account(&sysvar::epoch_rewards::id()).unwrap();
assert_eq!(account.lamports(), total_rewards - 10);
let epoch_rewards: sysvar::epoch_rewards::EpochRewards = from_account(&account).unwrap();
assert_eq!(epoch_rewards, expected_epoch_rewards);

// make a distribution from epoch rewards sysvar
bank.update_epoch_rewards_sysvar(10);
let account = bank.get_account(&sysvar::epoch_rewards::id()).unwrap();
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,
active: true,
};
assert_eq!(epoch_rewards, expected_epoch_rewards);

// burn epoch rewards sysvar
bank.burn_and_purge_account(&sysvar::epoch_rewards::id(), account);
let account = bank.get_account(&sysvar::epoch_rewards::id());
assert!(account.is_none());
}
}
51 changes: 0 additions & 51 deletions runtime/src/bank/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12650,57 +12650,6 @@ fn test_rewards_computation_and_partitioned_distribution_two_blocks() {
}
}

/// Test `EpochRewards` sysvar creation, distribution, and burning.
/// This test covers the following epoch_rewards_sysvar bank member functions, i.e.
/// `create_epoch_rewards_sysvar`, `update_epoch_rewards_sysvar`, `burn_and_purge_account`.
#[test]
fn test_epoch_rewards_sysvar() {
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 mut bank = Bank::new_for_tests(&genesis_config);
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());

let total_rewards = 1_000_000_000; // a large rewards so that the sysvar account is rent-exempted.

// 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,
active: true,
};

bank.create_epoch_rewards_sysvar(total_rewards, 10, 42);
let account = bank.get_account(&sysvar::epoch_rewards::id()).unwrap();
assert_eq!(account.lamports(), total_rewards - 10);
let epoch_rewards: sysvar::epoch_rewards::EpochRewards = from_account(&account).unwrap();
assert_eq!(epoch_rewards, expected_epoch_rewards);

// make a distribution from epoch rewards sysvar
bank.update_epoch_rewards_sysvar(10);
let account = bank.get_account(&sysvar::epoch_rewards::id()).unwrap();
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,
active: true,
};
assert_eq!(epoch_rewards, expected_epoch_rewards);

// burn epoch rewards sysvar
bank.burn_and_purge_account(&sysvar::epoch_rewards::id(), account);
let account = bank.get_account(&sysvar::epoch_rewards::id());
assert!(account.is_none());
}

/// Test that program execution that involves stake accounts should fail during reward period.
/// Any programs, which result in stake account changes, will throw `ProgramExecutionTemporarilyRestricted` error when
/// in reward period.
Expand Down

0 comments on commit 16c684e

Please sign in to comment.