Skip to content

Commit

Permalink
Implement Staking Reward Eras basics (#1589)
Browse files Browse the repository at this point in the history
Implement the basic functionality of tracking and rotating Reward Era. Closes #1567 
Does not include anything to do with the Reward Pool.

# Checklist
- [x] Chain spec updated
- [x] Design doc(s) updated 
- [x] Tests added
  • Loading branch information
shannonwells committed Jul 3, 2023
1 parent f25b57b commit ae88a01
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 22 deletions.
28 changes: 16 additions & 12 deletions designdocs/capacity_staking_rewards_implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct StakingAccountDetails {
pub total: BalanceOf<T>,
pub unlocking: BoundedVec<UnlockChunk<BalanceOf<T>, T::EpochNumber>, T::MaxUnlockingChunks>,
/// The number of the last StakingEra that this account's rewards were claimed.
pub last_rewards_claimed_at: Option<T::StakingEra>, // NEW None means never rewarded, Some(RewardEra) means last rewarded RewardEra.
pub last_rewards_claimed_at: Option<T::RewardEra>, // NEW None means never rewarded, Some(RewardEra) means last rewarded RewardEra.
/// What type of staking this account is doing
pub staking_type: StakingType, // NEW
/// staking amounts that have been retargeted are prevented from being retargeted again for the
Expand Down Expand Up @@ -94,15 +94,12 @@ pub struct StakingRewardClaim<T: Config> {
/// 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: AtLeast32BitUnsigned,
pub from_era: T::RewardEra,
/// The ending era for the claimed reward period, inclusive
pub to_era: RewardEra,
pub to_era: T::RewardEra,
}

pub trait StakingRewardsProvider {
type Balance;
type AccountId;
type RewardEra;
pub trait StakingRewardsProvider<T: Config> {

/// Return the size of the reward pool for the given era, in token
/// Errors:
Expand All @@ -112,12 +109,12 @@ pub trait StakingRewardsProvider {
/// Return the total unclaimed reward in token for `account_id` for `fromEra` --> `toEra`, inclusive
/// Errors:
/// - EraOutOfRange when fromEra or toEra are prior to the history retention limit, or greater than the current RewardEra.
fn staking_reward_total(account_id: AccountId, fromEra: RewardEra, toEra: RewardEra);
fn staking_reward_total(account_id: T::AccountId, fromEra: T::RewardEra, toEra: T::RewardEra);

/// Validate a payout claim for `account_id`, using `proof` and the provided `payload` StakingRewardClaim.
/// Returns whether the claim passes validation. Accounts must first pass `payoutEligible` test.
/// Errors: None
fn validate_staking_reward_claim(account_id: AccountIdOf<T>, proof: Hash, payload: StakingRewardClaim<T>) -> bool;
fn validate_staking_reward_claim(account_id: T::AccountID, proof: Hash, payload: StakingRewardClaim<T>) -> bool;
}
```

Expand Down Expand Up @@ -173,14 +170,21 @@ pub struct RewardPoolInfo<T: Config> {
pub type StakingRewardPool<T: Config> = <StorageMap<_, Twox64Concat, RewardEra, RewardPoolInfo<T>;
```

### NEW: CurrentEra
Incremented, like CurrentEpoch, tracks the current RewardEra number.
### NEW: CurrentEra, RewardEraInfo
Incremented, like CurrentEpoch, tracks the current RewardEra number and the block when it started.
```rust
#[pallet::storage]
#[pallet::whitelist_storage]
#[pallet::getter(fn get_current_era)]
/// Similar to CurrentEpoch
pub type CurrentEra<T:Config> = StorageValue<_, T::RewardEra, ValueQuery>;
pub type CurrentEraInfo<T:Config> = StorageValue<_, T::RewardEraInfo, ValueQuery>;

pub struct RewardEraInfo<RewardEra, BlockNumber> {
/// the index of this era
pub current_era: RewardEra,
/// the starting block of this era
pub era_start: BlockNumber,
}
```

### NEW: Error enums
Expand Down
27 changes: 25 additions & 2 deletions pallets/capacity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -231,6 +231,12 @@ pub mod pallet {
pub type EpochLength<T: Config> =
StorageValue<_, T::BlockNumber, ValueQuery, EpochLengthDefault<T>>;

/// Information for the current era
#[pallet::storage]
#[pallet::getter(fn get_current_era)]
pub type CurrentEraInfo<T: Config> =
StorageValue<_, RewardEraInfo<T::RewardEra, T::BlockNumber>, ValueQuery>;

// Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and
// method.
#[pallet::pallet]
Expand Down Expand Up @@ -327,6 +333,7 @@ pub mod pallet {
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(current: T::BlockNumber) -> Weight {
Self::start_new_epoch_if_needed(current)
.saturating_add(Self::start_new_reward_era_if_needed(current))
}
}

Expand Down Expand Up @@ -633,7 +640,23 @@ impl<T: Config> Pallet<T> {
.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<T::RewardEra, T::BlockNumber> = Self::get_current_era();
if current_block.saturating_sub(current_era_info.era_start) >= T::EraLength::get().into() {
CurrentEraInfo::<T>::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))
}
}

Expand Down
25 changes: 25 additions & 0 deletions pallets/capacity/src/tests/eras_tests.rs
Original file line number Diff line number Diff line change
@@ -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::<Test>::set(RewardEraInfo { current_era: 1, era_start: 0 });
system_run_to_block(9);
run_to_block(10);
let mut current_era_info = CurrentEraInfo::<Test>::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::<Test>::get();
assert_eq!(current_era_info.current_era, 3u32);
assert_eq!(current_era_info.era_start, 20u32);
})
}
1 change: 1 addition & 0 deletions pallets/capacity/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
27 changes: 19 additions & 8 deletions pallets/capacity/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ pub struct StakingAccountDetails<T: Config> {
/// 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<RewardEra>,
pub last_rewards_claimed_at: Option<T::RewardEra>,
/// Chunks that have been retargeted within T::UnstakingThawPeriod
pub stake_change_unlocking:
BoundedVec<UnlockChunk<BalanceOf<T>, RewardEra>, T::MaxUnlockingChunks>,
BoundedVec<UnlockChunk<BalanceOf<T>, T::RewardEra>, T::MaxUnlockingChunks>,
}

/// The type that is used to record a single request for a number of tokens to be unlocked.
Expand Down Expand Up @@ -260,25 +260,25 @@ pub struct StakingRewardClaim<T: Config> {
/// The end state of the staking account if the operations are valid
pub staking_account_end_state: StakingAccountDetails<T>,
/// 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.
pub trait StakingRewardsProvider<T: Config> {
/// 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<T>;
fn reward_pool_size(era: T::RewardEra) -> BalanceOf<T>;

/// 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<T>;

/// Validate a payout claim for `accountId`, using `proof` and the provided `payload` StakingRewardClaim.
Expand All @@ -303,5 +303,16 @@ pub trait StakingRewardsProvider<T: Config> {
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<RewardEra, BlockNumber> {
/// 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 {}

0 comments on commit ae88a01

Please sign in to comment.