From 2faed815c81946966119b58cbd161a640b1bcbfc Mon Sep 17 00:00:00 2001 From: Igor Papandinas <26460174+ipapandinas@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:52:00 +0200 Subject: [PATCH] sanity check: double check threshold variations --- pallets/dapp-staking-v3/src/migration.rs | 119 ++++++++++++++++++----- runtime/astar/src/lib.rs | 8 +- runtime/shibuya/src/lib.rs | 8 +- runtime/shiden/src/lib.rs | 8 +- 4 files changed, 115 insertions(+), 28 deletions(-) diff --git a/pallets/dapp-staking-v3/src/migration.rs b/pallets/dapp-staking-v3/src/migration.rs index 6918d848f2..57901383f6 100644 --- a/pallets/dapp-staking-v3/src/migration.rs +++ b/pallets/dapp-staking-v3/src/migration.rs @@ -44,13 +44,14 @@ pub mod versioned_migrations { /// Migration V7 to V8 wrapped in a [`frame_support::migrations::VersionedMigration`], ensuring /// the migration is only performed when on-chain version is 7. - pub type V7ToV8 = frame_support::migrations::VersionedMigration< - 7, - 8, - v8::VersionMigrateV7ToV8, - Pallet, - ::DbWeight, - >; + pub type V7ToV8 = + frame_support::migrations::VersionedMigration< + 7, + 8, + v8::VersionMigrateV7ToV8, + Pallet, + ::DbWeight, + >; } // TierThreshold as percentage of the total issuance @@ -59,10 +60,16 @@ mod v8 { use crate::migration::v7::TierParameters as TierParametersV7; use crate::migration::v7::TiersConfiguration as TiersConfigurationV7; - pub struct VersionMigrateV7ToV8(PhantomData<(T, TierThresholds)>); + pub struct VersionMigrateV7ToV8( + PhantomData<(T, TierThresholds, ThresholdVariationPercentage)>, + ); - impl> OnRuntimeUpgrade - for VersionMigrateV7ToV8 + impl< + T: Config, + TierThresholds: Get<[TierThreshold; 4]>, + ThresholdVariationPercentage: Get, + > OnRuntimeUpgrade + for VersionMigrateV7ToV8 { fn on_runtime_upgrade() -> Weight { // 1. Update static tier parameters with new thresholds from the runtime configurable param TierThresholds @@ -151,51 +158,103 @@ mod v8 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { + let tier_thresholds: Result, _> = + BoundedVec::try_from(TierThresholds::get().to_vec()); + assert!(tier_thresholds.is_ok()); + let old_config = v7::TierConfig::::get().ok_or_else(|| { TryRuntimeError::Other( "dapp-staking-v3::migration::v8: No old configuration found for TierConfig", ) })?; - Ok(old_config.number_of_slots.encode()) + Ok((old_config.number_of_slots, old_config.tier_thresholds).encode()) } #[cfg(feature = "try-runtime")] fn post_upgrade(data: Vec) -> Result<(), TryRuntimeError> { - let old_number_of_slots = u16::decode(&mut &data[..]).map_err(|_| { - TryRuntimeError::Other("dapp-staking-v3::migration::v8: Failed to decode old value for number of slots") + let (old_number_of_slots, old_tier_thresholds): (u16, BoundedVec) = + Decode::decode(&mut &data[..]).map_err(|_| { + TryRuntimeError::Other("dapp-staking-v3::migration::v8: Failed to decode old v7 version of tier config") })?; + // 0. Prerequisites let actual_config = TierConfig::::get(); + assert!(actual_config.is_valid()); + + ensure!( + Pallet::::on_chain_storage_version() >= 8, + "dapp-staking-v3::migration::v8: Wrong storage version." + ); - // Calculated based on "slots_per_tier", which might have slight variations due to the nature of saturating permill distribution. + // 1. Ensure the number of slots is preserved let actual_number_of_slots = actual_config.total_number_of_slots(); - let within_tolerance = (old_number_of_slots - 1)..=old_number_of_slots; + let within_tolerance = + (old_number_of_slots.saturating_sub(1))..=old_number_of_slots.saturating_add(1); + assert!( within_tolerance.contains(&actual_number_of_slots), - "dapp-staking-v3::migration::v8: New TiersConfiguration format not set correctly, number of slots has derived. Old: {}. Actual: {}.", + "dapp-staking-v3::migration::v8: New TiersConfiguration format not set correctly, number of slots has diverged. Old: {}. Actual: {}.", old_number_of_slots, actual_number_of_slots ); - assert!(actual_config.is_valid()); - + // 2. Ensure the provided static tier params are applied let actual_tier_params = StaticTierParams::::get(); assert!(actual_tier_params.is_valid()); - let expected_tier_thresholds: BoundedVec = - BoundedVec::try_from(TierThresholds::get().to_vec()).unwrap(); - let actual_tier_thresholds = actual_tier_params.tier_thresholds; - assert_eq!(expected_tier_thresholds, actual_tier_thresholds); + let expected_tier_thresholds: Result, _> = + BoundedVec::try_from(TierThresholds::get().to_vec()); + ensure!( + expected_tier_thresholds.is_ok(), + "dapp-staking-v3::migration::v8: Failed to convert expected tier thresholds." + ); + let actual_tier_thresholds = actual_tier_params.clone().tier_thresholds; + assert_eq!(expected_tier_thresholds.unwrap(), actual_tier_thresholds); + + // 3. Double check new threshold amounts allowing + let variation_percentage = ThresholdVariationPercentage::get(); + let total_issuance = T::Currency::total_issuance(); + let average_price = T::NativePriceProvider::average_price(); + + let old_threshold_amounts: Result, _> = + old_tier_thresholds + .iter() + .map(|t| t.threshold()) + .collect::>() + .try_into(); ensure!( - Pallet::::on_chain_storage_version() >= 8, - "dapp-staking-v3::migration::v8: Wrong storage version." + old_threshold_amounts.is_ok(), + "dapp-staking-v3::migration::v8: Failed to convert old v7 version tier thresholds to balance amounts." ); + let old_threshold_amounts = old_threshold_amounts.unwrap(); + let expected_new_threshold_amounts = actual_config + .calculate_new(&actual_tier_params, average_price, total_issuance) + .tier_thresholds; + + for (old_amount, actual_amount) in old_threshold_amounts + .iter() + .zip(expected_new_threshold_amounts) + { + let lower_bound = old_amount + .saturating_mul(100u32.saturating_sub(variation_percentage).into()) + .saturating_div(100u32.into()); + let upper_bound = old_amount + .saturating_mul(100u32.saturating_add(variation_percentage).into()) + .saturating_div(100u32.into()); + + assert!( + (lower_bound..=upper_bound).contains(&actual_amount), + "dapp-staking-v3::migration::v8: New tier threshold amounts diverged to much from old values, consider adjusting static tier parameters. Old: {}. Actual: {}.", + old_amount, + actual_amount + ); + } + Ok(()) } } } - /// Translate DAppTiers to include rank rewards. mod v7 { use super::*; @@ -214,6 +273,16 @@ mod v7 { }, } + impl TierThreshold { + /// Return threshold for the tier. + pub fn threshold(&self) -> Balance { + match self { + Self::FixedTvlAmount { amount } => *amount, + Self::DynamicTvlAmount { amount, .. } => *amount, + } + } + } + /// Top level description of tier slot parameters used to calculate tier configuration. #[derive(Encode, Decode)] pub struct TierParameters> { diff --git a/runtime/astar/src/lib.rs b/runtime/astar/src/lib.rs index 5d7136f82b..b64aad2671 100644 --- a/runtime/astar/src/lib.rs +++ b/runtime/astar/src/lib.rs @@ -1269,6 +1269,8 @@ pub type Executive = frame_executive::Executive< >; parameter_types! { + // Threshold amount variation allowed for this migration - 10% + pub const ThresholdVariationPercentage: u32 = 10; // percentages below are calulated based on total issuance at the time when dApp staking v3 was launched (8.4B) pub const TierThresholds: [TierThreshold; 4] = [ TierThreshold::DynamicPercentage { @@ -1299,7 +1301,11 @@ pub type Migrations = ( pallet_xc_asset_config::migrations::versioned::V2ToV3, pallet_identity::migration::versioned::V0ToV1, // dapp-staking dyn tier threshold migrations - pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8, + pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8< + Runtime, + TierThresholds, + ThresholdVariationPercentage, + >, ); type EventRecord = frame_system::EventRecord< diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index e28ea22f4e..2baa15bd27 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -1610,6 +1610,8 @@ pub type Executive = frame_executive::Executive< >; parameter_types! { + // Threshold amount variation allowed for this migration - 150% + pub const ThresholdVariationPercentage: u32 = 150; // percentages below are calulated based on a total issuance at the time when dApp staking v3 was launched (147M) pub const TierThresholds: [TierThreshold; 4] = [ TierThreshold::DynamicPercentage { @@ -1638,7 +1640,11 @@ pub type Migrations = ( // permanent migration, do not remove pallet_xcm::migration::MigrateToLatestXcmVersion, // dapp-staking dyn tier threshold migrations - pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8, + pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8< + Runtime, + TierThresholds, + ThresholdVariationPercentage, + >, ); type EventRecord = frame_system::EventRecord< diff --git a/runtime/shiden/src/lib.rs b/runtime/shiden/src/lib.rs index ef219c5a73..c73d8afa2b 100644 --- a/runtime/shiden/src/lib.rs +++ b/runtime/shiden/src/lib.rs @@ -1270,6 +1270,8 @@ pub type Executive = frame_executive::Executive< >; parameter_types! { + // Threshold amount variation allowed for this migration - 10% + pub const ThresholdVariationPercentage: u32 = 10; // percentages below are calulated based on a total issuance at the time when dApp staking v3 was launched (84.3M) pub const TierThresholds: [TierThreshold; 4] = [ TierThreshold::DynamicPercentage { @@ -1301,7 +1303,11 @@ pub type Migrations = ( pallet_xc_asset_config::migrations::versioned::V2ToV3, pallet_identity::migration::versioned::V0ToV1, // dapp-staking dyn tier threshold migrations - pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8, + pallet_dapp_staking_v3::migration::versioned_migrations::V7ToV8< + Runtime, + TierThresholds, + ThresholdVariationPercentage, + >, ); type EventRecord = frame_system::EventRecord<