From 9afc68ae9004dd1cbfbe616871574ddaa99be0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:57:40 +0100 Subject: [PATCH] dApp Staking v3 - Fix for Incorrect (Incomplete) Expired Entry Cleanup (#1137) * Fix for incorrect add stake * Minor adjustments * Adjustments * Typo & renaming * More review adjustments * Cycle Alignment * dApp staking modificiations * Fix tests * Migration logic * integration tests * Changes,updated weights * Minor changes * Format * Fixes * Fix failing UTs * Improved history cleanup * Updates & tests * Fixes, extend test utils * Benchmarks part1 * Finished benchmarks * Minor changes * Upgrade logic reset * Minor tests * Additional check --- .../dapp-staking-v3/src/benchmarking/mod.rs | 83 ++++++++----- .../dapp-staking-v3/src/benchmarking/utils.rs | 33 +----- pallets/dapp-staking-v3/src/lib.rs | 109 +++++++++++------- pallets/dapp-staking-v3/src/migrations.rs | 10 ++ .../dapp-staking-v3/src/test/testing_utils.rs | 107 +++++++++-------- .../dapp-staking-v3/src/test/tests_types.rs | 36 ++++++ pallets/dapp-staking-v3/src/types.rs | 15 ++- runtime/shibuya/src/lib.rs | 1 + 8 files changed, 248 insertions(+), 146 deletions(-) diff --git a/pallets/dapp-staking-v3/src/benchmarking/mod.rs b/pallets/dapp-staking-v3/src/benchmarking/mod.rs index abb37ee071..0069ec9b55 100644 --- a/pallets/dapp-staking-v3/src/benchmarking/mod.rs +++ b/pallets/dapp-staking-v3/src/benchmarking/mod.rs @@ -490,7 +490,7 @@ mod benchmarks { #[extrinsic_call] claim_staker_rewards(RawOrigin::Signed(staker.clone())); - // No need to do precise check of values, but predetermiend amount of 'Reward' events is expected. + // No need to do precise check of values, but predetermined amount of 'Reward' events is expected. let dapp_staking_events = dapp_staking_events::(); assert_eq!(dapp_staking_events.len(), x as usize); dapp_staking_events.iter().for_each(|e| { @@ -561,7 +561,7 @@ mod benchmarks { #[extrinsic_call] claim_staker_rewards(RawOrigin::Signed(staker.clone())); - // No need to do precise check of values, but predetermiend amount of 'Reward' events is expected. + // No need to do precise check of values, but predetermined amount of 'Reward' events is expected. let dapp_staking_events = dapp_staking_events::(); assert_eq!(dapp_staking_events.len(), x as usize); dapp_staking_events.iter().for_each(|e| { @@ -662,7 +662,7 @@ mod benchmarks { // Advance enough eras so dApp reward can be claimed. force_advance_to_next_subperiod::(); - // This is a hacky part to ensure we accomodate max number of contracts. + // This is a hacky part to ensure we accommodate max number of contracts. TierConfig::::mutate(|config| { let max_number_of_contracts: u16 = T::MaxNumberOfContracts::get().try_into().unwrap(); config.number_of_slots = max_number_of_contracts; @@ -840,6 +840,12 @@ mod benchmarks { // Register & stake contracts, just so we don't have empty stakes. prepare_contracts_for_tier_assignment::(max_number_of_contracts::()); + // Force advance enough periods into the future so we can ensure that history + // cleanup marker will be updated on the next period change. + let period_before_expiry_starts = + ActiveProtocolState::::get().period_number() + T::RewardRetentionInPeriods::get(); + force_advance_to_period::(period_before_expiry_starts); + // Advance to build&earn subperiod force_advance_to_next_subperiod::(); let snapshot_state = ActiveProtocolState::::get(); @@ -853,7 +859,7 @@ mod benchmarks { ); run_to_block::(ActiveProtocolState::::get().next_era_start - 1); - // Some sanity checks, we should still be in the build&earn subperiod, and in the first period. + // Some sanity checks, we should still be in the build&earn subperiod, and in the same period as when snapshot was taken. assert_eq!( ActiveProtocolState::::get().subperiod(), Subperiod::BuildAndEarn @@ -867,6 +873,8 @@ mod benchmarks { DappStaking::::on_finalize(new_era_start_block - 1); System::::set_block_number(new_era_start_block); + let pre_cleanup_marker = HistoryCleanupMarker::::get(); + #[block] { DappStaking::::era_and_period_handler(new_era_start_block, TierAssignment::Dummy); @@ -880,6 +888,9 @@ mod benchmarks { ActiveProtocolState::::get().period_number(), snapshot_state.period_number() + 1, ); + assert!( + HistoryCleanupMarker::::get().oldest_valid_era > pre_cleanup_marker.oldest_valid_era + ); } #[benchmark] @@ -928,7 +939,7 @@ mod benchmarks { // Investigate why the PoV size is so large here, even after removing read of `IntegratedDApps` storage. // Relevant file: polkadot-sdk/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs - // UPDATE: after some investigation, it seems that PoV size benchmarks are very unprecise + // UPDATE: after some investigation, it seems that PoV size benchmarks are very imprecise // - the worst case measured is usually very far off the actual value that is consumed on chain. // There's an ongoing item to improve it (mentioned on roundtable meeting). #[benchmark] @@ -960,39 +971,57 @@ mod benchmarks { // Prepare init config (protocol state, tier params & config, etc.) initial_config::(); - // Advance to era just after the last era covered by the first span. - // This is sufficient to completely fill up the first span with entries for the ongoing era. - force_advance_to_era::(T::EraRewardSpanLength::get()); - - // Advance enough periods to make cleanup feasible. - let retention_period = T::RewardRetentionInPeriods::get(); - force_advance_to_period::( - ActiveProtocolState::::get().period_number() + retention_period + 2, + // Hack + // Manually prepare state prior to the cleanup to ensure worst case. + let cleanup_marker = CleanupMarker { + era_reward_index: 0, + dapp_tiers_index: 0, + oldest_valid_era: T::EraRewardSpanLength::get().into(), + }; + HistoryCleanupMarker::::put(cleanup_marker); + + // Prepare completely filled up reward span and insert it into storage. + let mut reward_span = EraRewardSpan::<_>::new(); + (0..T::EraRewardSpanLength::get()).for_each(|era| { + assert_ok!(reward_span.push( + era as EraNumber, + EraReward { + staker_reward_pool: 1_000_000_000_000, + staked: 1_000_000_000_000, + dapp_reward_pool: 1_000_000_000_000, + }, + )); + }); + EraRewards::::insert(&cleanup_marker.era_reward_index, reward_span); + + // Prepare completely filled up tier rewards and insert it into storage. + DAppTiers::::insert( + &cleanup_marker.dapp_tiers_index, + DAppTierRewardsFor:: { + dapps: (0..T::MaxNumberOfContracts::get()) + .map(|dapp_id| (dapp_id as DAppId, 0)) + .collect::>() + .try_into() + .expect("Using `MaxNumberOfContracts` as length; QED."), + rewards: vec![1_000_000_000_000; T::NumberOfTiers::get() as usize] + .try_into() + .expect("Using `NumberOfTiers` as length; QED."), + period: 1, + }, ); - let first_era_span_index = 0; - assert!( - EraRewards::::contains_key(first_era_span_index), - "Sanity check - era reward span entry must exist." - ); - let first_period = 1; - assert!( - PeriodEnd::::contains_key(first_period), - "Sanity check - period end info must exist." - ); let block_number = System::::block_number(); - #[block] { DappStaking::::on_idle(block_number, Weight::MAX); } assert!( - !EraRewards::::contains_key(first_era_span_index), - "Entry should have been cleaned up." + !EraRewards::::contains_key(cleanup_marker.era_reward_index), + "Reward span should have been cleaned up." ); assert!( - !PeriodEnd::::contains_key(first_period), + !DAppTiers::::contains_key(cleanup_marker.dapp_tiers_index), "Period end info should have been cleaned up." ); } diff --git a/pallets/dapp-staking-v3/src/benchmarking/utils.rs b/pallets/dapp-staking-v3/src/benchmarking/utils.rs index 28dde52267..81a788da4b 100644 --- a/pallets/dapp-staking-v3/src/benchmarking/utils.rs +++ b/pallets/dapp-staking-v3/src/benchmarking/utils.rs @@ -77,29 +77,6 @@ pub(crate) fn force_advance_to_next_era() { run_for_blocks::(One::one()); } -/// Advance blocks until the specified period has been reached. -/// -/// Function has no effect if period is already passed. -pub(super) fn _advance_to_period(period: PeriodNumber) { - assert!(period >= ActiveProtocolState::::get().period_number()); - while ActiveProtocolState::::get().period_number() < period { - run_for_blocks::(One::one()); - } -} - -/// Advance to the specified period, using the `force` approach. -pub(super) fn force_advance_to_period(period: PeriodNumber) { - assert!(period >= ActiveProtocolState::::get().period_number()); - while ActiveProtocolState::::get().period_number() < period { - force_advance_to_next_subperiod::(); - } -} - -/// Advance blocks until next period has been reached. -pub(super) fn _advance_to_next_period() { - _advance_to_period::(ActiveProtocolState::::get().period_number() + 1); -} - /// Advance blocks until next period has been reached. /// /// Relies on the `force` approach to advance one subperiod per block. @@ -114,11 +91,11 @@ pub(super) fn force_advance_to_next_period() { } } -/// Advance blocks until next period type has been reached. -pub(super) fn _advance_to_next_subperiod() { - let subperiod = ActiveProtocolState::::get().subperiod(); - while ActiveProtocolState::::get().subperiod() == subperiod { - run_for_blocks::(One::one()); +/// Advance to the specified period, using the `force` approach. +pub(super) fn force_advance_to_period(period: PeriodNumber) { + assert!(period >= ActiveProtocolState::::get().period_number()); + while ActiveProtocolState::::get().period_number() < period { + force_advance_to_next_subperiod::(); } } diff --git a/pallets/dapp-staking-v3/src/lib.rs b/pallets/dapp-staking-v3/src/lib.rs index 7525370f4d..e1854b0e5d 100644 --- a/pallets/dapp-staking-v3/src/lib.rs +++ b/pallets/dapp-staking-v3/src/lib.rs @@ -1904,6 +1904,10 @@ pub mod pallet { TierConfig::::get().calculate_new(average_price, &tier_params); TierConfig::::put(new_tier_config); + // Update historical cleanup marker. + // Must be called with the new period number. + Self::update_cleanup_marker(protocol_state.period_number()); + consumed_weight.saturating_accrue( T::WeightInfo::on_initialize_build_and_earn_to_voting(), ); @@ -1963,70 +1967,86 @@ pub mod pallet { T::Observers::block_before_new_era(next_era) } - /// Attempt to cleanup some expired entries, if enough remaining weight & applicable entries exist. + /// Updates the cleanup marker with the new oldest valid era if possible. /// - /// Returns consumed weight. - fn expired_entry_cleanup(remaining_weight: &Weight) -> Weight { - // Need to be able to process full pass - if remaining_weight.any_lt(T::WeightInfo::on_idle_cleanup()) { - return Weight::zero(); - } - - // Get the cleanup marker - let mut cleanup_marker = HistoryCleanupMarker::::get(); - - // Whitelisted storage, no need to account for the read. - let protocol_state = ActiveProtocolState::::get(); - let latest_expired_period = match protocol_state - .period_number() + /// It's possible that the call will be a no-op since we haven't advanced enough periods yet. + fn update_cleanup_marker(new_period_number: PeriodNumber) { + // 1. Find out the latest expired period; rewards can no longer be claimed for it or any older period. + let latest_expired_period = match new_period_number .checked_sub(T::RewardRetentionInPeriods::get().saturating_add(1)) { - Some(latest_expired_period) => latest_expired_period, - None => { - // Protocol hasn't advanced enough to have any expired entries. - return T::WeightInfo::on_idle_cleanup(); - } + Some(period) if !period.is_zero() => period, + // Haven't advanced enough periods to have any expired entries. + _ => return, }; - // Get the oldest valid era - any era before it is safe to be cleaned up. - let oldest_valid_era = match PeriodEnd::::get(latest_expired_period) { + // 2. Find the oldest valid era for which rewards can still be claimed. + // Technically, this will be `Voting` subperiod era but it doesn't matter. + // + // Also, remove the expired `PeriodEnd` entry since it's no longer needed. + let oldest_valid_era = match PeriodEnd::::take(latest_expired_period) { Some(period_end_info) => period_end_info.final_era.saturating_add(1), None => { - // Can happen if it's period 0 or if the entry has already been cleaned up. - return T::WeightInfo::on_idle_cleanup(); + // Should never happen but nothing we can do if it does. + log::error!( + target: LOG_TARGET, + "No `PeriodEnd` entry for the expired period: {}", + latest_expired_period + ); + return; } }; - // Attempt to cleanup one expired `EraRewards` entry. - if let Some(era_reward) = EraRewards::::get(cleanup_marker.era_reward_index) { - // If oldest valid era comes AFTER this span, it's safe to delete it. - if era_reward.last_era() < oldest_valid_era { - EraRewards::::remove(cleanup_marker.era_reward_index); + // 3. Update the cleanup marker with the new oldest valid era. + HistoryCleanupMarker::::mutate(|marker| { + marker.oldest_valid_era = oldest_valid_era; + }); + } + + /// Attempt to cleanup some expired entries, if enough remaining weight & applicable entries exist. + /// + /// Returns consumed weight. + fn expired_entry_cleanup(remaining_weight: &Weight) -> Weight { + // Need to be able to process one full pass + if remaining_weight.any_lt(T::WeightInfo::on_idle_cleanup()) { + return Weight::zero(); + } + + // Get the cleanup marker and ensure we have pending cleanups. + let mut cleanup_marker = HistoryCleanupMarker::::get(); + if !cleanup_marker.has_pending_cleanups() { + return T::DbWeight::get().reads(1); + } + + // 1. Attempt to cleanup one expired `EraRewards` entry. + if cleanup_marker.era_reward_index < cleanup_marker.oldest_valid_era { + if let Some(era_reward) = EraRewards::::get(cleanup_marker.era_reward_index) { + // If oldest valid era comes AFTER this span, it's safe to delete it. + if era_reward.last_era() < cleanup_marker.oldest_valid_era { + EraRewards::::remove(cleanup_marker.era_reward_index); + cleanup_marker + .era_reward_index + .saturating_accrue(T::EraRewardSpanLength::get()); + } + } else { + // Can happen if the entry is part of history before dApp staking v3 + log::warn!( + target: LOG_TARGET, + "Era rewards span for era {} is missing, but cleanup marker is set.", + cleanup_marker.era_reward_index + ); cleanup_marker .era_reward_index .saturating_accrue(T::EraRewardSpanLength::get()); } - } else { - // Should never happen, but if it does, log an error and move on. - log::error!( - target: LOG_TARGET, - "Era rewards span for era {} is missing, but cleanup marker is set.", - cleanup_marker.era_reward_index - ); } - // Attempt to cleanup one expired `DAppTiers` entry. - if cleanup_marker.dapp_tiers_index < oldest_valid_era { + // 2. Attempt to cleanup one expired `DAppTiers` entry. + if cleanup_marker.dapp_tiers_index < cleanup_marker.oldest_valid_era { DAppTiers::::remove(cleanup_marker.dapp_tiers_index); cleanup_marker.dapp_tiers_index.saturating_inc(); } - // One extra grace period before we cleanup period end info. - // This so we can always read the `final_era` of that period. - if let Some(period_end_cleanup) = latest_expired_period.checked_sub(1) { - PeriodEnd::::remove(period_end_cleanup); - } - // Store the updated cleanup marker HistoryCleanupMarker::::put(cleanup_marker); @@ -2034,6 +2054,7 @@ pub mod pallet { // we opt for the simpler solution where only 1 entry per block is cleaned up. // It can be changed though. + // It could end up being less than this weight, but this won't occur often enough to be important. T::WeightInfo::on_idle_cleanup() } } diff --git a/pallets/dapp-staking-v3/src/migrations.rs b/pallets/dapp-staking-v3/src/migrations.rs index 5cb41d27e1..52e5f6da74 100644 --- a/pallets/dapp-staking-v3/src/migrations.rs +++ b/pallets/dapp-staking-v3/src/migrations.rs @@ -200,3 +200,13 @@ impl OnRuntimeUpgrade for DappStakingV3TierRewardAsTree { T::DbWeight::get().reads_writes(counter, counter) } } + +/// We just set it to default value (all zeros) and let the pallet itself do the history cleanup. +/// Only needed for Shibuya, can be removed later. +pub struct DappStakingV3HistoryCleanupMarkerReset(PhantomData); +impl OnRuntimeUpgrade for DappStakingV3HistoryCleanupMarkerReset { + fn on_runtime_upgrade() -> Weight { + HistoryCleanupMarker::::put(CleanupMarker::default()); + T::DbWeight::get().writes(1) + } +} diff --git a/pallets/dapp-staking-v3/src/test/testing_utils.rs b/pallets/dapp-staking-v3/src/test/testing_utils.rs index 1d03a8a4d0..b6ed9e3511 100644 --- a/pallets/dapp-staking-v3/src/test/testing_utils.rs +++ b/pallets/dapp-staking-v3/src/test/testing_utils.rs @@ -25,7 +25,7 @@ use crate::{ }; use frame_support::{ - assert_ok, assert_storage_noop, + assert_ok, traits::{fungible::InspectFreeze, Get, OnIdle}, weights::Weight, }; @@ -60,6 +60,7 @@ pub(crate) struct MemorySnapshot { era_rewards: HashMap::EraRewardSpanLength>>, period_end: HashMap, dapp_tiers: HashMap>, + cleanup_marker: CleanupMarker, } impl MemorySnapshot { @@ -78,6 +79,7 @@ impl MemorySnapshot { era_rewards: EraRewards::::iter().collect(), period_end: PeriodEnd::::iter().collect(), dapp_tiers: DAppTiers::::iter().collect(), + cleanup_marker: HistoryCleanupMarker::::get(), } } @@ -1354,7 +1356,39 @@ pub(crate) fn assert_block_bump(pre_snapshot: &MemorySnapshot) { ); } - // 5. Verify event(s) + // 5. Verify history cleanup marker update + let period_has_advanced = pre_protoc_state.period_number() < post_protoc_state.period_number(); + if period_has_advanced { + let reward_retention_in_periods: PeriodNumber = + ::RewardRetentionInPeriods::get(); + + let pre_marker = pre_snapshot.cleanup_marker; + let post_marker = post_snapshot.cleanup_marker; + + if let Some(expired_period) = pre_protoc_state + .period_number() + .checked_sub(reward_retention_in_periods) + { + if let Some(period_end_info) = pre_snapshot.period_end.get(&expired_period) { + let oldest_valid_era = period_end_info.final_era + 1; + + assert_eq!(post_marker.oldest_valid_era, oldest_valid_era); + assert_eq!(post_marker.dapp_tiers_index, pre_marker.dapp_tiers_index); + assert_eq!(post_marker.era_reward_index, pre_marker.era_reward_index); + + assert!( + !post_snapshot.period_end.contains_key(&expired_period), + "Expired entry should have been removed." + ); + } else { + assert_eq!(pre_marker, post_marker, "Must remain unchanged."); + } + } else { + assert_eq!(pre_marker, post_marker, "Must remain unchanged."); + } + } + + // 6. Verify event(s) if is_new_subperiod { let events = dapp_staking_events(); assert!( @@ -1385,45 +1419,24 @@ pub(crate) fn assert_block_bump(pre_snapshot: &MemorySnapshot) { pub(crate) fn assert_on_idle_cleanup() { // Pre-data snapshot (limited to speed up testing) let pre_cleanup_marker = HistoryCleanupMarker::::get(); - let pre_era_rewards: HashMap::EraRewardSpanLength>> = - EraRewards::::iter().collect(); - let pre_period_ends: HashMap = PeriodEnd::::iter().collect(); - - // Calculated the oldest era which is valid (not expired) - let protocol_state = ActiveProtocolState::::get(); - let retention_period: PeriodNumber = ::RewardRetentionInPeriods::get(); - - let oldest_valid_era = match protocol_state - .period_number() - .checked_sub(retention_period + 1) - { - Some(expired_period) if expired_period > 0 => { - pre_period_ends[&expired_period].final_era + 1 - } - _ => { - // No cleanup so no storage changes are expected - assert_storage_noop!(DappStaking::on_idle(System::block_number(), Weight::MAX)); - return; - } - }; - // Check if any span or tiers cleanup is needed. + // Check if any span or tier reward cleanup is needed. let is_era_span_cleanup_expected = - pre_era_rewards[&pre_cleanup_marker.era_reward_index].last_era() < oldest_valid_era; - let is_dapp_tiers_cleanup_expected = pre_cleanup_marker.dapp_tiers_index > 0 - && pre_cleanup_marker.dapp_tiers_index < oldest_valid_era; - - // Check if period end info should be cleaned up - let maybe_period_end_cleanup = match protocol_state - .period_number() - .checked_sub(retention_period + 2) - { - Some(period) if period > 0 => Some(period), - _ => None, - }; + EraRewards::::get(&pre_cleanup_marker.era_reward_index) + .map(|span| span.last_era() < pre_cleanup_marker.oldest_valid_era) + .unwrap_or(false); + let is_dapp_tiers_cleanup_expected = + pre_cleanup_marker.dapp_tiers_index < pre_cleanup_marker.oldest_valid_era; + + // If span doesn't exists, but no cleanup is expected, we should increment the era reward index anyway. + // This is because the span was never created in the first place since dApp staking v3 wasn't active then. + // + // In case of cleanup, we always increment the index. + let is_era_reward_index_increase = is_era_span_cleanup_expected + || !EraRewards::::contains_key(&pre_cleanup_marker.era_reward_index) + && pre_cleanup_marker.oldest_valid_era > pre_cleanup_marker.era_reward_index; // Cleanup and verify post state. - DappStaking::on_idle(System::block_number(), Weight::MAX); // Post checks @@ -1433,26 +1446,30 @@ pub(crate) fn assert_on_idle_cleanup() { assert!(!EraRewards::::contains_key( pre_cleanup_marker.era_reward_index )); + } + + if is_era_reward_index_increase { let span_length: EraNumber = ::EraRewardSpanLength::get(); assert_eq!( post_cleanup_marker.era_reward_index, pre_cleanup_marker.era_reward_index + span_length ); } + if is_dapp_tiers_cleanup_expected { - assert!( - !DAppTiers::::contains_key(pre_cleanup_marker.dapp_tiers_index), - "Sanity check." - ); + assert!(!DAppTiers::::contains_key( + pre_cleanup_marker.dapp_tiers_index + )); assert_eq!( post_cleanup_marker.dapp_tiers_index, pre_cleanup_marker.dapp_tiers_index + 1 - ) + ); } - if let Some(period) = maybe_period_end_cleanup { - assert!(!PeriodEnd::::contains_key(period)); - } + assert_eq!( + post_cleanup_marker.oldest_valid_era, pre_cleanup_marker.oldest_valid_era, + "Sanity check, must remain unchanged." + ); } /// Returns from which starting era to which ending era can rewards be claimed for the specified account. diff --git a/pallets/dapp-staking-v3/src/test/tests_types.rs b/pallets/dapp-staking-v3/src/test/tests_types.rs index 91d18964c5..5e5eebf5ef 100644 --- a/pallets/dapp-staking-v3/src/test/tests_types.rs +++ b/pallets/dapp-staking-v3/src/test/tests_types.rs @@ -2803,3 +2803,39 @@ fn dapp_tier_rewards_basic_tests() { "dApp doesn't exist in the list so no rewards can be claimed." ); } + +#[test] +fn cleanup_marker_works() { + let cleanup_marker = CleanupMarker::default(); + assert!(!cleanup_marker.has_pending_cleanups()); + + let cleanup_marker = CleanupMarker { + era_reward_index: 1, + dapp_tiers_index: 2, + oldest_valid_era: 3, + }; + assert!( + cleanup_marker.has_pending_cleanups(), + "There are pending cleanups for both era rewards and dApp tiers." + ); + + let cleanup_marker = CleanupMarker { + era_reward_index: 7, + dapp_tiers_index: 6, + oldest_valid_era: 7, + }; + assert!( + cleanup_marker.has_pending_cleanups(), + "There are pending cleanups for dApp tiers." + ); + + let cleanup_marker = CleanupMarker { + era_reward_index: 9, + dapp_tiers_index: 11, + oldest_valid_era: 11, + }; + assert!( + cleanup_marker.has_pending_cleanups(), + "There are pending cleanups for era reward spans." + ); +} diff --git a/pallets/dapp-staking-v3/src/types.rs b/pallets/dapp-staking-v3/src/types.rs index 5216a5b799..870e00223f 100644 --- a/pallets/dapp-staking-v3/src/types.rs +++ b/pallets/dapp-staking-v3/src/types.rs @@ -1717,8 +1717,19 @@ pub enum DAppTierError { pub struct CleanupMarker { /// Era reward span index that should be checked & cleaned up next. #[codec(compact)] - pub era_reward_index: EraNumber, + pub(crate) era_reward_index: EraNumber, /// dApp tier rewards index that should be checked & cleaned up next. #[codec(compact)] - pub dapp_tiers_index: EraNumber, + pub(crate) dapp_tiers_index: EraNumber, + /// Oldest valid era or earliest era in the oldest valid period. + #[codec(compact)] + pub(crate) oldest_valid_era: EraNumber, +} + +impl CleanupMarker { + /// Used to check whether there are any pending cleanups, according to marker values. + pub(crate) fn has_pending_cleanups(&self) -> bool { + self.era_reward_index != self.oldest_valid_era + || self.dapp_tiers_index != self.oldest_valid_era + } } diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 06541ae27b..ef88bce2a0 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -1387,6 +1387,7 @@ impl Get for InitActivePriceGet { pub type Migrations = ( pallet_static_price_provider::InitActivePrice, pallet_dapp_staking_v3::migrations::DappStakingV3TierRewardAsTree, + pallet_dapp_staking_v3::migrations::DappStakingV3HistoryCleanupMarkerReset, pallet_inflation::PalletInflationShibuyaMigration, );