// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::*; use crate::log; use frame_support::traits::OnRuntimeUpgrade; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; /// Exports for versioned migration `type`s for this pallet. pub mod versioned { use super::*; /// v8: Adds commission claim permissions to `BondedPools`. pub type V7ToV8 = frame_support::migrations::VersionedMigration< 7, 8, v8::VersionUncheckedMigrateV7ToV8, crate::pallet::Pallet, ::DbWeight, >; /// Migration V6 to V7 wrapped in a [`frame_support::migrations::VersionedMigration`], ensuring /// the migration is only performed when on-chain version is 6. pub type V6ToV7 = frame_support::migrations::VersionedMigration< 6, 7, v7::VersionUncheckedMigrateV6ToV7, crate::pallet::Pallet, ::DbWeight, >; /// Wrapper over `MigrateToV6` with convenience version checks. pub type V5toV6 = frame_support::migrations::VersionedMigration< 5, 6, v6::MigrateToV6, crate::pallet::Pallet, ::DbWeight, >; } pub mod unversioned { use super::*; /// Checks and updates `TotalValueLocked` if out of sync. pub struct TotalValueLockedSync(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for TotalValueLockedSync { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) } fn on_runtime_upgrade() -> Weight { let migrated = BondedPools::::count(); // recalcuate the `TotalValueLocked` to compare with the current on-chain TVL which may // be out of sync. let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); let onchain_tvl = TotalValueLocked::::get(); let writes = if tvl != onchain_tvl { TotalValueLocked::::set(tvl); log!( info, "on-chain TVL was out of sync, update. Old: {:?}, new: {:?}", onchain_tvl, tvl ); // writes: onchain version + set total value locked. 2 } else { log!(info, "on-chain TVL was OK: {:?}", tvl); // writes: onchain version write. 1 }; // reads: migrated * (BondedPools + Staking::total_stake) + count + onchain // version // // writes: current version + (maybe) TVL T::DbWeight::get() .reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), writes) } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { Ok(()) } } } pub mod v8 { use super::{v7::V7BondedPoolInner, *}; impl V7BondedPoolInner { 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, 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<(), TryRuntimeError> { // Check new `claim_permission` field is present. ensure!( BondedPools::::iter() .all(|(_, inner)| inner.commission.claim_permission.is_none()), "`claim_permission` value has not been set correctly." ); Ok(()) } } } /// This migration accumulates and initializes the [`TotalValueLocked`] for all pools. /// /// WARNING: This migration works under the assumption that the [`BondedPools`] cannot be inflated /// arbitrarily. Otherwise this migration could fail due to too high weight. pub(crate) mod v7 { use super::*; #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct V7Commission { 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 V7BondedPoolInner { pub commission: V7Commission, pub member_counter: u32, pub points: BalanceOf, pub roles: PoolRoles, pub state: PoolState, } #[allow(dead_code)] #[derive(RuntimeDebugNoBound)] #[cfg_attr(feature = "std", derive(Clone, PartialEq))] pub struct V7BondedPool { /// The identifier of the pool. id: PoolId, /// The inner fields. inner: V7BondedPoolInner, } impl V7BondedPool { #[allow(dead_code)] fn bonded_account(&self) -> T::AccountId { Pallet::::create_bonded_account(self.id) } } // NOTE: We cannot put a V7 prefix here since that would change the storage key. #[frame_support::storage_alias] pub type BondedPools = CountedStorageMap, Twox64Concat, PoolId, V7BondedPoolInner>; pub struct VersionUncheckedMigrateV6ToV7(sp_std::marker::PhantomData); 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 = helpers::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, TryRuntimeError> { Ok(Vec::new()) } #[cfg(feature = "try-runtime")] fn post_upgrade(_data: Vec) -> Result<(), TryRuntimeError> { // check that the `TotalValueLocked` written is actually the sum of `total_stake` of the // `BondedPools`` let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); ensure!( TotalValueLocked::::get() == tvl, "TVL written is not equal to `Staking::total_stake` of all `BondedPools`." ); // 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(); ensure!( TotalValueLocked::::get() <= total_balance_members, "TVL is greater than the balance of all PoolMembers." ); ensure!( Pallet::::on_chain_storage_version() >= 7, "nomination-pools::migration::v7: wrong storage version" ); Ok(()) } } } mod v6 { use super::*; /// This migration would restrict reward account of pools to go below ED by doing a named /// freeze on all the existing pools. pub struct MigrateToV6(sp_std::marker::PhantomData); impl MigrateToV6 { fn freeze_ed(pool_id: PoolId) -> Result<(), ()> { let reward_acc = Pallet::::create_reward_account(pool_id); Pallet::::freeze_pool_deposit(&reward_acc).map_err(|e| { log!(error, "Failed to freeze ED for pool {} with error: {:?}", pool_id, e); () }) } } impl OnRuntimeUpgrade for MigrateToV6 { fn on_runtime_upgrade() -> Weight { let mut success = 0u64; let mut fail = 0u64; BondedPools::::iter_keys().for_each(|p| { if Self::freeze_ed(p).is_ok() { success.saturating_inc(); } else { fail.saturating_inc(); } }); if fail > 0 { log!(error, "Failed to freeze ED for {} pools", fail); } else { log!(info, "Freezing ED succeeded for {} pools", success); } let total = success.saturating_add(fail); // freeze_ed = r:2 w:2 // reads: (freeze_ed + bonded pool key) * total // writes: freeze_ed * total T::DbWeight::get().reads_writes(3u64.saturating_mul(total), 2u64.saturating_mul(total)) } #[cfg(feature = "try-runtime")] fn post_upgrade(_data: Vec) -> Result<(), TryRuntimeError> { // there should be no ED imbalances anymore.. Pallet::::check_ed_imbalance() } } } pub mod v5 { use super::*; #[derive(Decode)] pub struct OldRewardPool { last_recorded_reward_counter: T::RewardCounter, last_recorded_total_payouts: BalanceOf, total_rewards_claimed: BalanceOf, } impl OldRewardPool { fn migrate_to_v5(self) -> RewardPool { RewardPool { last_recorded_reward_counter: self.last_recorded_reward_counter, last_recorded_total_payouts: self.last_recorded_total_payouts, total_rewards_claimed: self.total_rewards_claimed, total_commission_pending: Zero::zero(), total_commission_claimed: Zero::zero(), } } } /// This migration adds `total_commission_pending` and `total_commission_claimed` field to every /// `RewardPool`, if any. pub struct MigrateToV5(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV5 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, "Running migration with current storage version {:?} / onchain {:?}", current, onchain ); if current == 5 && onchain == 4 { let mut translated = 0u64; RewardPools::::translate::, _>(|_id, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v5()) }); current.put::>(); log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); // reads: translated + onchain version. // writes: translated + current.put. T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { log!(info, "Migration did not execute. This probably should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { let rpool_keys = RewardPools::::iter_keys().count(); let rpool_values = RewardPools::::iter_values().count(); if rpool_keys != rpool_values { log!(info, "🔥 There are {} undecodable RewardPools in storage. This migration will try to correct them. keys: {}, values: {}", rpool_keys.saturating_sub(rpool_values), rpool_keys, rpool_values); } ensure!( PoolMembers::::iter_keys().count() == PoolMembers::::iter_values().count(), "There are undecodable PoolMembers in storage. This migration will not fix that." ); ensure!( BondedPools::::iter_keys().count() == BondedPools::::iter_values().count(), "There are undecodable BondedPools in storage. This migration will not fix that." ); ensure!( SubPoolsStorage::::iter_keys().count() == SubPoolsStorage::::iter_values().count(), "There are undecodable SubPools in storage. This migration will not fix that." ); ensure!( Metadata::::iter_keys().count() == Metadata::::iter_values().count(), "There are undecodable Metadata in storage. This migration will not fix that." ); Ok((rpool_values as u64).encode()) } #[cfg(feature = "try-runtime")] fn post_upgrade(data: Vec) -> Result<(), TryRuntimeError> { let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap(); let rpool_keys = RewardPools::::iter_keys().count() as u64; let rpool_values = RewardPools::::iter_values().count() as u64; ensure!( rpool_keys == rpool_values, "There are STILL undecodable RewardPools - migration failed" ); if old_rpool_values != rpool_values { log!( info, "🎉 Fixed {} undecodable RewardPools.", rpool_values.saturating_sub(old_rpool_values) ); } // ensure all RewardPools items now contain `total_commission_pending` and // `total_commission_claimed` field. ensure!( RewardPools::::iter().all(|(_, reward_pool)| reward_pool .total_commission_pending >= Zero::zero() && reward_pool .total_commission_claimed >= Zero::zero()), "a commission value has been incorrectly set" ); ensure!( Pallet::::on_chain_storage_version() >= 5, "nomination-pools::migration::v5: wrong storage version" ); // These should not have been touched - just in case. ensure!( PoolMembers::::iter_keys().count() == PoolMembers::::iter_values().count(), "There are undecodable PoolMembers in storage." ); ensure!( BondedPools::::iter_keys().count() == BondedPools::::iter_values().count(), "There are undecodable BondedPools in storage." ); ensure!( SubPoolsStorage::::iter_keys().count() == SubPoolsStorage::::iter_values().count(), "There are undecodable SubPools in storage." ); ensure!( Metadata::::iter_keys().count() == Metadata::::iter_values().count(), "There are undecodable Metadata in storage." ); Ok(()) } } } pub mod v4 { use super::*; #[derive(Decode)] pub struct OldBondedPoolInner { pub points: BalanceOf, pub state: PoolState, pub member_counter: u32, pub roles: PoolRoles, } impl OldBondedPoolInner { fn migrate_to_v4(self) -> BondedPoolInner { BondedPoolInner { commission: Commission::default(), member_counter: self.member_counter, points: self.points, state: self.state, roles: self.roles, } } } /// Migrates from `v3` directly to `v5` to avoid the broken `v4` migration. #[allow(deprecated)] pub type MigrateV3ToV5 = (v4::MigrateToV4, v5::MigrateToV5); /// # Warning /// /// To avoid mangled storage please use `MigrateV3ToV5` instead. /// See: github.com/paritytech/substrate/pull/13715 /// /// This migration adds a `commission` field to every `BondedPoolInner`, if /// any. #[deprecated( note = "To avoid mangled storage please use `MigrateV3ToV5` instead. See: github.com/paritytech/substrate/pull/13715" )] pub struct MigrateToV4(sp_std::marker::PhantomData<(T, U)>); #[allow(deprecated)] impl> OnRuntimeUpgrade for MigrateToV4 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, "Running migration with current storage version {:?} / onchain {:?}", current, onchain ); if onchain == 3 { log!(warn, "Please run MigrateToV5 immediately after this migration. See github.com/paritytech/substrate/pull/13715"); let initial_global_max_commission = U::get(); GlobalMaxCommission::::set(Some(initial_global_max_commission)); log!( info, "Set initial global max commission to {:?}.", initial_global_max_commission ); let mut translated = 0u64; BondedPools::::translate::, _>(|_key, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v4()) }); StorageVersion::new(4).put::>(); log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); // reads: translated + onchain version. // writes: translated + current.put + initial global commission. T::DbWeight::get().reads_writes(translated + 1, translated + 2) } else { log!(info, "Migration did not execute. This probably should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( BondedPools::::iter().all(|(_, inner)| // Check current (inner.commission.current.is_none() || inner.commission.current.is_some()) && // Check max (inner.commission.max.is_none() || inner.commission.max.is_some()) && // Check change_rate (inner.commission.change_rate.is_none() || inner.commission.change_rate.is_some()) && // Check throttle_from (inner.commission.throttle_from.is_none() || inner.commission.throttle_from.is_some())), "a commission value has not been set correctly" ); ensure!( GlobalMaxCommission::::get() == Some(U::get()), "global maximum commission error" ); ensure!( Pallet::::on_chain_storage_version() >= 4, "nomination-pools::migration::v4: wrong storage version" ); Ok(()) } } } pub mod v3 { use super::*; /// This migration removes stale bonded-pool metadata, if any. pub struct MigrateToV3(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV3 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); if onchain == 2 { log!( info, "Running migration with current storage version {:?} / onchain {:?}", current, onchain ); let mut metadata_iterated = 0u64; let mut metadata_removed = 0u64; Metadata::::iter_keys() .filter(|id| { metadata_iterated += 1; !BondedPools::::contains_key(&id) }) .collect::>() .into_iter() .for_each(|id| { metadata_removed += 1; Metadata::::remove(&id); }); StorageVersion::new(3).put::>(); // metadata iterated + bonded pools read + a storage version read let total_reads = metadata_iterated * 2 + 1; // metadata removed + a storage version write let total_writes = metadata_removed + 1; T::DbWeight::get().reads_writes(total_reads, total_writes) } else { log!(info, "MigrateToV3 should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { ensure!( Metadata::::iter_keys().all(|id| BondedPools::::contains_key(&id)), "not all of the stale metadata has been removed" ); ensure!( Pallet::::on_chain_storage_version() >= 3, "nomination-pools::migration::v3: wrong storage version" ); Ok(()) } } } pub mod v2 { use super::*; use sp_runtime::Perbill; #[test] fn migration_assumption_is_correct() { // this migrations cleans all the reward accounts to contain exactly ed, and all members // having no claimable rewards. In this state, all fields of the `RewardPool` and // `member.last_recorded_reward_counter` are all zero. use crate::mock::*; ExtBuilder::default().build_and_execute(|| { let join = |x| { Currency::set_balance(&x, Balances::minimum_balance() + 10); frame_support::assert_ok!(Pools::join(RuntimeOrigin::signed(x), 10, 1)); }; assert_eq!(BondedPool::::get(1).unwrap().points, 10); assert_eq!( RewardPools::::get(1).unwrap(), RewardPool { ..Default::default() } ); assert_eq!( PoolMembers::::get(10).unwrap().last_recorded_reward_counter, Zero::zero() ); join(20); assert_eq!(BondedPool::::get(1).unwrap().points, 20); assert_eq!( RewardPools::::get(1).unwrap(), RewardPool { ..Default::default() } ); assert_eq!( PoolMembers::::get(10).unwrap().last_recorded_reward_counter, Zero::zero() ); assert_eq!( PoolMembers::::get(20).unwrap().last_recorded_reward_counter, Zero::zero() ); join(30); assert_eq!(BondedPool::::get(1).unwrap().points, 30); assert_eq!( RewardPools::::get(1).unwrap(), RewardPool { ..Default::default() } ); assert_eq!( PoolMembers::::get(10).unwrap().last_recorded_reward_counter, Zero::zero() ); assert_eq!( PoolMembers::::get(20).unwrap().last_recorded_reward_counter, Zero::zero() ); assert_eq!( PoolMembers::::get(30).unwrap().last_recorded_reward_counter, Zero::zero() ); }); } #[derive(Decode)] pub struct OldRewardPool { pub balance: B, pub total_earnings: B, pub points: U256, } #[derive(Decode)] pub struct OldPoolMember { pub pool_id: PoolId, pub points: BalanceOf, pub reward_pool_total_earnings: BalanceOf, pub unbonding_eras: BoundedBTreeMap, T::MaxUnbonding>, } /// Migrate the pool reward scheme to the new version, as per /// . pub struct MigrateToV2(sp_std::marker::PhantomData); impl MigrateToV2 { fn run(current: StorageVersion) -> Weight { let mut reward_pools_translated = 0u64; let mut members_translated = 0u64; // just for logging. let mut total_value_locked = BalanceOf::::zero(); let mut total_points_locked = BalanceOf::::zero(); // store each member of the pool, with their active points. In the process, migrate // their data as well. let mut temp_members = BTreeMap::)>>::new(); PoolMembers::::translate::, _>(|key, old_member| { let id = old_member.pool_id; temp_members.entry(id).or_default().push((key, old_member.points)); total_points_locked += old_member.points; members_translated += 1; Some(PoolMember:: { last_recorded_reward_counter: Zero::zero(), pool_id: old_member.pool_id, points: old_member.points, unbonding_eras: old_member.unbonding_eras, }) }); // translate all reward pools. In the process, do the last payout as well. RewardPools::::translate::>, _>( |id, _old_reward_pool| { // each pool should have at least one member. let members = match temp_members.get(&id) { Some(x) => x, None => { log!(error, "pool {} has no member! deleting it..", id); return None }, }; let bonded_pool = match BondedPools::::get(id) { Some(x) => x, None => { log!(error, "pool {} has no bonded pool! deleting it..", id); return None }, }; let accumulated_reward = RewardPool::::current_balance(id); let reward_account = Pallet::::create_reward_account(id); let mut sum_paid_out = BalanceOf::::zero(); members .into_iter() .filter_map(|(who, points)| { let bonded_pool = match BondedPool::::get(id) { Some(x) => x, None => { log!(error, "pool {} for member {:?} does not exist!", id, who); return None }, }; total_value_locked += bonded_pool.points_to_balance(*points); let portion = Perbill::from_rational(*points, bonded_pool.points); let last_claim = portion * accumulated_reward; log!( debug, "{:?} has {:?} ({:?}) of pool {} with total reward of {:?}", who, portion, last_claim, id, accumulated_reward ); if last_claim.is_zero() { None } else { Some((who, last_claim)) } }) .for_each(|(who, last_claim)| { let outcome = T::Currency::transfer( &reward_account, &who, last_claim, Preservation::Preserve, ); if let Err(reason) = outcome { log!(warn, "last reward claim failed due to {:?}", reason,); } else { sum_paid_out = sum_paid_out.saturating_add(last_claim); } Pallet::::deposit_event(Event::::PaidOut { member: who.clone(), pool_id: id, payout: last_claim, }); }); // this can only be because of rounding down, or because the person we // wanted to pay their reward to could not accept it (dust). let leftover = accumulated_reward.saturating_sub(sum_paid_out); if !leftover.is_zero() { // pay it all to depositor. let o = T::Currency::transfer( &reward_account, &bonded_pool.roles.depositor, leftover, Preservation::Preserve, ); log!(warn, "paying {:?} leftover to the depositor: {:?}", leftover, o); } // finally, migrate the reward pool. reward_pools_translated += 1; Some(RewardPool { last_recorded_reward_counter: Zero::zero(), last_recorded_total_payouts: Zero::zero(), total_rewards_claimed: Zero::zero(), total_commission_claimed: Zero::zero(), total_commission_pending: Zero::zero(), }) }, ); log!( info, "Upgraded {} members, {} reward pools, TVL {:?} TPL {:?}, storage to version {:?}", members_translated, reward_pools_translated, total_value_locked, total_points_locked, current ); current.put::>(); T::DbWeight::get().reads_writes(members_translated + 1, reward_pools_translated + 1) } } impl OnRuntimeUpgrade for MigrateToV2 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, "Running migration with current storage version {:?} / onchain {:?}", current, onchain ); if current == 2 && onchain == 1 { Self::run(current) } else { log!(info, "MigrateToV2 did not executed. This probably should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { // all reward accounts must have more than ED. RewardPools::::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> { ensure!( >::balance(&Pallet::::create_reward_account(id)) >= T::Currency::minimum_balance(), "Reward accounts must have greater balance than ED." ); Ok(()) })?; Ok(Vec::new()) } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // new version must be set. ensure!( Pallet::::on_chain_storage_version() == 2, "The onchain version must be updated after the migration." ); // no reward or bonded pool has been skipped. ensure!( RewardPools::::iter().count() as u32 == RewardPools::::count(), "The count of reward pools must remain the same after the migration." ); ensure!( BondedPools::::iter().count() as u32 == BondedPools::::count(), "The count of reward pools must remain the same after the migration." ); // all reward pools must have exactly ED in them. This means no reward can be claimed, // and that setting reward counters all over the board to zero will work henceforth. RewardPools::::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> { ensure!( RewardPool::::current_balance(id) == Zero::zero(), "Reward pool balance must be zero.", ); Ok(()) })?; log!(info, "post upgrade hook for MigrateToV2 executed."); Ok(()) } } } pub mod v1 { use super::*; #[derive(Decode)] pub struct OldPoolRoles { pub depositor: AccountId, pub root: AccountId, pub nominator: AccountId, pub bouncer: AccountId, } impl OldPoolRoles { fn migrate_to_v1(self) -> PoolRoles { PoolRoles { depositor: self.depositor, root: Some(self.root), nominator: Some(self.nominator), bouncer: Some(self.bouncer), } } } #[derive(Decode)] pub struct OldBondedPoolInner { pub points: BalanceOf, pub state: PoolState, pub member_counter: u32, pub roles: OldPoolRoles, } impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { // Note: `commission` field not introduced to `BondedPoolInner` until // migration 4. BondedPoolInner { points: self.points, commission: Commission::default(), member_counter: self.member_counter, state: self.state, roles: self.roles.migrate_to_v1(), } } } /// Trivial migration which makes the roles of each pool optional. /// /// Note: The depositor is not optional since they can never change. pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, "Running migration with current storage version {:?} / onchain {:?}", current, onchain ); if current == 1 && onchain == 0 { // this is safe to execute on any runtime that has a bounded number of pools. let mut translated = 0u64; BondedPools::::translate::, _>(|_key, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v1()) }); current.put::>(); log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { log!(info, "Migration did not executed. This probably should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { // new version must be set. ensure!( Pallet::::on_chain_storage_version() == 1, "The onchain version must be updated after the migration." ); Pallet::::try_state(frame_system::Pallet::::block_number())?; Ok(()) } } } mod helpers { use super::*; pub(crate) 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() } }