Skip to content

Commit

Permalink
Refactor reward era (#2069)
Browse files Browse the repository at this point in the history
# Goal
The goal of this PR is primarily to pull RewardEra out of the Capacity Config and make it the same type everywhere.

Other changes in this PR that were incidental to this refactor
* I renamed staking_events in testing_utils, because it captures all the events from the pallet.
* I pulled out RewardEraLength into constants so it could be different for different environments.

Related to #1970
  • Loading branch information
shannonwells committed Jul 18, 2024
1 parent 842b227 commit 5350108
Show file tree
Hide file tree
Showing 20 changed files with 106 additions and 99 deletions.
3 changes: 3 additions & 0 deletions common/primitives/src/capacity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use crate::msa::MessageSourceId;
use frame_support::traits::tokens::Balance;
use sp_runtime::DispatchError;

/// The type of a Reward Era
pub type RewardEra = u32;

/// A trait for checking that a target MSA can be staked to.
pub trait TargetValidator {
/// Checks if an MSA is a valid target.
Expand Down
14 changes: 10 additions & 4 deletions e2e/capacity/change_staking_target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ describe("Capacity: change_staking_target", function() {
const stakeKeys = createKeys("staker");
const oldProvider = await createMsaAndProvider(fundingSource, stakeKeys, "Provider2", providerBalance);

await assert.doesNotReject(stakeToProvider(fundingSource, stakeKeys, oldProvider, tokenMinStake*3n));
const notAProvider = 3;
const call = ExtrinsicHelper.changeStakingTarget(stakeKeys, oldProvider, notAProvider, tokenMinStake);
await assert.rejects(call.signAndSend(), {name: "InvalidTarget"})
await assert.doesNotReject(stakeToProvider(fundingSource, stakeKeys, oldProvider, tokenMinStake*6n));
const notAProvider = 9999;
const call = ExtrinsicHelper.changeStakingTarget(stakeKeys, oldProvider, notAProvider, tokenMinStake*2n);
await assert.rejects(call.signAndSend(),
(err) => {
assert. strictEqual(err?.name, 'InvalidTarget', `expected InvalidTarget, got ${err?.name}`);
// // {name: "InvalidTarget"}
// assert. strictEqual(err?.message, `Wrong value: expected`);
return true;
});
});
});
9 changes: 5 additions & 4 deletions pallets/capacity/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use super::*;
use crate::Pallet as Capacity;

use crate::StakingType::*;
use frame_benchmarking::{account, benchmarks, whitelist_account};
use frame_support::{assert_ok, BoundedVec};
use frame_system::RawOrigin;
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
use parity_scale_codec::alloc::vec::Vec;

const SEED: u32 = 0;
Expand Down Expand Up @@ -38,11 +39,11 @@ pub fn set_up_epoch<T: Config>(current_block: BlockNumberFor<T>, current_epoch:
}

pub fn set_era_and_reward_pool_at_block<T: Config>(
era_index: T::RewardEra,
era_index: RewardEra,
started_at: BlockNumberFor<T>,
total_staked_token: BalanceOf<T>,
) {
let era_info: RewardEraInfo<T::RewardEra, BlockNumberFor<T>> =
let era_info: RewardEraInfo<RewardEra, BlockNumberFor<T>> =
RewardEraInfo { era_index, started_at };
CurrentEraInfo::<T>::set(era_info);
CurrentEraProviderBoostTotal::<T>::set(total_staked_token)
Expand Down Expand Up @@ -149,7 +150,7 @@ benchmarks! {
let total_staked_token: BalanceOf<T> = 5_000u32.into();
let started_at: BlockNumberFor<T> = current_block.saturating_sub(<T as Config>::EraLength::get().into());

let current_era: T::RewardEra = (history_limit + 1u32).into();
let current_era: RewardEra = (history_limit + 1u32).into();
CurrentEraInfo::<T>::set(RewardEraInfo{ era_index: current_era, started_at });
fill_reward_pool_chunks::<T>();
}: {
Expand Down
55 changes: 20 additions & 35 deletions pallets/capacity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use frame_support::{
},
weights::{constants::RocksDbWeight, Weight},
};

use frame_system::pallet_prelude::BlockNumberFor;
use sp_runtime::{
traits::{CheckedAdd, CheckedDiv, One, Saturating, Zero},
ArithmeticError, BoundedVec, DispatchError, Perbill, Permill,
Expand All @@ -50,7 +50,6 @@ pub use common_primitives::{

#[cfg(feature = "runtime-benchmarks")]
use common_primitives::benchmarks::RegisterProviderBenchmarkHelper;

pub use pallet::*;
pub use types::*;
pub use weights::*;
Expand All @@ -66,18 +65,23 @@ mod tests;
/// storage migrations
pub mod migration;
pub mod weights;
pub(crate) type BalanceOf<T> =
type BalanceOf<T> =
<<T as Config>::Currency as InspectFungible<<T as frame_system::Config>::AccountId>>::Balance;

use crate::StakingType::{MaximumCapacity, ProviderBoost};
use crate::StakingType::ProviderBoost;
use common_primitives::capacity::RewardEra;
use frame_system::pallet_prelude::*;

#[frame_support::pallet]
pub mod pallet {
use super::*;

use frame_support::{pallet_prelude::*, Twox64Concat};
use parity_scale_codec::EncodeLike;
use crate::StakingType::MaximumCapacity;
use common_primitives::capacity::RewardEra;
use frame_support::{
pallet_prelude::{StorageVersion, *},
Twox64Concat,
};
use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeDisplay};

/// A reason for freezing funds.
Expand Down Expand Up @@ -153,24 +157,6 @@ pub mod pallet {
#[pallet::constant]
type CapacityPerToken: Get<Perbill>;

/// A period of `EraLength` blocks in which a Staking Pool applies and
/// when Provider Boost Rewards may be earned.
type RewardEra: Parameter
+ Member
+ MaybeSerializeDeserialize
+ MaybeDisplay
+ AtLeast32BitUnsigned
+ Default
+ Copy
+ sp_std::hash::Hash
+ MaxEncodedLen
+ EncodeLike
+ Into<BalanceOf<Self>>
+ Into<BlockNumberFor<Self>>
+ Into<u32>
+ EncodeLike<u32>
+ TypeInfo;

/// The number of blocks in a RewardEra
#[pallet::constant]
type EraLength: Get<u32>;
Expand Down Expand Up @@ -272,7 +258,7 @@ pub mod pallet {
#[pallet::whitelist_storage]
#[pallet::getter(fn get_current_era)]
pub type CurrentEraInfo<T: Config> =
StorageValue<_, RewardEraInfo<T::RewardEra, BlockNumberFor<T>>, ValueQuery>;
StorageValue<_, RewardEraInfo<RewardEra, BlockNumberFor<T>>, ValueQuery>;

/// Reward Pool history is divided into chunks of size RewardPoolChunkLength.
/// ProviderBoostHistoryLimit is the total number of items, the key is the
Expand Down Expand Up @@ -949,8 +935,7 @@ impl<T: Config> Pallet<T> {
}

fn start_new_reward_era_if_needed(current_block: BlockNumberFor<T>) -> Weight {
let current_era_info: RewardEraInfo<T::RewardEra, BlockNumberFor<T>> =
Self::get_current_era(); // 1r
let current_era_info: RewardEraInfo<RewardEra, BlockNumberFor<T>> = Self::get_current_era(); // 1r

if current_block.saturating_sub(current_era_info.started_at) >= T::EraLength::get().into() {
// 1r
Expand All @@ -976,7 +961,7 @@ impl<T: Config> Pallet<T> {
/// Returns:
/// Error::MaxRetargetsExceeded if they try to retarget too many times in one era.
fn update_retarget_record(staker: &T::AccountId) -> Result<(), DispatchError> {
let current_era: T::RewardEra = Self::get_current_era().era_index;
let current_era: RewardEra = Self::get_current_era().era_index;
let mut retargets = Self::get_retargets_for(staker).unwrap_or_default();
ensure!(retargets.update(current_era).is_some(), Error::<T>::MaxRetargetsExceeded);
Retargets::<T>::set(staker, Some(retargets));
Expand Down Expand Up @@ -1014,7 +999,7 @@ impl<T: Config> Pallet<T> {
/// pass 'false' for a decrease (unstake)
pub(crate) fn upsert_boost_history(
account: &T::AccountId,
current_era: T::RewardEra,
current_era: RewardEra,
boost_amount: BalanceOf<T>,
add: bool,
) -> Result<(), DispatchError> {
Expand Down Expand Up @@ -1113,7 +1098,7 @@ impl<T: Config> Pallet<T> {

// Returns the block number for the end of the provided era. Assumes `era` is at least this
// era or in the future
pub(crate) fn block_at_end_of_era(era: T::RewardEra) -> BlockNumberFor<T> {
pub(crate) fn block_at_end_of_era(era: RewardEra) -> BlockNumberFor<T> {
let current_era_info = Self::get_current_era();
let era_length: BlockNumberFor<T> = T::EraLength::get().into();

Expand All @@ -1127,8 +1112,8 @@ impl<T: Config> Pallet<T> {

// Figure out the history chunk that a given era is in and pull out the total stake for that era.
pub(crate) fn get_total_stake_for_past_era(
reward_era: T::RewardEra,
current_era: T::RewardEra,
reward_era: RewardEra,
current_era: RewardEra,
) -> Result<BalanceOf<T>, DispatchError> {
// Make sure that the past era is not too old
let era_range = current_era.saturating_sub(reward_era);
Expand All @@ -1153,7 +1138,7 @@ impl<T: Config> Pallet<T> {
/// - The second step is which chunk to add to:
/// - Divide the cycle by the chunk length and take the floor
/// - Floor(5 / 3) = 1
pub(crate) fn get_chunk_index_for_era(era: T::RewardEra) -> u32 {
pub(crate) fn get_chunk_index_for_era(era: RewardEra) -> u32 {
let history_limit: u32 = T::ProviderBoostHistoryLimit::get();
let chunk_len = T::RewardPoolChunkLength::get();
// Remove one because eras are 1 indexed
Expand All @@ -1170,7 +1155,7 @@ impl<T: Config> Pallet<T> {
// - [6], [2,3], [4,5]
// - [6,7], [2,3], [4,5]
// - [6,7], [8], [4,5]
pub(crate) fn update_provider_boost_reward_pool(era: T::RewardEra, boost_total: BalanceOf<T>) {
pub(crate) fn update_provider_boost_reward_pool(era: RewardEra, boost_total: BalanceOf<T>) {
// Current era is this era
let chunk_idx: u32 = Self::get_chunk_index_for_era(era);
let mut new_chunk =
Expand Down Expand Up @@ -1268,7 +1253,7 @@ impl<T: Config> Replenishable for Pallet<T> {

impl<T: Config> ProviderBoostRewardsProvider<T> for Pallet<T> {
type AccountId = T::AccountId;
type RewardEra = T::RewardEra;
type RewardEra = common_primitives::capacity::RewardEra;
type Hash = T::Hash;
type Balance = BalanceOf<T>;

Expand Down
3 changes: 2 additions & 1 deletion pallets/capacity/src/migration/provider_boost_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use frame_support::{
traits::{Get, OnRuntimeUpgrade},
};

use common_primitives::capacity::RewardEra;
#[cfg(feature = "try-runtime")]
use sp_std::vec::Vec;

Expand All @@ -15,7 +16,7 @@ impl<T: Config> OnRuntimeUpgrade for ProviderBoostInit<T> {
let current_era_info = CurrentEraInfo::<T>::get(); // 1r
if current_era_info.eq(&RewardEraInfo::default()) {
let current_block = frame_system::Pallet::<T>::block_number(); // Whitelisted
let era_index: T::RewardEra = 1u32.into();
let era_index: RewardEra = 1u32.into();
CurrentEraInfo::<T>::set(RewardEraInfo { era_index, started_at: current_block }); // 1w
CurrentEraProviderBoostTotal::<T>::set(0u32.into()); // 1w
T::DbWeight::get().reads_writes(2, 1)
Expand Down
15 changes: 7 additions & 8 deletions pallets/capacity/src/tests/change_staking_target_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::{
mock::*,
testing_utils::{setup_provider, staking_events},
testing_utils::{capacity_events, setup_provider},
};
use crate::*;
use crate::{StakingType::*, *};
use common_primitives::msa::MessageSourceId;
use frame_support::{assert_noop, assert_ok, traits::Get};

Expand Down Expand Up @@ -229,7 +229,7 @@ fn change_staking_starget_emits_event_on_success() {
to_msa,
to_amount
));
let events = staking_events();
let events = capacity_events();

assert_eq!(
events.last().unwrap(),
Expand Down Expand Up @@ -425,7 +425,7 @@ fn impl_retarget_info_errors_when_attempt_to_update_past_bounded_max() {
TestCase { era: 1u32, retargets: 5u32, last_retarget: 1, expected: None },
] {
let mut retarget_info: RetargetInfo<Test> =
RetargetInfo { retarget_count: tc.retargets, last_retarget_at: tc.last_retarget };
RetargetInfo::new(tc.retargets, tc.last_retarget);
assert_eq!(retarget_info.update(tc.era), tc.expected);
}
})
Expand All @@ -448,7 +448,7 @@ fn impl_retarget_info_updates_values_correctly() {
TestCase { era: 1, retargets: 5, last_retarget: 1, expected_retargets: 5 },
] {
let mut retarget_info: RetargetInfo<Test> =
RetargetInfo { retarget_count: tc.retargets, last_retarget_at: tc.last_retarget };
RetargetInfo::new(tc.retargets, tc.last_retarget);
retarget_info.update(tc.era);
assert_eq!(retarget_info.retarget_count, tc.expected_retargets);
}
Expand All @@ -459,10 +459,9 @@ fn impl_retarget_info_updates_values_correctly() {
fn impl_retarget_chunks_cleanup_when_new_reward_era() {
new_test_ext().execute_with(|| {
let current_era = 2u32;
let mut retarget_info: RetargetInfo<Test> =
RetargetInfo { retarget_count: 5, last_retarget_at: 1 };
let mut retarget_info: RetargetInfo<Test> = RetargetInfo::new(5, 1);
assert!(retarget_info.update(current_era).is_some());
let expected: RetargetInfo<Test> = RetargetInfo { retarget_count: 1, last_retarget_at: 2 };
let expected: RetargetInfo<Test> = RetargetInfo::new(1, 2);
assert_eq!(retarget_info, expected);
});
}
9 changes: 4 additions & 5 deletions pallets/capacity/src/tests/eras_tests.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use super::mock::*;
use crate::{
tests::testing_utils::*, BalanceOf, Config, CurrentEraInfo, CurrentEraProviderBoostTotal,
RewardEraInfo,
tests::testing_utils::*, BalanceOf, CurrentEraInfo, CurrentEraProviderBoostTotal, RewardEraInfo,
};
use common_primitives::msa::MessageSourceId;
use common_primitives::{capacity::RewardEra, msa::MessageSourceId};
use frame_support::assert_ok;

pub fn boost_provider_and_run_to_end_of_era(
Expand Down Expand Up @@ -44,7 +43,7 @@ fn start_new_era_if_needed_updates_era_info() {
fn assert_chunk_is_full_and_has_earliest_era_total(
chunk_index: u32,
is_full: bool,
era: <Test as Config>::RewardEra,
era: RewardEra,
total: BalanceOf<Test>,
) {
let chunk = Capacity::get_reward_pool_chunk(chunk_index).unwrap();
Expand All @@ -55,7 +54,7 @@ fn assert_chunk_is_full_and_has_earliest_era_total(

// gets the last (i.e. latest non-current) stored reward pool era, which is in chunk 0.
// asserts that it is the same as `era`, and that it has amount `total`
fn assert_last_era_total(era: <Test as Config>::RewardEra, total: BalanceOf<Test>) {
fn assert_last_era_total(era: RewardEra, total: BalanceOf<Test>) {
let chunk_idx = Capacity::get_chunk_index_for_era(era);
let chunk_opt = Capacity::get_reward_pool_chunk(chunk_idx);
assert!(chunk_opt.is_some(), "No pool for Era: {:?} with chunk index: {:?}", era, chunk_idx);
Expand Down
3 changes: 1 addition & 2 deletions pallets/capacity/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl ProviderBoostRewardsProvider<Test> for TestRewardsProvider {
}

fn staking_reward_totals(
account_id: Self::AccountId,
_account_id: Self::AccountId,
) -> Result<
BoundedVec<UnclaimedRewardInfo<Test>, <Test as Config>::ProviderBoostHistoryLimit>,
DispatchError,
Expand Down Expand Up @@ -197,7 +197,6 @@ impl pallet_capacity::Config for Test {
type MaxEpochLength = ConstU32<100>;
type EpochNumber = u32;
type CapacityPerToken = TestCapacityPerToken;
type RewardEra = TestRewardEra;
type EraLength = ConstU32<10>;
type ProviderBoostHistoryLimit = ConstU32<12>;
type RewardsProvider = Capacity;
Expand Down
5 changes: 3 additions & 2 deletions pallets/capacity/src/tests/provider_boost_history_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
Config, ProviderBoostHistory,
StakingType::{MaximumCapacity, ProviderBoost},
};
use common_primitives::capacity::RewardEra;
use frame_support::assert_ok;
use sp_runtime::traits::{Get, Zero};

Expand Down Expand Up @@ -71,12 +72,12 @@ fn provider_boost_history_add_era_balance_adds_entries_and_deletes_old_if_full()
}
assert_eq!(pbh.count(), bound as usize);

let new_era: <Test as Config>::RewardEra = 99;
let new_era: RewardEra = 99;
pbh.add_era_balance(&new_era, &1000u64);
assert_eq!(pbh.count(), bound as usize);
assert!(pbh.get_entry_for_era(&new_era).is_some());

let first_era: <Test as Config>::RewardEra = 1;
let first_era: RewardEra = 1;
assert!(pbh.get_entry_for_era(&first_era).is_none());
}

Expand Down
2 changes: 1 addition & 1 deletion pallets/capacity/src/tests/provider_boost_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn provider_boost_works() {
let capacity_details = Capacity::get_capacity_for(target).unwrap();
assert_eq!(capacity_details.total_capacity_issued, capacity);

let events = staking_events();
let events = capacity_events();
assert_eq!(
events.first().unwrap(),
&Event::ProviderBoosted { account, target, amount, capacity }
Expand Down
3 changes: 2 additions & 1 deletion pallets/capacity/src/tests/reward_pool_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use crate::{
tests::{mock::*, testing_utils::set_era_and_reward_pool},
BalanceOf, Config, ProviderBoostRewardPools, RewardPoolHistoryChunk,
};
use common_primitives::capacity::RewardEra;
use frame_support::{assert_ok, traits::Get};
use std::ops::Add;

// Check eras_tests for how reward pool chunks are expected to be filled during
// runtime.
fn fill_reward_pool_history_chunk(
chunk_index: u32,
starting_era: <Test as Config>::RewardEra,
starting_era: RewardEra,
number_of_items: u32,
starting_total_stake: BalanceOf<Test>,
) {
Expand Down
Loading

0 comments on commit 5350108

Please sign in to comment.