From 998b3b0ada0d7adaad3926f3ffc52511c9c28636 Mon Sep 17 00:00:00 2001 From: Shannon Wells Date: Wed, 7 Jun 2023 15:38:28 -0700 Subject: [PATCH] Implement Staking Reward Eras basics (#1589) Implement the basic functionality of tracking and rotating Reward Era. Closes #1567 Does not include anything to do with the Reward Pool. - [x] Chain spec updated - [x] Design doc(s) updated - [x] Tests added --- pallets/capacity/src/lib.rs | 27 ++++++++++++++++++++++-- pallets/capacity/src/tests/eras_tests.rs | 25 ++++++++++++++++++++++ pallets/capacity/src/tests/mod.rs | 1 + pallets/capacity/src/types.rs | 27 +++++++++++++++++------- 4 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 pallets/capacity/src/tests/eras_tests.rs diff --git a/pallets/capacity/src/lib.rs b/pallets/capacity/src/lib.rs index 50b142ce25..a23c2a5a96 100644 --- a/pallets/capacity/src/lib.rs +++ b/pallets/capacity/src/lib.rs @@ -70,7 +70,7 @@ pub use common_primitives::{ #[cfg(feature = "runtime-benchmarks")] use common_primitives::benchmarks::RegisterProviderBenchmarkHelper; -use common_primitives::{capacity::StakingType, node::RewardEra}; +use common_primitives::capacity::StakingType; pub use pallet::*; pub use types::*; @@ -231,6 +231,12 @@ pub mod pallet { pub type EpochLength = StorageValue<_, T::BlockNumber, ValueQuery, EpochLengthDefault>; + /// Information for the current era + #[pallet::storage] + #[pallet::getter(fn get_current_era)] + pub type CurrentEraInfo = + StorageValue<_, RewardEraInfo, ValueQuery>; + // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] @@ -325,6 +331,7 @@ pub mod pallet { impl Hooks> for Pallet { fn on_initialize(current: T::BlockNumber) -> Weight { Self::start_new_epoch_if_needed(current) + .saturating_add(Self::start_new_reward_era_if_needed(current)) } } @@ -631,7 +638,23 @@ impl Pallet { .saturating_add(T::DbWeight::get().writes(2)) } else { // 1 for get_current_epoch_info, 1 for get_epoch_length - T::DbWeight::get().reads(2u64).saturating_add(RocksDbWeight::get().writes(1)) + T::DbWeight::get().reads(2).saturating_add(RocksDbWeight::get().writes(1)) + } + } + + fn start_new_reward_era_if_needed(current_block: T::BlockNumber) -> Weight { + let current_era_info: RewardEraInfo = Self::get_current_era(); + if current_block.saturating_sub(current_era_info.era_start) >= T::EraLength::get().into() { + CurrentEraInfo::::set(RewardEraInfo { + current_era: current_era_info.current_era.saturating_add(1u8.into()), + era_start: current_block, + }); + // TODO: modify reads/writes as needed when RewardPoolInfo stuff is added + T::WeightInfo::on_initialize() + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } else { + T::DbWeight::get().reads(2).saturating_add(RocksDbWeight::get().writes(1)) } } diff --git a/pallets/capacity/src/tests/eras_tests.rs b/pallets/capacity/src/tests/eras_tests.rs new file mode 100644 index 0000000000..2d362f7c55 --- /dev/null +++ b/pallets/capacity/src/tests/eras_tests.rs @@ -0,0 +1,25 @@ +use super::mock::*; +use crate::{ + tests::testing_utils::{run_to_block, system_run_to_block}, + Config, CurrentEraInfo, Error, Event, RewardEraInfo, +}; + +use frame_support::traits::Get; + +#[test] +fn start_new_era_if_needed() { + new_test_ext().execute_with(|| { + CurrentEraInfo::::set(RewardEraInfo { current_era: 1, era_start: 0 }); + system_run_to_block(9); + run_to_block(10); + let mut current_era_info = CurrentEraInfo::::get(); + assert_eq!(current_era_info.current_era, 2u32); + assert_eq!(current_era_info.era_start, 10u32); + + system_run_to_block(19); + run_to_block(20); + current_era_info = CurrentEraInfo::::get(); + assert_eq!(current_era_info.current_era, 3u32); + assert_eq!(current_era_info.era_start, 20u32); + }) +} diff --git a/pallets/capacity/src/tests/mod.rs b/pallets/capacity/src/tests/mod.rs index afef352dc8..68428be3c3 100644 --- a/pallets/capacity/src/tests/mod.rs +++ b/pallets/capacity/src/tests/mod.rs @@ -1,5 +1,6 @@ pub mod capacity_details_tests; pub mod epochs_tests; +mod eras_tests; pub mod mock; pub mod other_tests; pub mod replenishment_tests; diff --git a/pallets/capacity/src/types.rs b/pallets/capacity/src/types.rs index 18f292d0de..2f7738cdd5 100644 --- a/pallets/capacity/src/types.rs +++ b/pallets/capacity/src/types.rs @@ -29,10 +29,10 @@ pub struct StakingAccountDetails { /// What type of staking this account is doing pub staking_type: StakingType, /// The None or Some(number): never, or the last RewardEra that this account's rewards were claimed. - pub last_rewards_claimed_at: Option, + pub last_rewards_claimed_at: Option, /// Chunks that have been retargeted within T::UnstakingThawPeriod pub stake_change_unlocking: - BoundedVec, RewardEra>, T::MaxUnlockingChunks>, + BoundedVec, T::RewardEra>, T::MaxUnlockingChunks>, } /// The type that is used to record a single request for a number of tokens to be unlocked. @@ -260,9 +260,9 @@ pub struct StakingRewardClaim { /// The end state of the staking account if the operations are valid pub staking_account_end_state: StakingAccountDetails, /// The starting era for the claimed reward period, inclusive - pub from_era: RewardEra, + pub from_era: T::RewardEra, /// The ending era for the claimed reward period, inclusive - pub to_era: RewardEra, + pub to_era: T::RewardEra, } /// A trait that provides the Economic Model for Provider Boosting. @@ -270,15 +270,15 @@ pub trait StakingRewardsProvider { /// Return the size of the reward pool for the given era, in token /// Errors: /// - EraOutOfRange when `era` is prior to the history retention limit, or greater than the current Era. - fn reward_pool_size(era: RewardEra) -> BalanceOf; + fn reward_pool_size(era: T::RewardEra) -> BalanceOf; /// Return the total unclaimed reward in token for `accountId` for `from_era` --> `to_era`, inclusive /// Errors: /// - EraOutOfRange when from_era or to_era are prior to the history retention limit, or greater than the current Era. fn staking_reward_total( - account_id: AccountId, - from_era: RewardEra, - to_era: RewardEra, + account_id: T::AccountId, + from_era: T::RewardEra, + to_era: T::RewardEra, ) -> BalanceOf; /// Validate a payout claim for `accountId`, using `proof` and the provided `payload` StakingRewardClaim. @@ -303,5 +303,16 @@ pub trait StakingRewardsProvider { fn payout_eligible(account_id: AccountId) -> bool; } +/// The information needed to track a Reward Era +#[derive( + PartialEq, Eq, Clone, Default, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, +)] +pub struct RewardEraInfo { + /// the index of this era + pub current_era: RewardEra, + /// the starting block of this era + pub era_start: BlockNumber, +} + /// Needed data about a RewardPool for a given RewardEra. pub struct RewardPoolInfo {}