Skip to content

Commit a3dc25b

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 a669c3e commit a3dc25b

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
@@ -69,7 +69,7 @@ pub use common_primitives::{
6969

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

7474
pub use pallet::*;
7575
pub use types::*;
@@ -231,6 +231,12 @@ pub mod pallet {
231231
pub type EpochLength<T: Config> =
232232
StorageValue<_, BlockNumberFor<T>, 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]
@@ -325,6 +331,7 @@ pub mod pallet {
325331
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
326332
fn on_initialize(current: BlockNumberFor<T>) -> Weight {
327333
Self::start_new_epoch_if_needed(current)
334+
.saturating_add(Self::start_new_reward_era_if_needed(current))
328335
}
329336
}
330337

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

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
@@ -31,10 +31,10 @@ pub struct StakingAccountDetails<T: Config> {
3131
/// What type of staking this account is doing
3232
pub staking_type: StakingType,
3333
/// The None or Some(number): never, or the last RewardEra that this account's rewards were claimed.
34-
pub last_rewards_claimed_at: Option<RewardEra>,
34+
pub last_rewards_claimed_at: Option<T::RewardEra>,
3535
/// Chunks that have been retargeted within T::UnstakingThawPeriod
3636
pub stake_change_unlocking:
37-
BoundedVec<UnlockChunk<BalanceOf<T>, RewardEra>, T::MaxUnlockingChunks>,
37+
BoundedVec<UnlockChunk<BalanceOf<T>, T::RewardEra>, T::MaxUnlockingChunks>,
3838
}
3939

4040
/// The type that is used to record a single request for a number of tokens to be unlocked.
@@ -264,25 +264,25 @@ pub struct StakingRewardClaim<T: Config> {
264264
/// The end state of the staking account if the operations are valid
265265
pub staking_account_end_state: StakingAccountDetails<T>,
266266
/// The starting era for the claimed reward period, inclusive
267-
pub from_era: RewardEra,
267+
pub from_era: T::RewardEra,
268268
/// The ending era for the claimed reward period, inclusive
269-
pub to_era: RewardEra,
269+
pub to_era: T::RewardEra,
270270
}
271271

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

279279
/// Return the total unclaimed reward in token for `accountId` for `from_era` --> `to_era`, inclusive
280280
/// Errors:
281281
/// - EraOutOfRange when from_era or to_era are prior to the history retention limit, or greater than the current Era.
282282
fn staking_reward_total(
283-
account_id: AccountId,
284-
from_era: RewardEra,
285-
to_era: RewardEra,
283+
account_id: T::AccountId,
284+
from_era: T::RewardEra,
285+
to_era: T::RewardEra,
286286
) -> BalanceOf<T>;
287287

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

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

0 commit comments

Comments
 (0)