From 2938f4c4b390dcd89ef8dff1453d19441a55a4cb Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 16 Jan 2024 16:50:13 +0100 Subject: [PATCH] WIP: (DNM) Oliver's migration fix for `pallet-nominations-pools` (fix from https://github.com/paritytech/polkadot-sdk/pull/2942) --- relay/kusama/src/lib.rs | 209 ++++++++++++++++++++++++++++++++++++- relay/polkadot/src/lib.rs | 210 +++++++++++++++++++++++++++++++++++++- 2 files changed, 413 insertions(+), 6 deletions(-) diff --git a/relay/kusama/src/lib.rs b/relay/kusama/src/lib.rs index 69115d14a8..327576296f 100644 --- a/relay/kusama/src/lib.rs +++ b/relay/kusama/src/lib.rs @@ -1685,11 +1685,15 @@ pub mod migrations { /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - pallet_nomination_pools::migration::versioned::V5toV6, - pallet_nomination_pools::migration::versioned::V6ToV7, pallet_staking::migrations::v14::MigrateToV14, parachains_configuration::migration::v10::MigrateToV10, - pallet_nomination_pools::migration::versioned::V7ToV8, + pallet_nomination_pools::migration::versioned::V5toV6, + // TODO:(PR#137) - replace with fixed/released version + crate::test_oliverfix_migration::V6ToV7, + // pallet_nomination_pools::migration::versioned::V6ToV7, + // TODO:(PR#137) - replace with fixed/released version + crate::test_oliverfix_migration::V7ToV8, + // pallet_nomination_pools::migration::versioned::V7ToV8, ); } @@ -2781,3 +2785,202 @@ mod init_state_migration { } } } + +// TODO:(PR#137) - replace with fixed/released version +mod test_oliverfix_migration { + use super::*; + use frame_support::{ + traits::OnRuntimeUpgrade, DebugNoBound, RuntimeDebugNoBound, Twox64Concat, + }; + use frame_system::pallet_prelude::BlockNumberFor; + use pallet_nomination_pools::*; + use sp_runtime::Saturating; + + pub type V7ToV8 = frame_support::migrations::VersionedMigration< + 7, + 8, + v8::VersionUncheckedMigrateV7ToV8, + pallet_nomination_pools::pallet::Pallet, + ::DbWeight, + >; + + pub type V6ToV7 = frame_support::migrations::VersionedMigration< + 6, + 7, + v7::VersionUncheckedMigrateV6ToV7, + pallet_nomination_pools::pallet::Pallet, + ::DbWeight, + >; + + pub mod v8 { + use super::*; + + use super::v7::BondedPoolInner as OldBondedPoolInner; + + impl OldBondedPoolInner { + fn migrate_to_v8(self) -> BondedPoolInner { + BondedPoolInner { + commission: Commission { + current: self.commission.current, + max: self.commission.max, + change_rate: self.commission.change_rate, + throttle_from: self.commission.throttle_from, + // `claim_permission` is a new field. + claim_permission: None, + }, + member_counter: self.member_counter, + points: self.points, + roles: self.roles, + state: self.state, + } + } + } + + pub struct VersionUncheckedMigrateV7ToV8(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for VersionUncheckedMigrateV7ToV8 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let mut translated = 0u64; + BondedPools::::translate::, _>(|_key, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v8()) + }); + T::DbWeight::get().reads_writes(translated, translated + 1) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + // Check new `claim_permission` field is present. + frame_support::ensure!( + BondedPools::::iter() + .all(|(_, inner)| inner.commission.claim_permission.is_none()), + "`claim_permission` value has not been set correctly." + ); + Ok(()) + } + } + } + + mod v7 { + use super::*; + + use sp_staking::StakingInterface; + // use frame_support::traits::GetStorageVersion; + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct Commission { + pub current: Option<(Perbill, T::AccountId)>, + pub max: Option, + pub change_rate: Option>>, + pub throttle_from: Option>, + } + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct BondedPoolInner { + pub commission: Commission, + pub member_counter: u32, + pub points: BalanceOf, + pub roles: PoolRoles, + pub state: PoolState, + } + + #[derive(RuntimeDebugNoBound)] + #[cfg_attr(feature = "std", derive(Clone, PartialEq))] + pub struct BondedPool { + /// The identifier of the pool. + id: PoolId, + /// The inner fields. + inner: BondedPoolInner, + } + + impl BondedPool { + fn bonded_account(&self) -> T::AccountId { + Pallet::::create_bonded_account(self.id) + } + } + + #[frame_support::storage_alias] + pub type BondedPools = + CountedStorageMap, Twox64Concat, PoolId, BondedPoolInner>; + + pub struct VersionUncheckedMigrateV6ToV7(sp_std::marker::PhantomData); + impl VersionUncheckedMigrateV6ToV7 { + fn calculate_tvl_by_total_stake() -> BalanceOf { + BondedPools::::iter() + .map(|(id, inner)| { + T::Staking::total_stake( + &BondedPool { id, inner: inner.clone() }.bonded_account(), + ) + .unwrap_or_default() + }) + .reduce(|acc, total_balance| acc + total_balance) + .unwrap_or_default() + } + } + + impl OnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7 { + fn on_runtime_upgrade() -> Weight { + let migrated = BondedPools::::count(); + // The TVL should be the sum of all the funds that are actively staked and in the + // unbonding process of the account of each pool. + let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + + TotalValueLocked::::set(tvl); + + log!(info, "Upgraded {} pools with a TVL of {:?}", migrated, tvl); + + // reads: migrated * (BondedPools + Staking::total_stake) + count + onchain + // version + // + // writes: current version + TVL + T::DbWeight::get() + .reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), 2) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_data: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + // check that the `TotalValueLocked` written is actually the sum of `total_stake` of + // the `BondedPools`` + let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + frame_support::ensure!( + TotalValueLocked::::get() == tvl, + "TVL written is not equal to `Staking::total_stake` of all `BondedPools`." + ); + + // TODO: skip for now + // calculate the sum of `total_balance` of all `PoolMember` as the upper bound for + // the `TotalValueLocked`. + // let total_balance_members: BalanceOf = PoolMembers::::iter() + // .map(|(_, member)| member.total_balance()) + // .reduce(|acc, total_balance| acc + total_balance) + // .unwrap_or_default(); + // + // frame_support::ensure!( + // TotalValueLocked::::get() <= total_balance_members, + // "TVL is greater than the balance of all PoolMembers." + // ); + // + // frame_support::ensure!( + // Pallet::::on_chain_storage_version() >= 7, + // "nomination-pools::migration::v7: wrong storage version" + // ); + + Ok(()) + } + } + } +} diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index 81fa3dc720..5898bfd7c3 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -1706,11 +1706,15 @@ pub mod migrations { pub type Unreleased = ( // Upgrade SessionKeys to include BEEFY key UpgradeSessionKeys, - pallet_nomination_pools::migration::versioned::V5toV6, - pallet_nomination_pools::migration::versioned::V6ToV7, pallet_staking::migrations::v14::MigrateToV14, parachains_configuration::migration::v10::MigrateToV10, - pallet_nomination_pools::migration::versioned::V7ToV8, + pallet_nomination_pools::migration::versioned::V5toV6, + // TODO:(PR#137) - replace with fixed/released version + crate::test_oliverfix_migration::V6ToV7, + // pallet_nomination_pools::migration::versioned::V6ToV7, + // TODO:(PR#137) - replace with fixed/released version + crate::test_oliverfix_migration::V7ToV8, + // pallet_nomination_pools::migration::versioned::V7ToV8, ); } @@ -2947,3 +2951,203 @@ mod remote_tests { }); } } + +// TODO:(PR#137) - replace with fixed/released version +mod test_oliverfix_migration { + use super::*; + use frame_support::{ + traits::OnRuntimeUpgrade, DebugNoBound, RuntimeDebugNoBound, Twox64Concat, + }; + use frame_system::pallet_prelude::BlockNumberFor; + use pallet_nomination_pools::*; + use sp_runtime::Saturating; + + pub type V7ToV8 = frame_support::migrations::VersionedMigration< + 7, + 8, + v8::VersionUncheckedMigrateV7ToV8, + pallet_nomination_pools::pallet::Pallet, + ::DbWeight, + >; + + pub type V6ToV7 = frame_support::migrations::VersionedMigration< + 6, + 7, + v7::VersionUncheckedMigrateV6ToV7, + pallet_nomination_pools::pallet::Pallet, + ::DbWeight, + >; + + pub mod v8 { + use super::*; + + use super::v7::BondedPoolInner as OldBondedPoolInner; + + impl OldBondedPoolInner { + fn migrate_to_v8(self) -> BondedPoolInner { + BondedPoolInner { + commission: Commission { + current: self.commission.current, + max: self.commission.max, + change_rate: self.commission.change_rate, + throttle_from: self.commission.throttle_from, + // `claim_permission` is a new field. + claim_permission: None, + }, + member_counter: self.member_counter, + points: self.points, + roles: self.roles, + state: self.state, + } + } + } + + pub struct VersionUncheckedMigrateV7ToV8(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for VersionUncheckedMigrateV7ToV8 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let mut translated = 0u64; + BondedPools::::translate::, _>(|_key, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v8()) + }); + T::DbWeight::get().reads_writes(translated, translated + 1) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + // Check new `claim_permission` field is present. + frame_support::ensure!( + BondedPools::::iter() + .all(|(_, inner)| inner.commission.claim_permission.is_none()), + "`claim_permission` value has not been set correctly." + ); + Ok(()) + } + } + } + + mod v7 { + use super::*; + + use scale_info::TypeInfo; + use sp_staking::StakingInterface; + // use frame_support::traits::GetStorageVersion; + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct Commission { + pub current: Option<(Perbill, T::AccountId)>, + pub max: Option, + pub change_rate: Option>>, + pub throttle_from: Option>, + } + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct BondedPoolInner { + pub commission: Commission, + pub member_counter: u32, + pub points: BalanceOf, + pub roles: PoolRoles, + pub state: PoolState, + } + + #[derive(RuntimeDebugNoBound)] + #[cfg_attr(feature = "std", derive(Clone, PartialEq))] + pub struct BondedPool { + /// The identifier of the pool. + id: PoolId, + /// The inner fields. + inner: BondedPoolInner, + } + + impl BondedPool { + fn bonded_account(&self) -> T::AccountId { + Pallet::::create_bonded_account(self.id) + } + } + + #[frame_support::storage_alias] + pub type BondedPools = + CountedStorageMap, Twox64Concat, PoolId, BondedPoolInner>; + + pub struct VersionUncheckedMigrateV6ToV7(sp_std::marker::PhantomData); + impl VersionUncheckedMigrateV6ToV7 { + fn calculate_tvl_by_total_stake() -> BalanceOf { + BondedPools::::iter() + .map(|(id, inner)| { + T::Staking::total_stake( + &BondedPool { id, inner: inner.clone() }.bonded_account(), + ) + .unwrap_or_default() + }) + .reduce(|acc, total_balance| acc + total_balance) + .unwrap_or_default() + } + } + + impl OnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7 { + fn on_runtime_upgrade() -> Weight { + let migrated = BondedPools::::count(); + // The TVL should be the sum of all the funds that are actively staked and in the + // unbonding process of the account of each pool. + let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + + TotalValueLocked::::set(tvl); + + log!(info, "Upgraded {} pools with a TVL of {:?}", migrated, tvl); + + // reads: migrated * (BondedPools + Staking::total_stake) + count + onchain + // version + // + // writes: current version + TVL + T::DbWeight::get() + .reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), 2) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_data: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + // check that the `TotalValueLocked` written is actually the sum of `total_stake` of + // the `BondedPools`` + let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + frame_support::ensure!( + TotalValueLocked::::get() == tvl, + "TVL written is not equal to `Staking::total_stake` of all `BondedPools`." + ); + + // TODO: skip for now + // calculate the sum of `total_balance` of all `PoolMember` as the upper bound for + // the `TotalValueLocked`. + // let total_balance_members: BalanceOf = PoolMembers::::iter() + // .map(|(_, member)| member.total_balance()) + // .reduce(|acc, total_balance| acc + total_balance) + // .unwrap_or_default(); + // + // frame_support::ensure!( + // TotalValueLocked::::get() <= total_balance_members, + // "TVL is greater than the balance of all PoolMembers." + // ); + // + // frame_support::ensure!( + // Pallet::::on_chain_storage_version() >= 7, + // "nomination-pools::migration::v7: wrong storage version" + // ); + + Ok(()) + } + } + } +}