diff --git a/pallets/capacity/src/benchmarking.rs b/pallets/capacity/src/benchmarking.rs index 446efa2d4b..b5d9686597 100644 --- a/pallets/capacity/src/benchmarking.rs +++ b/pallets/capacity/src/benchmarking.rs @@ -44,15 +44,8 @@ pub fn set_era_and_reward_pool_at_block( ) { let era_info: RewardEraInfo> = RewardEraInfo { era_index, started_at }; - let total_reward_pool: BalanceOf = - T::MinimumStakingAmount::get().saturating_add(1_100u32.into()); CurrentEraInfo::::set(era_info); - let pool_info: RewardPoolInfo> = RewardPoolInfo { - total_staked_token, - total_reward_pool, - unclaimed_balance: total_reward_pool, - }; - ProviderBoostRewardPool::::insert(era_index, pool_info); + CurrentEraProviderBoostTotal::::set(total_staked_token) } // caller stakes the given amount to the given target @@ -93,6 +86,19 @@ fn fill_unlock_chunks(caller: &T::AccountId, count: u32) { UnstakeUnlocks::::set(caller, Some(unlocking)); } +fn fill_reward_pool_chunks() { + let chunk_len = T::RewardPoolChunkLength::get(); + let chunks = T::ProviderBoostHistoryLimit::get() / (chunk_len); + for i in 0..chunks { + let mut new_chunk = RewardPoolHistoryChunk::::new(); + for j in 0..chunk_len { + let era = (i + 1) * (j + 1); + assert_ok!(new_chunk.try_insert(era.into(), (1000u32 * era).into())); + } + ProviderBoostRewardPools::::set(i, Some(new_chunk)); + } +} + benchmarks! { stake { let caller: T::AccountId = create_funded_account::("account", SEED, 105u32); @@ -145,11 +151,7 @@ benchmarks! { let current_era: T::RewardEra = (history_limit + 1u32).into(); CurrentEraInfo::::set(RewardEraInfo{ era_index: current_era, started_at }); - - for i in 0..history_limit { - let era: T::RewardEra = i.into(); - ProviderBoostRewardPool::::insert(era, RewardPoolInfo { total_staked_token, total_reward_pool, unclaimed_balance}); - } + fill_reward_pool_chunks::(); }: { Capacity::::start_new_reward_era_if_needed(current_block); } verify { diff --git a/pallets/capacity/src/lib.rs b/pallets/capacity/src/lib.rs index 8e13b22198..d98f577e82 100644 --- a/pallets/capacity/src/lib.rs +++ b/pallets/capacity/src/lib.rs @@ -47,7 +47,8 @@ rustdoc::invalid_codeblock_attributes, missing_docs )] -use sp_std::ops::{Add, Mul}; + +use sp_std::ops::Mul; use frame_support::{ ensure, @@ -188,6 +189,8 @@ pub mod pallet { + EncodeLike + Into> + Into> + + Into + + EncodeLike + TypeInfo; /// The number of blocks in a RewardEra @@ -197,6 +200,7 @@ pub mod pallet { /// The maximum number of eras over which one can claim rewards /// Note that you can claim rewards even if you no longer are boosting, because you /// may claim rewards for past eras up to the history limit. + /// MUST be a multiple of [`Self::RewardPoolChunkLength`] #[pallet::constant] type ProviderBoostHistoryLimit: Get; @@ -214,6 +218,11 @@ pub mod pallet { /// the percentage cap per era of an individual Provider Boost reward #[pallet::constant] type RewardPercentCap: Get; + + /// The number of chunks of Reward Pool history we expect to store + /// MUST be a divisor of [`Self::ProviderBoostHistoryLimit`] + #[pallet::constant] + type RewardPoolChunkLength: Get; } /// Storage for keeping a ledger of staked token amounts for accounts. @@ -275,6 +284,11 @@ pub mod pallet { pub type UnstakeUnlocks = StorageMap<_, Twox64Concat, T::AccountId, UnlockChunkList>; + /// stores how many times an account has retargeted, and when it last retargeted. + #[pallet::storage] + #[pallet::getter(fn get_retargets_for)] + pub type Retargets = StorageMap<_, Twox64Concat, T::AccountId, RetargetInfo>; + /// Information about the current reward era. Checked every block. #[pallet::storage] #[pallet::whitelist_storage] @@ -282,22 +296,24 @@ pub mod pallet { pub type CurrentEraInfo = StorageValue<_, RewardEraInfo>, ValueQuery>; - /// Reward Pool history + /// Reward Pool history is divided into chunks of size RewardPoolChunkLength. + /// ProviderBoostHistoryLimit is the total number of items, the key is the + /// chunk number. #[pallet::storage] - #[pallet::getter(fn get_reward_pool_for_era)] - pub type ProviderBoostRewardPool = - CountedStorageMap<_, Twox64Concat, T::RewardEra, RewardPoolInfo>>; + #[pallet::getter(fn get_reward_pool_chunk)] + pub type ProviderBoostRewardPools = + StorageMap<_, Twox64Concat, u32, RewardPoolHistoryChunk>; + /// How much is staked this era + #[pallet::storage] + pub type CurrentEraProviderBoostTotal = StorageValue<_, BalanceOf, ValueQuery>; + + /// Individual history for each account that has Provider-Boosted. #[pallet::storage] #[pallet::getter(fn get_staking_history_for)] pub type ProviderBoostHistories = StorageMap<_, Twox64Concat, T::AccountId, ProviderBoostHistory>; - /// stores how many times an account has retargeted, and when it last retargeted. - #[pallet::storage] - #[pallet::getter(fn get_retargets_for)] - pub type Retargets = StorageMap<_, Twox64Concat, T::AccountId, RetargetInfo>; - // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] @@ -605,7 +621,7 @@ pub mod pallet { let (mut boosting_details, actual_amount) = Self::ensure_can_boost(&staker, &target, &amount)?; - let capacity = Self::increase_stake_and_issue_boost( + let capacity = Self::increase_stake_and_issue_boost_capacity( &staker, &mut boosting_details, &target, @@ -703,13 +719,14 @@ impl Pallet { Ok(capacity) } - fn increase_stake_and_issue_boost( + fn increase_stake_and_issue_boost_capacity( staker: &T::AccountId, staking_details: &mut StakingDetails, target: &MessageSourceId, amount: &BalanceOf, ) -> Result, DispatchError> { staking_details.deposit(*amount).ok_or(ArithmeticError::Overflow)?; + Self::set_staking_account_and_lock(staker, staking_details)?; // get the capacity generated by a Provider Boost let capacity = Self::capacity_generated(T::RewardsProvider::capacity_boost(*amount)); @@ -717,20 +734,18 @@ impl Pallet { let mut target_details = Self::get_target_for(staker, target).unwrap_or_default(); target_details.deposit(*amount, capacity).ok_or(ArithmeticError::Overflow)?; + Self::set_target_details_for(staker, *target, target_details); let mut capacity_details = Self::get_capacity_for(target).unwrap_or_default(); capacity_details.deposit(amount, &capacity).ok_or(ArithmeticError::Overflow)?; + Self::set_capacity_for(*target, capacity_details); let era = Self::get_current_era().era_index; - let mut reward_pool = - Self::get_reward_pool_for_era(era).ok_or(Error::::EraOutOfRange)?; - reward_pool.total_staked_token = reward_pool.total_staked_token.saturating_add(*amount); - - Self::set_staking_account_and_lock(staker, staking_details)?; - Self::set_target_details_for(staker, *target, target_details); - Self::set_capacity_for(*target, capacity_details); - Self::set_reward_pool(era, &reward_pool); Self::upsert_boost_history(staker, era, *amount, true)?; + + let reward_pool_total = CurrentEraProviderBoostTotal::::get(); + CurrentEraProviderBoostTotal::::set(reward_pool_total.saturating_add(*amount)); + Ok(capacity) } @@ -786,10 +801,6 @@ impl Pallet { CapacityLedger::::insert(target, capacity_details); } - fn set_reward_pool(era: ::RewardEra, new_reward_pool: &RewardPoolInfo>) { - ProviderBoostRewardPool::::set(era, Some(new_reward_pool.clone())); - } - /// Decrease a staking account's active token and reap if it goes below the minimum. /// Returns: actual amount unstaked, plus the staking type + StakingDetails, /// since StakingDetails may be reaped and staking type must be used to calculate the @@ -808,11 +819,11 @@ impl Pallet { let staking_type = staking_account.staking_type; if staking_type == ProviderBoost { let era = Self::get_current_era().era_index; - let mut reward_pool = - Self::get_reward_pool_for_era(era).ok_or(Error::::EraOutOfRange)?; - reward_pool.total_staked_token = reward_pool.total_staked_token.saturating_sub(amount); - Self::set_reward_pool(era, &reward_pool.clone()); Self::upsert_boost_history(&unstaker, era, actual_unstaked_amount, false)?; + let reward_pool_total = CurrentEraProviderBoostTotal::::get(); + CurrentEraProviderBoostTotal::::set( + reward_pool_total.saturating_sub(actual_unstaked_amount), + ); } Ok((actual_unstaked_amount, staking_type)) } @@ -971,26 +982,12 @@ impl Pallet { }; CurrentEraInfo::::set(new_era_info); // 1w - let current_reward_pool = - Self::get_reward_pool_for_era(current_era_info.era_index).unwrap_or_default(); // 1r - - let past_eras_max = T::ProviderBoostHistoryLimit::get(); - let entries: u32 = ProviderBoostRewardPool::::count(); // 1r - - if past_eras_max.eq(&entries) { - let earliest_era = - current_era_info.era_index.saturating_sub(past_eras_max.into()).add(One::one()); - ProviderBoostRewardPool::::remove(earliest_era); // 1w - } - - let total_reward_pool = - T::RewardsProvider::reward_pool_size(current_reward_pool.total_staked_token); - let new_reward_pool = RewardPoolInfo { - total_staked_token: current_reward_pool.total_staked_token, - total_reward_pool, - unclaimed_balance: total_reward_pool, - }; - ProviderBoostRewardPool::::insert(new_era_info.era_index, new_reward_pool); // 1w + // carry over the current reward pool total + let current_reward_pool_total: BalanceOf = CurrentEraProviderBoostTotal::::get(); // 1 + Self::update_provider_boost_reward_pool( + current_era_info.era_index, + current_reward_pool_total, + ); T::WeightInfo::start_new_reward_era_if_needed() } else { T::DbWeight::get().reads(1) @@ -1099,39 +1096,34 @@ impl Pallet { let staking_history = Self::get_staking_history_for(account).ok_or(Error::::NotAStakingAccount)?; // cached read from has_unclaimed_rewards - let era_info = Self::get_current_era(); // cached read, ditto + let current_era_info = Self::get_current_era(); // cached read, ditto + let max_history: u32 = T::ProviderBoostHistoryLimit::get(); // 1r + let era_length: u32 = T::EraLength::get(); // 1r length in blocks + let chunk_length: u32 = T::RewardPoolChunkLength::get(); + + let mut reward_era = current_era_info.era_index.saturating_sub((max_history).into()); + let end_era = current_era_info.era_index.saturating_sub(One::one()); - let max_history: u32 = T::ProviderBoostHistoryLimit::get() - 1; // 1r - let era_length: u32 = T::EraLength::get(); // 1r - let mut reward_era = era_info.era_index.saturating_sub((max_history).into()); - let end_era = era_info.era_index.saturating_sub(One::one()); // start with how much was staked in the era before the earliest for which there are eligible rewards. let mut previous_amount: BalanceOf = staking_history.get_amount_staked_for_era(&(reward_era.saturating_sub(1u32.into()))); + while reward_era.le(&end_era) { let staked_amount = staking_history.get_amount_staked_for_era(&reward_era); if !staked_amount.is_zero() { let expires_at_era = reward_era.saturating_add(max_history.into()); - let reward_pool = - Self::get_reward_pool_for_era(reward_era).ok_or(Error::::EraOutOfRange)?; // 1r - let expires_at_block = if expires_at_era.eq(&era_info.era_index) { - era_info.started_at + era_length.into() // expires at end of this era - } else { - let eras_to_expiration = - expires_at_era.saturating_sub(era_info.era_index).add(1u32.into()); - let blocks_to_expiration = eras_to_expiration * era_length.into(); - let started_at = era_info.started_at; - started_at + blocks_to_expiration.into() - }; + let expires_at_block = Self::block_at_end_of_era(expires_at_era); let eligible_amount = if staked_amount.lt(&previous_amount) { staked_amount } else { previous_amount }; + let total_for_era = + Self::get_total_stake_for_past_era(reward_era, current_era_info.era_index)?; let earned_amount = ::RewardsProvider::era_staking_reward( eligible_amount, - reward_pool.total_staked_token, - reward_pool.total_reward_pool, + total_for_era, + T::RewardPoolEachEra::get(), ); unclaimed_rewards .try_push(UnclaimedRewardInfo { @@ -1141,13 +1133,90 @@ impl Pallet { earned_amount, }) .map_err(|_e| Error::::CollectionBoundExceeded)?; - // ^^ there's no good reason for this ever to fail in production but it should be handled. + // ^^ there's no good reason for this ever to fail in production but it must be handled. previous_amount = staked_amount; } reward_era = reward_era.saturating_add(One::one()); } // 1r * up to ProviderBoostHistoryLimit-1, if they staked every RewardEra. Ok(unclaimed_rewards) } + + // 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 { + let current_era_info = Self::get_current_era(); + let era_length: BlockNumberFor = T::EraLength::get().into(); + + let era_diff = if current_era_info.era_index.eq(&era) { + 1u32.into() + } else { + era.saturating_sub(current_era_info.era_index).saturating_add(1u32.into()) + }; + current_era_info.started_at + era_length.mul(era_diff.into()) - 1u32.into() + } + + // 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, + ) -> Result, DispatchError> { + // Make sure that the past era is not too old + let era_range = current_era.saturating_sub(reward_era); + ensure!( + current_era.gt(&reward_era) && + era_range.le(&T::ProviderBoostHistoryLimit::get().into()), + Error::::EraOutOfRange + ); + + let chunk_idx: u32 = Self::get_chunk_index_for_era(reward_era); + let reward_pool_chunk = Self::get_reward_pool_chunk(chunk_idx).unwrap_or_default(); // 1r + let total_for_era = + reward_pool_chunk.total_for_era(&reward_era).ok_or(Error::::EraOutOfRange)?; + Ok(*total_for_era) + } + + /// Get the index of the chunk for a given era, hustory limit, and chunk length + /// Example with history limit of 6 and chunk length 3: + /// - Arrange the chuncks such that we overwrite a complete chunk only when it is not needed + /// - The cycle is thus era modulo (history limit + chunk length) + /// - `[0,1,2],[3,4,5],[6,7,8]` + /// - 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 { + let history_limit: u32 = T::ProviderBoostHistoryLimit::get(); + let chunk_len = T::RewardPoolChunkLength::get(); + // Remove one because eras are 1 indexed + let era_u32: u32 = era.saturating_sub(One::one()).into(); + + // Add one chunk so that we always have the full history limit in our chunks + let cycle: u32 = era_u32 % history_limit.saturating_add(chunk_len); + cycle.saturating_div(chunk_len) + } + + // This is where the reward pool gets updated. + // Example with Limit 6, Chunk 2: + // - [0,1], [2,3], [4,5] + // - [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) { + // Current era is this era + let chunk_idx: u32 = Self::get_chunk_index_for_era(era); + let mut new_chunk = + ProviderBoostRewardPools::::get(chunk_idx).unwrap_or(RewardPoolHistoryChunk::new()); // 1r + + // If it is full we are resetting. + // This assumes that the chunk length is a divisor of the history limit + if new_chunk.is_full() { + new_chunk = RewardPoolHistoryChunk::new(); + }; + + if new_chunk.try_insert(era, boost_total).is_err() { + // Handle the error case that should never happen + } + ProviderBoostRewardPools::::set(chunk_idx, Some(new_chunk)); // 1w + } } /// Nontransferable functions are intended for capacity spend and recharge. diff --git a/pallets/capacity/src/migration/provider_boost_init.rs b/pallets/capacity/src/migration/provider_boost_init.rs index 3cc36960ed..23239e9877 100644 --- a/pallets/capacity/src/migration/provider_boost_init.rs +++ b/pallets/capacity/src/migration/provider_boost_init.rs @@ -1,4 +1,4 @@ -use crate::{Config, CurrentEraInfo, ProviderBoostRewardPool, RewardEraInfo, RewardPoolInfo}; +use crate::{Config, CurrentEraInfo, CurrentEraProviderBoostTotal, RewardEraInfo}; use frame_support::{ pallet_prelude::Weight, traits::{Get, OnRuntimeUpgrade}, @@ -12,14 +12,13 @@ pub struct ProviderBoostInit(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for ProviderBoostInit { fn on_runtime_upgrade() -> Weight { - let current_era_info = CurrentEraInfo::::get(); + let current_era_info = CurrentEraInfo::::get(); // 1r if current_era_info.eq(&RewardEraInfo::default()) { - // 1r - let current_block = frame_system::Pallet::::block_number(); // 1r - let era_index: T::RewardEra = 0u32.into(); + let current_block = frame_system::Pallet::::block_number(); // Whitelisted + let era_index: T::RewardEra = 1u32.into(); CurrentEraInfo::::set(RewardEraInfo { era_index, started_at: current_block }); // 1w - ProviderBoostRewardPool::::insert(era_index, RewardPoolInfo::default()); // 1w - T::DbWeight::get().reads_writes(2, 2) + CurrentEraProviderBoostTotal::::set(0u32.into()); // 1w + T::DbWeight::get().reads_writes(2, 1) } else { T::DbWeight::get().reads(1) } @@ -32,11 +31,6 @@ impl OnRuntimeUpgrade for ProviderBoostInit { } else { log::info!("CurrentEraInfo not found. Initialization should proceed."); } - if ProviderBoostRewardPool::::iter().count() == 0usize { - log::info!("ProviderBoostRewardPool will be updated with Era 0"); - } else { - log::info!("ProviderBoostRewardPool has already been initialized.") - } Ok(Vec::default()) } @@ -47,7 +41,6 @@ impl OnRuntimeUpgrade for ProviderBoostInit { let info = CurrentEraInfo::::get(); assert_eq!(info.started_at, current_block); log::info!("CurrentEraInfo.started_at is set to {:?}.", info.started_at); - assert_eq!(ProviderBoostRewardPool::::iter().count(), 1); Ok(()) } } diff --git a/pallets/capacity/src/tests/eras_tests.rs b/pallets/capacity/src/tests/eras_tests.rs index 6b56cc552f..2c5548d128 100644 --- a/pallets/capacity/src/tests/eras_tests.rs +++ b/pallets/capacity/src/tests/eras_tests.rs @@ -1,12 +1,23 @@ use super::mock::*; use crate::{ - tests::testing_utils::{run_to_block, setup_provider, system_run_to_block}, - Config, CurrentEraInfo, ProviderBoostRewardPool, RewardEraInfo, RewardPoolInfo, - StakingType::*, + tests::testing_utils::*, BalanceOf, Config, CurrentEraInfo, CurrentEraProviderBoostTotal, + RewardEraInfo, }; use common_primitives::msa::MessageSourceId; use frame_support::assert_ok; -use sp_core::Get; + +pub fn boost_provider_and_run_to_end_of_era( + staker: u64, + provider_msa: MessageSourceId, + stake_amount: u64, + era: u32, +) { + assert_ok!(Capacity::provider_boost(RuntimeOrigin::signed(staker), provider_msa, stake_amount)); + let block_decade: u32 = (era * 10) + 1; + run_to_block(block_decade); + assert_eq!(CurrentEraProviderBoostTotal::::get(), stake_amount * (era as u64)); + system_run_to_block(block_decade + 9); +} #[test] fn start_new_era_if_needed_updates_era_info() { @@ -23,14 +34,46 @@ fn start_new_era_if_needed_updates_era_info() { current_era_info, RewardEraInfo { era_index: expected_era, started_at: block_decade } ); - - let past_eras_max: u32 = ::ProviderBoostHistoryLimit::get(); - assert!(ProviderBoostRewardPool::::count().le(&past_eras_max)); system_run_to_block(block_decade + 9); } }) } +// checks that the chunk indicated has an earliest era matching `era`, +// asserts whether it is full (or not) and asserts that its total stored matches `total` +fn assert_chunk_is_full_and_has_earliest_era_total( + chunk_index: u32, + is_full: bool, + era: ::RewardEra, + total: BalanceOf, +) { + let chunk = Capacity::get_reward_pool_chunk(chunk_index).unwrap(); + assert_eq!(chunk.is_full(), is_full, "{:?}", chunk); + assert_eq!(chunk.earliest_era(), Some(&era), "{:?}", chunk); + assert_eq!(chunk.total_for_era(&era), Some(&total), "{:?}", chunk); +} + +// 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: ::RewardEra, total: BalanceOf) { + 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); + let chunk = chunk_opt.unwrap(); + let (_earliest, latest) = chunk.era_range(); + assert_eq!(latest, era); + assert_eq!(chunk.total_for_era(&era), Some(&total)); +} + +fn assert_chunk_is_empty(chunk_index: u32) { + let chunk_opt = Capacity::get_reward_pool_chunk(chunk_index); + if chunk_opt.is_some() { + assert!(chunk_opt.unwrap().earliest_era().is_none()) + } else { + assert!(chunk_opt.is_none()) + } +} + // Test that additional stake is carried over to the next era's RewardPoolInfo. #[test] fn start_new_era_if_needed_updates_reward_pool() { @@ -38,30 +81,106 @@ fn start_new_era_if_needed_updates_reward_pool() { system_run_to_block(8); let staker = 10_000; let provider_msa: MessageSourceId = 1; - let stake_amount = 600u64; + let stake_amount = 100u64; - // set up initial stake. - setup_provider(&staker, &provider_msa, &stake_amount, ProviderBoost); + register_provider(provider_msa, "Binky".to_string()); - for i in 1u32..4 { - let era = i + 1; - let final_block = (i * 10) + 1; - system_run_to_block(final_block - 1); - run_to_block(final_block); - assert_eq!(ProviderBoostRewardPool::::count(), era); - assert_eq!( - ProviderBoostRewardPool::::get(era).unwrap(), - RewardPoolInfo { - total_staked_token: (stake_amount * i as u64), - total_reward_pool: 10_000, - unclaimed_balance: 10_000, - } - ); - assert_ok!(Capacity::provider_boost( - RuntimeOrigin::signed(staker), - provider_msa, - stake_amount - )); + for i in [1u32, 2u32, 3u32] { + boost_provider_and_run_to_end_of_era(staker, provider_msa, stake_amount, i); + } + // check that first chunk is filled correctly. + assert_chunk_is_full_and_has_earliest_era_total(0, true, 1, 100); + assert_chunk_is_empty(1); + assert_chunk_is_empty(2); + assert_last_era_total(3, 300); + + for i in [4u32, 5u32, 6u32] { + boost_provider_and_run_to_end_of_era(staker, provider_msa, stake_amount, i); + } + // No change + assert_chunk_is_full_and_has_earliest_era_total(0, true, 1, 100); + // New Chunk + assert_chunk_is_full_and_has_earliest_era_total(1, true, 4, 400); + assert_chunk_is_empty(2); + assert_last_era_total(6, 600); + + for i in [7u32, 8u32, 9u32, 10u32, 11u32, 12u32, 13u32, 14u32, 15u32] { + boost_provider_and_run_to_end_of_era(staker, provider_msa, stake_amount, i); + } + // No changes + assert_chunk_is_full_and_has_earliest_era_total(0, true, 1, 100); + assert_chunk_is_full_and_has_earliest_era_total(1, true, 4, 400); + // New + assert_chunk_is_full_and_has_earliest_era_total(2, true, 7, 700); + assert_chunk_is_full_and_has_earliest_era_total(3, true, 10, 1000); + assert_chunk_is_full_and_has_earliest_era_total(4, true, 13, 1300); + assert_last_era_total(15, 1500); + + // check that it all rolls over properly. + for i in [16u32, 17u32] { + boost_provider_and_run_to_end_of_era(staker, provider_msa, stake_amount, i); } + // No Changes + assert_chunk_is_full_and_has_earliest_era_total(1, true, 4, 400); + assert_chunk_is_full_and_has_earliest_era_total(2, true, 7, 700); + assert_chunk_is_full_and_has_earliest_era_total(3, true, 10, 1000); + assert_chunk_is_full_and_has_earliest_era_total(4, true, 13, 1300); + // New + assert_chunk_is_full_and_has_earliest_era_total(0, false, 16, 1600); + assert_last_era_total(17, 1700); + // There shouldn't be a chunk 5 ever with this config + assert_chunk_is_empty(5); }); } + +#[test] +fn get_expiration_block_for_era_works() { + new_test_ext().execute_with(|| { + assert_eq!(Capacity::get_current_era().era_index, 1u32); + assert_eq!(Capacity::block_at_end_of_era(10u32), 100); + + set_era_and_reward_pool(7, 61, 0); + assert_eq!(Capacity::block_at_end_of_era(7u32), 70); + assert_eq!(Capacity::block_at_end_of_era(10u32), 100); + + set_era_and_reward_pool(10, 91, 0); + assert_eq!(Capacity::block_at_end_of_era(10u32), 100); + + set_era_and_reward_pool(99, 2342, 0); + assert_eq!(Capacity::block_at_end_of_era(99), 2351); + + assert_eq!(Capacity::block_at_end_of_era(108), 2441); + }) +} + +#[test] +fn get_chunk_index_for_era_works() { + new_test_ext().execute_with(|| { + #[derive(Debug)] + struct TestCase { + era: u32, + expected: u32, + } + // assuming history limit is 12, chunk length is 3 + // [1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15] + // [16],[4,5,6],[7,8,9],[10,11,12],[13,14,15] + for test in { + vec![ + TestCase { era: 1, expected: 0 }, + TestCase { era: 2, expected: 0 }, + TestCase { era: 3, expected: 0 }, + TestCase { era: 4, expected: 1 }, + TestCase { era: 5, expected: 1 }, + TestCase { era: 6, expected: 1 }, + TestCase { era: 7, expected: 2 }, + TestCase { era: 11, expected: 3 }, + TestCase { era: 15, expected: 4 }, + TestCase { era: 16, expected: 0 }, + TestCase { era: 22, expected: 2 }, + TestCase { era: 55, expected: 3 }, + ] + } { + assert_eq!(Capacity::get_chunk_index_for_era(test.era), test.expected, "{:?}", test); + } + }) +} diff --git a/pallets/capacity/src/tests/mock.rs b/pallets/capacity/src/tests/mock.rs index fe15edca5d..10f5eb9b41 100644 --- a/pallets/capacity/src/tests/mock.rs +++ b/pallets/capacity/src/tests/mock.rs @@ -1,8 +1,8 @@ use crate as pallet_capacity; use crate::{ - tests::testing_utils::set_era_and_reward_pool, BalanceOf, ProviderBoostRewardClaim, - ProviderBoostRewardsProvider, + tests::testing_utils::set_era_and_reward_pool, BalanceOf, Config, ProviderBoostRewardClaim, + ProviderBoostRewardPools, ProviderBoostRewardsProvider, RewardPoolHistoryChunk, }; use common_primitives::{ node::{AccountId, Hash, ProposalProvider}, @@ -15,7 +15,7 @@ use frame_support::{ use frame_system::EnsureSigned; use sp_core::{ConstU8, H256}; use sp_runtime::{ - traits::{BlakeTwo256, Convert, IdentityLookup}, + traits::{BlakeTwo256, Convert, Get, IdentityLookup}, AccountId32, BuildStorage, DispatchError, Perbill, Permill, }; use sp_std::ops::Mul; @@ -208,11 +208,20 @@ impl pallet_capacity::Config for Test { type CapacityPerToken = TestCapacityPerToken; type RewardEra = TestRewardEra; type EraLength = ConstU32<10>; - type ProviderBoostHistoryLimit = ConstU32<6>; // 5 for claiming rewards, 1 for current reward era + type ProviderBoostHistoryLimit = ConstU32<12>; type RewardsProvider = Capacity; type MaxRetargetsPerRewardEra = ConstU32<5>; type RewardPoolEachEra = ConstU64<10_000>; type RewardPercentCap = TestRewardCap; + type RewardPoolChunkLength = ConstU32<3>; +} + +fn initialize_reward_pool() { + let history_limit: u32 = ::ProviderBoostHistoryLimit::get(); + let chunks = history_limit.saturating_div(::RewardPoolChunkLength::get()); + for i in 0u32..chunks { + ProviderBoostRewardPools::::insert(i, RewardPoolHistoryChunk::::new()) + } } pub fn new_test_ext() -> sp_io::TestExternalities { @@ -235,6 +244,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { System::set_block_number(1); + initialize_reward_pool(); set_era_and_reward_pool(1, 1, 0); }); ext diff --git a/pallets/capacity/src/tests/mod.rs b/pallets/capacity/src/tests/mod.rs index 639754ddaf..32ff4eb883 100644 --- a/pallets/capacity/src/tests/mod.rs +++ b/pallets/capacity/src/tests/mod.rs @@ -8,6 +8,7 @@ pub mod other_tests; mod provider_boost_history_tests; mod provider_boost_tests; pub mod replenishment_tests; +mod reward_pool_tests; mod rewards_provider_tests; pub mod stake_and_deposit_tests; pub mod staking_account_details_tests; diff --git a/pallets/capacity/src/tests/provider_boost_tests.rs b/pallets/capacity/src/tests/provider_boost_tests.rs index af00dd6214..f231570703 100644 --- a/pallets/capacity/src/tests/provider_boost_tests.rs +++ b/pallets/capacity/src/tests/provider_boost_tests.rs @@ -1,5 +1,7 @@ use super::{mock::*, testing_utils::*}; -use crate::{Config, Error, Event, FreezeReason, RewardPoolInfo, StakingDetails, StakingType}; +use crate::{ + Config, CurrentEraProviderBoostTotal, Error, Event, FreezeReason, StakingDetails, StakingType, +}; use common_primitives::msa::MessageSourceId; use frame_support::{assert_noop, assert_ok, traits::fungible::InspectFreeze}; @@ -57,16 +59,7 @@ fn provider_boost_updates_reward_pool_history() { assert_ok!(Capacity::provider_boost(RuntimeOrigin::signed(account1), target, amount1)); assert_ok!(Capacity::provider_boost(RuntimeOrigin::signed(account2), target, amount2)); - - let reward_pool = Capacity::get_reward_pool_for_era(1).unwrap(); - assert_eq!( - reward_pool, - RewardPoolInfo { - total_staked_token: 700, - total_reward_pool: 10_000, - unclaimed_balance: 10_000, - } - ); + assert_eq!(CurrentEraProviderBoostTotal::::get(), 700u64); }); } diff --git a/pallets/capacity/src/tests/reward_pool_tests.rs b/pallets/capacity/src/tests/reward_pool_tests.rs new file mode 100644 index 0000000000..b284da82ed --- /dev/null +++ b/pallets/capacity/src/tests/reward_pool_tests.rs @@ -0,0 +1,134 @@ +use crate::{ + tests::{mock::*, testing_utils::set_era_and_reward_pool}, + BalanceOf, Config, ProviderBoostRewardPools, RewardPoolHistoryChunk, +}; +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: ::RewardEra, + number_of_items: u32, + starting_total_stake: BalanceOf, +) { + let mut chunk: RewardPoolHistoryChunk = RewardPoolHistoryChunk::new(); + for i in 0u32..number_of_items { + let additional_stake: BalanceOf = (i * 100u32).into(); + let total_stake: BalanceOf = starting_total_stake.add(additional_stake); + let era = starting_era + i; + assert_ok!(chunk.try_insert(era, total_stake)); + } + ProviderBoostRewardPools::set(chunk_index, Some(chunk)); +} + +#[test] +fn reward_pool_history_chunk_default_tests() { + let chunk: RewardPoolHistoryChunk = RewardPoolHistoryChunk::new(); + assert!(!chunk.is_full()); + assert!(chunk.total_for_era(&0u32).is_none()); + let default: RewardPoolHistoryChunk = RewardPoolHistoryChunk::default(); + assert!(default.total_for_era(&032).is_none()); + assert_eq!(default.era_range(), (0u32, 0u32)); +} + +#[test] +fn reward_pool_history_chunk_insert_range_remove() { + let mut chunk: RewardPoolHistoryChunk = RewardPoolHistoryChunk::new(); + assert_eq!(chunk.try_insert(22u32, 100u64), Ok(None)); + assert_eq!(chunk.try_insert(22u32, 110u64), Ok(Some(100u64))); + assert!(chunk.try_insert(23u32, 123u64).is_ok()); + assert!(chunk.try_insert(24u32, 99u64).is_ok()); + // For the limit is 3 + assert_eq!(chunk.try_insert(25u32, 99u64), Err((25u32, 99u64))); + assert_eq!(chunk.total_for_era(&23u32), Some(&123u64)); + assert_eq!(chunk.era_range(), (22u32, 24u32)); +} + +#[test] +fn reward_pool_history_chunk_try_removing_oldest() { + let mut chunk: RewardPoolHistoryChunk = RewardPoolHistoryChunk::new(); + assert!(chunk.try_insert(22u32, 100u64).is_ok()); + assert!(chunk.try_insert(23u32, 123u64).is_ok()); + assert!(chunk.try_insert(24u32, 99u64).is_ok()); + assert_eq!(chunk.is_full(), true); + + let earliest = chunk.earliest_era().unwrap(); + assert_eq!(earliest, &22u32); + + // E0502 + let mut new_chunk = chunk.clone(); + assert_eq!(new_chunk.is_full(), true); + let remove_result = new_chunk.remove(earliest); + assert_eq!(remove_result, Some(100u64)); + assert_eq!(new_chunk.is_full(), false); + + assert_eq!(new_chunk.earliest_era(), Some(&23u32)); +} + +// Check boundary behavior for the first reward eras before hitting maximum history, +// then check behavior once the reward pool chunks are always full. +#[test] +fn get_total_stake_for_past_era_works_with_partly_filled_single_chunk() { + new_test_ext().execute_with(|| { + set_era_and_reward_pool(3, 21, 1000); + System::set_block_number(22); + fill_reward_pool_history_chunk(0, 1, 2, 100); + assert_eq!(Capacity::get_total_stake_for_past_era(1, 3), Ok(100)); + assert_eq!(Capacity::get_total_stake_for_past_era(2, 3), Ok(200)); + assert!(Capacity::get_total_stake_for_past_era(3, 3).is_err()); + }) +} + +#[test] +fn get_total_stake_for_past_era_works_with_1_full_chunk() { + new_test_ext().execute_with(|| { + System::set_block_number(52); + set_era_and_reward_pool(6, 51, 1000); + fill_reward_pool_history_chunk(0, 1, 3, 100); // eras 1-3 + fill_reward_pool_history_chunk(1, 4, 2, 400); // eras 4,5 + for i in 3u32..=5u32 { + let expected_total: BalanceOf = (i * 100u32).into(); + let actual = Capacity::get_total_stake_for_past_era(i, 6); + assert_eq!(actual, Ok(expected_total)); + } + assert!(Capacity::get_total_stake_for_past_era(6, 6).is_err()); + }) +} + +#[test] +fn get_total_stake_for_past_era_works_with_2_full_chunks() { + new_test_ext().execute_with(|| { + System::set_block_number(72); + set_era_and_reward_pool(8, 71, 1000); + fill_reward_pool_history_chunk(0, 1, 3, 100); + fill_reward_pool_history_chunk(1, 4, 3, 400); + fill_reward_pool_history_chunk(2, 7, 1, 700); + for i in 1u32..=7u32 { + let expected_total: BalanceOf = (i * 100u32).into(); + assert_eq!(Capacity::get_total_stake_for_past_era(i, 8), Ok(expected_total)); + } + assert!(Capacity::get_total_stake_for_past_era(8, 8).is_err()); + }) +} + +#[test] +fn get_total_stake_for_past_era_works_with_full_reward_pool() { + new_test_ext().execute_with(|| { + System::set_block_number(72); + let history_limit: u32 = ::ProviderBoostHistoryLimit::get(); + set_era_and_reward_pool(13, 121, (2000u32).into()); + + fill_reward_pool_history_chunk(0, 1, 3, 101); + fill_reward_pool_history_chunk(1, 4, 3, 401); + fill_reward_pool_history_chunk(2, 7, 3, 701); + fill_reward_pool_history_chunk(3, 10, 3, 1001); + + (1u32..=history_limit).for_each(|era| { + let expected_total: BalanceOf = ((era * 100u32) + 1u32).into(); + assert_eq!(Capacity::get_total_stake_for_past_era(era, 13), Ok(expected_total)); + }); + assert!(Capacity::get_total_stake_for_past_era(13, 13).is_err()); + }) +} diff --git a/pallets/capacity/src/tests/rewards_provider_tests.rs b/pallets/capacity/src/tests/rewards_provider_tests.rs index 87b62c44e3..99e86a4c62 100644 --- a/pallets/capacity/src/tests/rewards_provider_tests.rs +++ b/pallets/capacity/src/tests/rewards_provider_tests.rs @@ -1,8 +1,8 @@ use super::mock::*; use crate::{ - CurrentEraInfo, Error, ProviderBoostHistories, ProviderBoostHistory, ProviderBoostRewardClaim, - ProviderBoostRewardsProvider, RewardEraInfo, StakingDetails, StakingType::*, - UnclaimedRewardInfo, + Config, CurrentEraInfo, Error, ProviderBoostHistories, ProviderBoostHistory, + ProviderBoostRewardClaim, ProviderBoostRewardsProvider, RewardEraInfo, StakingDetails, + StakingType::*, UnclaimedRewardInfo, }; use frame_support::{assert_err, assert_ok, traits::Len}; @@ -10,7 +10,7 @@ use crate::tests::testing_utils::{ run_to_block, set_era_and_reward_pool, setup_provider, system_run_to_block, }; use common_primitives::msa::MessageSourceId; -use sp_core::H256; +use sp_core::{Get, H256}; #[test] fn staking_reward_total_happy_path() { @@ -150,10 +150,6 @@ fn check_for_unclaimed_rewards_has_eligible_rewards() { assert_eq!(Capacity::get_current_era().era_index, 6u32); assert_eq!(Capacity::get_current_era().started_at, 51u32); - assert!(Capacity::get_reward_pool_for_era(0u32).is_none()); - assert!(Capacity::get_reward_pool_for_era(1u32).is_some()); - assert!(Capacity::get_reward_pool_for_era(6u32).is_some()); - // rewards for era 6 should not be returned; era 6 is current era and therefore ineligible. // eligible amounts for rewards for eras should be: 1=0, 2=1k, 3=2k, 4=2k, 5=3k let rewards = Capacity::list_unclaimed_rewards(&account).unwrap(); @@ -161,52 +157,53 @@ fn check_for_unclaimed_rewards_has_eligible_rewards() { let expected_info: [UnclaimedRewardInfo; 5] = [ UnclaimedRewardInfo { reward_era: 1u32, - expires_at_block: 61, + expires_at_block: 130, eligible_amount: 0, earned_amount: 0, }, UnclaimedRewardInfo { reward_era: 2u32, - expires_at_block: 71, + expires_at_block: 140, eligible_amount: 1000, earned_amount: 4, }, UnclaimedRewardInfo { reward_era: 3u32, - expires_at_block: 81, + expires_at_block: 150, eligible_amount: 2_000, earned_amount: 8, }, UnclaimedRewardInfo { reward_era: 4u32, - expires_at_block: 91, + expires_at_block: 160, eligible_amount: 2000, earned_amount: 8, }, UnclaimedRewardInfo { reward_era: 5u32, - expires_at_block: 101, + expires_at_block: 170, eligible_amount: 3_000, earned_amount: 11, }, ]; - for i in 0..=4 { + for i in 0..5 { assert_eq!(rewards.get(i).unwrap(), &expected_info[i]); } - run_to_block(61); + run_to_block(131); let rewards = Capacity::list_unclaimed_rewards(&account).unwrap(); + let max_history: u32 = ::ProviderBoostHistoryLimit::get(); // the earliest era should no longer be stored. - assert_eq!(rewards.len(), 5usize); + assert_eq!(rewards.len(), max_history as usize); assert_eq!(rewards.get(0).unwrap().reward_era, 2u32); // there was no change in stake, so the eligible and earned amounts should be the same as in // reward era 5. assert_eq!( - rewards.get(4).unwrap(), + rewards.get((max_history - 1) as usize).unwrap(), &UnclaimedRewardInfo { - reward_era: 6u32, - expires_at_block: 111, + reward_era: 13u32, + expires_at_block: 250, eligible_amount: 3_000, earned_amount: 11, } @@ -229,18 +226,21 @@ fn check_for_unclaimed_rewards_returns_correctly_for_old_single_boost() { setup_provider(&account, &target, &amount, ProviderBoost); assert!(!Capacity::has_unclaimed_rewards(&account)); - run_to_block(71); - assert_eq!(Capacity::get_current_era().era_index, 8u32); - assert_eq!(Capacity::get_current_era().started_at, 71u32); + run_to_block(131); + assert_eq!(Capacity::get_current_era().era_index, 14u32); + assert_eq!(Capacity::get_current_era().started_at, 131u32); let rewards = Capacity::list_unclaimed_rewards(&account).unwrap(); + + let max_history: u32 = ::ProviderBoostHistoryLimit::get(); + assert_eq!(rewards.len(), max_history as usize); + // the earliest era should no longer be stored. - assert_eq!(rewards.len(), 5usize); - for i in 0..=4 { - let expected_era: u32 = i + 3; + for i in 0u32..max_history { + let era = i + 2u32; let expected_info: UnclaimedRewardInfo = UnclaimedRewardInfo { - reward_era: expected_era.into(), - expires_at_block: (expected_era * 10u32 + 51u32).into(), + reward_era: era.into(), + expires_at_block: (era * 10u32 + 120u32).into(), eligible_amount: 1000, earned_amount: 4, }; diff --git a/pallets/capacity/src/tests/testing_utils.rs b/pallets/capacity/src/tests/testing_utils.rs index 4dda6d5954..c45f777b26 100644 --- a/pallets/capacity/src/tests/testing_utils.rs +++ b/pallets/capacity/src/tests/testing_utils.rs @@ -5,8 +5,8 @@ use frame_support::{assert_ok, traits::Hooks}; use sp_runtime::traits::SignedExtension; use crate::{ - BalanceOf, CapacityDetails, Config, CurrentEraInfo, Event, ProviderBoostRewardPool, - RewardEraInfo, RewardPoolInfo, StakingType, + BalanceOf, CapacityDetails, Config, CurrentEraInfo, CurrentEraProviderBoostTotal, Event, + RewardEraInfo, StakingType, }; use common_primitives::msa::MessageSourceId; @@ -96,12 +96,6 @@ pub fn setup_provider( // Currently the reward pool is a constant, however it could change in the future. pub fn set_era_and_reward_pool(era_index: u32, started_at: u32, total_staked_token: u64) { let era_info = RewardEraInfo { era_index, started_at }; - let total_reward_pool = 10_000u64; CurrentEraInfo::::set(era_info); - let pool_info = RewardPoolInfo { - total_staked_token, - total_reward_pool, - unclaimed_balance: total_reward_pool, - }; - ProviderBoostRewardPool::::insert(era_index, pool_info); + CurrentEraProviderBoostTotal::::set(total_staked_token); } diff --git a/pallets/capacity/src/tests/unstaking_tests.rs b/pallets/capacity/src/tests/unstaking_tests.rs index fa639b9349..9a7943af28 100644 --- a/pallets/capacity/src/tests/unstaking_tests.rs +++ b/pallets/capacity/src/tests/unstaking_tests.rs @@ -1,8 +1,8 @@ use super::{mock::*, testing_utils::*}; use crate as pallet_capacity; use crate::{ - CapacityDetails, FreezeReason, ProviderBoostHistory, RewardPoolInfo, StakingDetails, - StakingTargetDetails, StakingType, StakingType::ProviderBoost, UnlockChunk, + CapacityDetails, CurrentEraProviderBoostTotal, FreezeReason, ProviderBoostHistory, + StakingDetails, StakingTargetDetails, StakingType, StakingType::ProviderBoost, UnlockChunk, }; use common_primitives::msa::MessageSourceId; use frame_support::{ @@ -298,15 +298,7 @@ fn unstake_provider_boosted_target_adjusts_reward_pool_total() { assert_ok!(Capacity::provider_boost(RuntimeOrigin::signed(account1), target, amount1)); assert_ok!(Capacity::unstake(RuntimeOrigin::signed(account1), target, unstake_amount)); - let reward_pool = Capacity::get_reward_pool_for_era(1).unwrap(); - assert_eq!( - reward_pool, - RewardPoolInfo { - total_staked_token: 300, - total_reward_pool: 10_000, - unclaimed_balance: 10_000, - } - ); + assert_eq!(CurrentEraProviderBoostTotal::::get(), 300u64); }); } @@ -315,30 +307,21 @@ fn unstake_maximum_does_not_change_reward_pool() { new_test_ext().execute_with(|| { // two accounts staking to the same target let account1 = 600; - let a_booster = 500; + let a_booster = 10_000; let target: MessageSourceId = 1; let amount1 = 500; let unstake_amount = 200; - let expected_reward_pool: RewardPoolInfo> = RewardPoolInfo { - total_staked_token: 490, // ??? - total_reward_pool: 10_000, - unclaimed_balance: 10_000, - }; - register_provider(target, String::from("Foo")); run_to_block(5); // ensures Capacity::on_initialize is run assert_ok!(Capacity::stake(RuntimeOrigin::signed(account1), target, amount1)); assert_ok!(Capacity::provider_boost(RuntimeOrigin::signed(a_booster), target, amount1)); - // there should be only the one contribution - let mut reward_pool = Capacity::get_reward_pool_for_era(1).unwrap(); - assert_eq!(reward_pool, expected_reward_pool); + assert_eq!(CurrentEraProviderBoostTotal::::get(), amount1); assert_ok!(Capacity::unstake(RuntimeOrigin::signed(account1), target, unstake_amount)); - reward_pool = Capacity::get_reward_pool_for_era(1).unwrap(); - assert_eq!(reward_pool, expected_reward_pool); + assert_eq!(CurrentEraProviderBoostTotal::::get(), amount1); }); } diff --git a/pallets/capacity/src/types.rs b/pallets/capacity/src/types.rs index 7348aac4e0..f29d400a12 100644 --- a/pallets/capacity/src/types.rs +++ b/pallets/capacity/src/types.rs @@ -269,21 +269,83 @@ where pub started_at: BlockNumber, } -/// Needed data about a RewardPool for a given RewardEra. -/// The total_reward_pool balance for the previous era is set when a new era starts, -/// based on total staked token at the end of the previous era, and remains unchanged. -/// The unclaimed_balance is initialized to total_reward_pool and deducted whenever a -/// valid claim occurs. -#[derive( - PartialEq, Eq, Clone, Default, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, -)] -pub struct RewardPoolInfo { - /// the total staked for rewards in the associated RewardEra - pub total_staked_token: Balance, - /// the reward pool for this era - pub total_reward_pool: Balance, - /// the remaining rewards balance to be claimed - pub unclaimed_balance: Balance, +/// A chunk of Reward Pool history items consists of a BoundedBTreeMap, +/// RewardEra is the key and the total stake for the RewardPool is the value. +/// the map has up to T::RewardPoolChunkLength items, however, the chunk storing the current era +/// has only that one. +// TODO: on new era, copy CurrentEraProviderBoostTotal value to highest-era chunk +// TODO: on new era, delete oldest era value. +// TODO: on new era, zero out total in CurrentEraProviderBoostTotal +// TODO: on boost/unstake: update CEPBT +// TODO: on list_unclaimed_rewards, use new struct +// TODO: on has_unclaimed_rewards, use_new_struct +#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct RewardPoolHistoryChunk( + BoundedBTreeMap, T::RewardPoolChunkLength>, +); +impl Default for RewardPoolHistoryChunk { + fn default() -> Self { + Self::new() + } +} + +impl Clone for RewardPoolHistoryChunk { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl RewardPoolHistoryChunk { + /// Constructs a new empty RewardPoolHistoryChunk + pub fn new() -> Self { + RewardPoolHistoryChunk(BoundedBTreeMap::new()) + } + + /// A wrapper for retrieving how much was provider_boosted in the given era + /// from the BoundedBTreeMap + pub fn total_for_era(&self, reward_era: &T::RewardEra) -> Option<&BalanceOf> { + self.0.get(reward_era) + } + + /// returns the range of eras in this chunk + /// TODO: needed? + pub fn era_range(&self) -> (T::RewardEra, T::RewardEra) { + let zero_reward_era: T::RewardEra = Zero::zero(); + let zero_balance: BalanceOf = Zero::zero(); + let (first, _vf) = self.0.first_key_value().unwrap_or((&zero_reward_era, &zero_balance)); + let (last, _vl) = self.0.last_key_value().unwrap_or((&zero_reward_era, &zero_balance)); + (*first, *last) + } + + /// A wrapper for adding a new reward_era_entry to the BoundedBTreeMap + pub fn try_insert( + &mut self, + reward_era: T::RewardEra, + total: BalanceOf, + ) -> Result>, (T::RewardEra, BalanceOf)> { + self.0.try_insert(reward_era, total) + } + + /// A wrapper for removing a reward era entry from the BoundedBTreeMap + /// Returns the total stored at that entry. + /// TODO: needed? + pub fn remove(&mut self, reward_era: &T::RewardEra) -> Option> { + self.0.remove(reward_era) + } + + /// Get the earliest reward era stored in this BoundedBTreeMap + pub fn earliest_era(&self) -> Option<&T::RewardEra> { + if let Some((first_era, _first_total)) = self.0.first_key_value() { + return Some(first_era); + } + None + } + + /// Is this chunk full? It should always be yes once there is enough RewardPool history. + pub fn is_full(&self) -> bool { + self.0.len().eq(&(T::RewardPoolChunkLength::get() as usize)) + } } /// A record of staked amounts for a complete RewardEra diff --git a/pallets/capacity/src/weights.rs b/pallets/capacity/src/weights.rs index 20beb8b813..9029ed14e8 100644 --- a/pallets/capacity/src/weights.rs +++ b/pallets/capacity/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_capacity` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-05-21, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-30, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `ip-10-173-4-174`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` +//! HOSTNAME: `UL-Mac.local`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("frequency-bench")`, DB CACHE: `1024` // Executed Command: @@ -31,7 +31,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_capacity. +/// Weight functions needed for `pallet_capacity`. pub trait WeightInfo { fn stake() -> Weight; fn withdraw_unstaked() -> Weight; @@ -62,10 +62,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) fn stake() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `177` // Estimated: `6249` - // Minimum execution time: 40_905_000 picoseconds. - Weight::from_parts(41_735_000, 6249) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(38_000_000, 6249) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -81,8 +81,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `226` // Estimated: `6249` - // Minimum execution time: 27_968_000 picoseconds. - Weight::from_parts(28_258_000, 6249) + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(24_000_000, 6249) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -99,25 +99,25 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Capacity::ProviderBoostRewardPool` (r:3 w:2) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) - /// Storage: `Capacity::CounterForProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::CounterForProviderBoostRewardPool` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:0) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Capacity::ProviderBoostRewardPools` (r:1 w:1) + /// Proof: `Capacity::ProviderBoostRewardPools` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) fn start_new_reward_era_if_needed() -> Weight { // Proof Size summary in bytes: - // Measured: `613` - // Estimated: `10080` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 10080) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `158` + // Estimated: `5063` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 5063) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Capacity::ProviderBoostHistories` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(661), added: 3136, mode: `MaxEncodedLen`) + /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(641), added: 3116, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingAccountLedger` (r:1 w:1) /// Proof: `Capacity::StakingAccountLedger` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) - /// Storage: `Capacity::ProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:1) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Capacity::UnstakeUnlocks` (r:1 w:1) /// Proof: `Capacity::UnstakeUnlocks` (`max_values`: None, `max_size`: Some(121), added: 2596, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) @@ -126,10 +126,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn unstake() -> Weight { // Proof Size summary in bytes: - // Measured: `393` - // Estimated: `5611` - // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(38_000_000, 5611) + // Measured: `359` + // Estimated: `5591` + // Minimum execution time: 33_000_000 picoseconds. + Weight::from_parts(34_000_000, 5591) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -139,8 +139,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_267_000 picoseconds. - Weight::from_parts(6_644_000, 0) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_000_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Capacity::Retargets` (r:1 w:1) @@ -155,10 +155,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn change_staking_target() -> Weight { // Proof Size summary in bytes: - // Measured: `315` + // Measured: `353` // Estimated: `7601` - // Minimum execution time: 31_000_000 picoseconds. - Weight::from_parts(32_000_000, 7601) + // Minimum execution time: 32_000_000 picoseconds. + Weight::from_parts(33_000_000, 7601) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -166,26 +166,26 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Msa::ProviderToRegistryEntry` (`max_values`: None, `max_size`: Some(33), added: 2508, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingAccountLedger` (r:1 w:1) /// Proof: `Capacity::StakingAccountLedger` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) - /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) - /// Proof: `Capacity::StakingTargetLedger` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) - /// Storage: `Capacity::CapacityLedger` (r:1 w:1) - /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) - /// Storage: `Capacity::ProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) /// Storage: `Capacity::UnstakeUnlocks` (r:1 w:0) /// Proof: `Capacity::UnstakeUnlocks` (`max_values`: None, `max_size`: Some(121), added: 2596, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:1) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:0) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) + /// Proof: `Capacity::StakingTargetLedger` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CapacityLedger` (r:1 w:1) + /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Capacity::ProviderBoostHistories` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(661), added: 3136, mode: `MaxEncodedLen`) + /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(641), added: 3116, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:1) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn provider_boost() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `177` // Estimated: `6249` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(47_000_000, 6249) + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(45_000_000, 6249) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -209,10 +209,10 @@ impl WeightInfo for () { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) fn stake() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `177` // Estimated: `6249` - // Minimum execution time: 40_905_000 picoseconds. - Weight::from_parts(41_735_000, 6249) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(38_000_000, 6249) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -228,8 +228,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `226` // Estimated: `6249` - // Minimum execution time: 27_968_000 picoseconds. - Weight::from_parts(28_258_000, 6249) + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(24_000_000, 6249) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -246,25 +246,25 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Capacity::ProviderBoostRewardPool` (r:3 w:2) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) - /// Storage: `Capacity::CounterForProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::CounterForProviderBoostRewardPool` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:0) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Capacity::ProviderBoostRewardPools` (r:1 w:1) + /// Proof: `Capacity::ProviderBoostRewardPools` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) fn start_new_reward_era_if_needed() -> Weight { // Proof Size summary in bytes: - // Measured: `613` - // Estimated: `10080` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 10080) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `158` + // Estimated: `5063` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 5063) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Capacity::ProviderBoostHistories` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(661), added: 3136, mode: `MaxEncodedLen`) + /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(641), added: 3116, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingAccountLedger` (r:1 w:1) /// Proof: `Capacity::StakingAccountLedger` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) - /// Storage: `Capacity::ProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:1) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Capacity::UnstakeUnlocks` (r:1 w:1) /// Proof: `Capacity::UnstakeUnlocks` (`max_values`: None, `max_size`: Some(121), added: 2596, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) @@ -273,10 +273,10 @@ impl WeightInfo for () { /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn unstake() -> Weight { // Proof Size summary in bytes: - // Measured: `393` - // Estimated: `5611` - // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(38_000_000, 5611) + // Measured: `359` + // Estimated: `5591` + // Minimum execution time: 33_000_000 picoseconds. + Weight::from_parts(34_000_000, 5591) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -286,8 +286,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_267_000 picoseconds. - Weight::from_parts(6_644_000, 0) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Capacity::Retargets` (r:1 w:1) @@ -302,10 +302,10 @@ impl WeightInfo for () { /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn change_staking_target() -> Weight { // Proof Size summary in bytes: - // Measured: `315` + // Measured: `353` // Estimated: `7601` - // Minimum execution time: 29_000_000 picoseconds. - Weight::from_parts(31_000_000, 7601) + // Minimum execution time: 32_000_000 picoseconds. + Weight::from_parts(33_000_000, 7601) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -313,26 +313,26 @@ impl WeightInfo for () { /// Proof: `Msa::ProviderToRegistryEntry` (`max_values`: None, `max_size`: Some(33), added: 2508, mode: `MaxEncodedLen`) /// Storage: `Capacity::StakingAccountLedger` (r:1 w:1) /// Proof: `Capacity::StakingAccountLedger` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) - /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) - /// Proof: `Capacity::StakingTargetLedger` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) - /// Storage: `Capacity::CapacityLedger` (r:1 w:1) - /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) - /// Storage: `Capacity::ProviderBoostRewardPool` (r:1 w:1) - /// Proof: `Capacity::ProviderBoostRewardPool` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) /// Storage: `Capacity::UnstakeUnlocks` (r:1 w:0) /// Proof: `Capacity::UnstakeUnlocks` (`max_values`: None, `max_size`: Some(121), added: 2596, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:1) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Balances::Locks` (r:1 w:0) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Capacity::StakingTargetLedger` (r:1 w:1) + /// Proof: `Capacity::StakingTargetLedger` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CapacityLedger` (r:1 w:1) + /// Proof: `Capacity::CapacityLedger` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Capacity::ProviderBoostHistories` (r:1 w:1) /// Proof: `Capacity::ProviderBoostHistories` (`max_values`: None, `max_size`: Some(641), added: 3116, mode: `MaxEncodedLen`) + /// Storage: `Capacity::CurrentEraProviderBoostTotal` (r:1 w:1) + /// Proof: `Capacity::CurrentEraProviderBoostTotal` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn provider_boost() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `177` // Estimated: `6249` - // Minimum execution time: 45_000_000 picoseconds. - Weight::from_parts(48_000_000, 6249) + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(45_000_000, 6249) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -396,7 +396,6 @@ mod tests { > 2974 ); } - #[test] fn test_start_new_reward_era_if_needed() { assert!( @@ -406,7 +405,7 @@ mod tests { .max_extrinsic .unwrap_or_else(::max_value) .proof_size() - > 10080 + > 5063 ); } #[test] @@ -418,7 +417,7 @@ mod tests { .max_extrinsic .unwrap_or_else(::max_value) .proof_size() - > 5071 + > 5591 ); } #[test] diff --git a/pallets/frequency-tx-payment/src/tests/mock.rs b/pallets/frequency-tx-payment/src/tests/mock.rs index 7ab52a3a1a..e6a466ed5f 100644 --- a/pallets/frequency-tx-payment/src/tests/mock.rs +++ b/pallets/frequency-tx-payment/src/tests/mock.rs @@ -220,11 +220,12 @@ impl pallet_capacity::Config for Test { type RuntimeFreezeReason = RuntimeFreezeReason; type RewardEra = u32; type EraLength = ConstU32<5>; - type ProviderBoostHistoryLimit = ConstU32<2>; + type ProviderBoostHistoryLimit = ConstU32<6>; type RewardsProvider = Capacity; type MaxRetargetsPerRewardEra = ConstU32<5>; type RewardPoolEachEra = ConstU64<10_000>; type RewardPercentCap = TestRewardCap; + type RewardPoolChunkLength = ConstU32<2>; } use pallet_balances::Call as BalancesCall; diff --git a/runtime/frequency/src/lib.rs b/runtime/frequency/src/lib.rs index 755e1fc2be..ad0f808296 100644 --- a/runtime/frequency/src/lib.rs +++ b/runtime/frequency/src/lib.rs @@ -561,12 +561,14 @@ impl pallet_capacity::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type RewardEra = u32; type EraLength = ConstU32<{ 14 * DAYS }>; - type ProviderBoostHistoryLimit = ConstU32<31u32>; // 30 for claiming rewards, 1 for current era + type ProviderBoostHistoryLimit = ConstU32<30u32>; type RewardsProvider = Capacity; type MaxRetargetsPerRewardEra = ConstU32<16>; // Value determined by desired inflation rate limits for chosen economic model type RewardPoolEachEra = ConstU128<{ currency::CENTS.saturating_mul(172_602_740u128) }>; type RewardPercentCap = CapacityRewardCap; + // Must evenly divide ProviderBoostHistoryLimit + type RewardPoolChunkLength = ConstU32<5>; } impl pallet_schemas::Config for Runtime {