Skip to content

Commit ae88a01

Browse files
committed
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. # Checklist - [x] Chain spec updated - [x] Design doc(s) updated - [x] Tests added
1 parent f25b57b commit ae88a01

File tree

5 files changed

+86
-22
lines changed

5 files changed

+86
-22
lines changed

designdocs/capacity_staking_rewards_implementation.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub struct StakingAccountDetails {
3737
pub total: BalanceOf<T>,
3838
pub unlocking: BoundedVec<UnlockChunk<BalanceOf<T>, T::EpochNumber>, T::MaxUnlockingChunks>,
3939
/// The number of the last StakingEra that this account's rewards were claimed.
40-
pub last_rewards_claimed_at: Option<T::StakingEra>, // NEW None means never rewarded, Some(RewardEra) means last rewarded RewardEra.
40+
pub last_rewards_claimed_at: Option<T::RewardEra>, // NEW None means never rewarded, Some(RewardEra) means last rewarded RewardEra.
4141
/// What type of staking this account is doing
4242
pub staking_type: StakingType, // NEW
4343
/// staking amounts that have been retargeted are prevented from being retargeted again for the
@@ -94,15 +94,12 @@ pub struct StakingRewardClaim<T: Config> {
9494
/// The end state of the staking account if the operations are valid
9595
pub staking_account_end_state: StakingAccountDetails,
9696
/// The starting era for the claimed reward period, inclusive
97-
pub from_era: AtLeast32BitUnsigned,
97+
pub from_era: T::RewardEra,
9898
/// The ending era for the claimed reward period, inclusive
99-
pub to_era: RewardEra,
99+
pub to_era: T::RewardEra,
100100
}
101101

102-
pub trait StakingRewardsProvider {
103-
type Balance;
104-
type AccountId;
105-
type RewardEra;
102+
pub trait StakingRewardsProvider<T: Config> {
106103

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

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

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

176-
### NEW: CurrentEra
177-
Incremented, like CurrentEpoch, tracks the current RewardEra number.
173+
### NEW: CurrentEra, RewardEraInfo
174+
Incremented, like CurrentEpoch, tracks the current RewardEra number and the block when it started.
178175
```rust
179176
#[pallet::storage]
180177
#[pallet::whitelist_storage]
181178
#[pallet::getter(fn get_current_era)]
182179
/// Similar to CurrentEpoch
183-
pub type CurrentEra<T:Config> = StorageValue<_, T::RewardEra, ValueQuery>;
180+
pub type CurrentEraInfo<T:Config> = StorageValue<_, T::RewardEraInfo, ValueQuery>;
181+
182+
pub struct RewardEraInfo<RewardEra, BlockNumber> {
183+
/// the index of this era
184+
pub current_era: RewardEra,
185+
/// the starting block of this era
186+
pub era_start: BlockNumber,
187+
}
184188
```
185189

186190
### NEW: Error enums

pallets/capacity/src/lib.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub use common_primitives::{
7070

7171
#[cfg(feature = "runtime-benchmarks")]
7272
use common_primitives::benchmarks::RegisterProviderBenchmarkHelper;
73-
use common_primitives::{capacity::StakingType, node::RewardEra};
73+
use common_primitives::capacity::StakingType;
7474

7575
pub use pallet::*;
7676
pub use types::*;
@@ -231,6 +231,12 @@ pub mod pallet {
231231
pub type EpochLength<T: Config> =
232232
StorageValue<_, T::BlockNumber, ValueQuery, EpochLengthDefault<T>>;
233233

234+
/// Information for the current era
235+
#[pallet::storage]
236+
#[pallet::getter(fn get_current_era)]
237+
pub type CurrentEraInfo<T: Config> =
238+
StorageValue<_, RewardEraInfo<T::RewardEra, T::BlockNumber>, ValueQuery>;
239+
234240
// Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and
235241
// method.
236242
#[pallet::pallet]
@@ -327,6 +333,7 @@ pub mod pallet {
327333
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
328334
fn on_initialize(current: T::BlockNumber) -> Weight {
329335
Self::start_new_epoch_if_needed(current)
336+
.saturating_add(Self::start_new_reward_era_if_needed(current))
330337
}
331338
}
332339

@@ -633,7 +640,23 @@ impl<T: Config> Pallet<T> {
633640
.saturating_add(T::DbWeight::get().writes(2))
634641
} else {
635642
// 1 for get_current_epoch_info, 1 for get_epoch_length
636-
T::DbWeight::get().reads(2u64).saturating_add(RocksDbWeight::get().writes(1))
643+
T::DbWeight::get().reads(2).saturating_add(RocksDbWeight::get().writes(1))
644+
}
645+
}
646+
647+
fn start_new_reward_era_if_needed(current_block: T::BlockNumber) -> Weight {
648+
let current_era_info: RewardEraInfo<T::RewardEra, T::BlockNumber> = Self::get_current_era();
649+
if current_block.saturating_sub(current_era_info.era_start) >= T::EraLength::get().into() {
650+
CurrentEraInfo::<T>::set(RewardEraInfo {
651+
current_era: current_era_info.current_era.saturating_add(1u8.into()),
652+
era_start: current_block,
653+
});
654+
// TODO: modify reads/writes as needed when RewardPoolInfo stuff is added
655+
T::WeightInfo::on_initialize()
656+
.saturating_add(T::DbWeight::get().reads(1))
657+
.saturating_add(T::DbWeight::get().writes(1))
658+
} else {
659+
T::DbWeight::get().reads(2).saturating_add(RocksDbWeight::get().writes(1))
637660
}
638661
}
639662

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use super::mock::*;
2+
use crate::{
3+
tests::testing_utils::{run_to_block, system_run_to_block},
4+
Config, CurrentEraInfo, Error, Event, RewardEraInfo,
5+
};
6+
7+
use frame_support::traits::Get;
8+
9+
#[test]
10+
fn start_new_era_if_needed() {
11+
new_test_ext().execute_with(|| {
12+
CurrentEraInfo::<Test>::set(RewardEraInfo { current_era: 1, era_start: 0 });
13+
system_run_to_block(9);
14+
run_to_block(10);
15+
let mut current_era_info = CurrentEraInfo::<Test>::get();
16+
assert_eq!(current_era_info.current_era, 2u32);
17+
assert_eq!(current_era_info.era_start, 10u32);
18+
19+
system_run_to_block(19);
20+
run_to_block(20);
21+
current_era_info = CurrentEraInfo::<Test>::get();
22+
assert_eq!(current_era_info.current_era, 3u32);
23+
assert_eq!(current_era_info.era_start, 20u32);
24+
})
25+
}

pallets/capacity/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod capacity_details_tests;
22
pub mod epochs_tests;
3+
mod eras_tests;
34
pub mod mock;
45
pub mod other_tests;
56
pub mod replenishment_tests;

pallets/capacity/src/types.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ pub struct StakingAccountDetails<T: Config> {
2929
/// What type of staking this account is doing
3030
pub staking_type: StakingType,
3131
/// The None or Some(number): never, or the last RewardEra that this account's rewards were claimed.
32-
pub last_rewards_claimed_at: Option<RewardEra>,
32+
pub last_rewards_claimed_at: Option<T::RewardEra>,
3333
/// Chunks that have been retargeted within T::UnstakingThawPeriod
3434
pub stake_change_unlocking:
35-
BoundedVec<UnlockChunk<BalanceOf<T>, RewardEra>, T::MaxUnlockingChunks>,
35+
BoundedVec<UnlockChunk<BalanceOf<T>, T::RewardEra>, T::MaxUnlockingChunks>,
3636
}
3737

3838
/// The type that is used to record a single request for a number of tokens to be unlocked.
@@ -260,25 +260,25 @@ pub struct StakingRewardClaim<T: Config> {
260260
/// The end state of the staking account if the operations are valid
261261
pub staking_account_end_state: StakingAccountDetails<T>,
262262
/// The starting era for the claimed reward period, inclusive
263-
pub from_era: RewardEra,
263+
pub from_era: T::RewardEra,
264264
/// The ending era for the claimed reward period, inclusive
265-
pub to_era: RewardEra,
265+
pub to_era: T::RewardEra,
266266
}
267267

268268
/// A trait that provides the Economic Model for Provider Boosting.
269269
pub trait StakingRewardsProvider<T: Config> {
270270
/// Return the size of the reward pool for the given era, in token
271271
/// Errors:
272272
/// - EraOutOfRange when `era` is prior to the history retention limit, or greater than the current Era.
273-
fn reward_pool_size(era: RewardEra) -> BalanceOf<T>;
273+
fn reward_pool_size(era: T::RewardEra) -> BalanceOf<T>;
274274

275275
/// Return the total unclaimed reward in token for `accountId` for `from_era` --> `to_era`, inclusive
276276
/// Errors:
277277
/// - EraOutOfRange when from_era or to_era are prior to the history retention limit, or greater than the current Era.
278278
fn staking_reward_total(
279-
account_id: AccountId,
280-
from_era: RewardEra,
281-
to_era: RewardEra,
279+
account_id: T::AccountId,
280+
from_era: T::RewardEra,
281+
to_era: T::RewardEra,
282282
) -> BalanceOf<T>;
283283

284284
/// Validate a payout claim for `accountId`, using `proof` and the provided `payload` StakingRewardClaim.
@@ -303,5 +303,16 @@ pub trait StakingRewardsProvider<T: Config> {
303303
fn payout_eligible(account_id: AccountId) -> bool;
304304
}
305305

306+
/// The information needed to track a Reward Era
307+
#[derive(
308+
PartialEq, Eq, Clone, Default, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
309+
)]
310+
pub struct RewardEraInfo<RewardEra, BlockNumber> {
311+
/// the index of this era
312+
pub current_era: RewardEra,
313+
/// the starting block of this era
314+
pub era_start: BlockNumber,
315+
}
316+
306317
/// Needed data about a RewardPool for a given RewardEra.
307318
pub struct RewardPoolInfo {}

0 commit comments

Comments
 (0)