mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 21:01:05 +00:00
[NPoS] Fix for Reward Deficit in the pool (#1255)
closes https://github.com/paritytech/polkadot-sdk/issues/158. partially addresses https://github.com/paritytech/polkadot-sdk/issues/226. Instead of fragile calculation of current balance by looking at `free balance - ED`, Nomination Pool now freezes ED in the pool reward account to restrict an account from going below minimum balance. This also has a nice side effect that if ED changes, we know how much is the imbalance in ED frozen in the pool and the current required ED. A pool operator can diligently top up the pool with the deficit in ED or vice versa, withdraw the excess they transferred to the pool. ## Notable changes - New call `adjust_pool_deposit`: Allows to top up the deficit or withdraw the excess deposited funds to the pool. - Uses Fungible trait (instead of Currency trait). Since NP was not doing any locking/reserving previously, no migration is needed for this. - One time migration of freezing ED from each of the existing pools (not very PoV friendly but fine for relay chain).
This commit is contained in:
@@ -23,55 +23,95 @@ use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
|
||||
#[cfg(feature = "try-runtime")]
|
||||
use sp_runtime::TryRuntimeError;
|
||||
|
||||
pub mod v1 {
|
||||
/// Exports for versioned migration `type`s for this pallet.
|
||||
pub mod versioned_migrations {
|
||||
use super::*;
|
||||
|
||||
/// Wrapper over `MigrateToV6` with convenience version checks.
|
||||
pub type V5toV6<T> = frame_support::migrations::VersionedMigration<
|
||||
5,
|
||||
6,
|
||||
v6::MigrateToV6<T>,
|
||||
crate::pallet::Pallet<T>,
|
||||
<T as frame_system::Config>::DbWeight,
|
||||
>;
|
||||
}
|
||||
|
||||
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<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Config> MigrateToV6<T> {
|
||||
fn freeze_ed(pool_id: PoolId) -> Result<(), ()> {
|
||||
let reward_acc = Pallet::<T>::create_reward_account(pool_id);
|
||||
Pallet::<T>::freeze_pool_deposit(&reward_acc).map_err(|e| {
|
||||
log!(error, "Failed to freeze ED for pool {} with error: {:?}", pool_id, e);
|
||||
()
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV6<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let mut success = 0u64;
|
||||
let mut fail = 0u64;
|
||||
|
||||
BondedPools::<T>::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<u8>) -> Result<(), TryRuntimeError> {
|
||||
// there should be no ED imbalances anymore..
|
||||
Pallet::<T>::check_ed_imbalance()
|
||||
}
|
||||
}
|
||||
}
|
||||
pub mod v5 {
|
||||
use super::*;
|
||||
|
||||
#[derive(Decode)]
|
||||
pub struct OldPoolRoles<AccountId> {
|
||||
pub depositor: AccountId,
|
||||
pub root: AccountId,
|
||||
pub nominator: AccountId,
|
||||
pub bouncer: AccountId,
|
||||
pub struct OldRewardPool<T: Config> {
|
||||
last_recorded_reward_counter: T::RewardCounter,
|
||||
last_recorded_total_payouts: BalanceOf<T>,
|
||||
total_rewards_claimed: BalanceOf<T>,
|
||||
}
|
||||
|
||||
impl<AccountId> OldPoolRoles<AccountId> {
|
||||
fn migrate_to_v1(self) -> PoolRoles<AccountId> {
|
||||
PoolRoles {
|
||||
depositor: self.depositor,
|
||||
root: Some(self.root),
|
||||
nominator: Some(self.nominator),
|
||||
bouncer: Some(self.bouncer),
|
||||
impl<T: Config> OldRewardPool<T> {
|
||||
fn migrate_to_v5(self) -> RewardPool<T> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Decode)]
|
||||
pub struct OldBondedPoolInner<T: Config> {
|
||||
pub points: BalanceOf<T>,
|
||||
pub state: PoolState,
|
||||
pub member_counter: u32,
|
||||
pub roles: OldPoolRoles<T::AccountId>,
|
||||
}
|
||||
|
||||
impl<T: Config> OldBondedPoolInner<T> {
|
||||
fn migrate_to_v1(self) -> BondedPoolInner<T> {
|
||||
// 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<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
|
||||
/// This migration adds `total_commission_pending` and `total_commission_claimed` field to every
|
||||
/// `RewardPool`, if any.
|
||||
pub struct MigrateToV5<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV5<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::on_chain_storage_version();
|
||||
@@ -83,33 +123,284 @@ pub mod v1 {
|
||||
onchain
|
||||
);
|
||||
|
||||
if current == 1 && onchain == 0 {
|
||||
// this is safe to execute on any runtime that has a bounded number of pools.
|
||||
if current == 5 && onchain == 4 {
|
||||
let mut translated = 0u64;
|
||||
BondedPools::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
|
||||
RewardPools::<T>::translate::<OldRewardPool<T>, _>(|_id, old_value| {
|
||||
translated.saturating_inc();
|
||||
Some(old_value.migrate_to_v1())
|
||||
Some(old_value.migrate_to_v5())
|
||||
});
|
||||
|
||||
current.put::<Pallet<T>>();
|
||||
|
||||
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 executed. This probably should be removed");
|
||||
log!(info, "Migration did not execute. This probably should be removed");
|
||||
T::DbWeight::get().reads(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
// new version must be set.
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
let rpool_keys = RewardPools::<T>::iter_keys().count();
|
||||
let rpool_values = RewardPools::<T>::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!(
|
||||
Pallet::<T>::on_chain_storage_version() == 1,
|
||||
"The onchain version must be updated after the migration."
|
||||
PoolMembers::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
||||
"There are undecodable PoolMembers in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
||||
"There are undecodable BondedPools in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
SubPoolsStorage::<T>::iter_keys().count() ==
|
||||
SubPoolsStorage::<T>::iter_values().count(),
|
||||
"There are undecodable SubPools in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().count() == Metadata::<T>::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<u8>) -> Result<(), TryRuntimeError> {
|
||||
let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap();
|
||||
let rpool_keys = RewardPools::<T>::iter_keys().count() as u64;
|
||||
let rpool_values = RewardPools::<T>::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::<T>::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::<T>::on_chain_storage_version() >= 5,
|
||||
"nomination-pools::migration::v5: wrong storage version"
|
||||
);
|
||||
|
||||
// These should not have been touched - just in case.
|
||||
ensure!(
|
||||
PoolMembers::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
||||
"There are undecodable PoolMembers in storage."
|
||||
);
|
||||
ensure!(
|
||||
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
||||
"There are undecodable BondedPools in storage."
|
||||
);
|
||||
ensure!(
|
||||
SubPoolsStorage::<T>::iter_keys().count() ==
|
||||
SubPoolsStorage::<T>::iter_values().count(),
|
||||
"There are undecodable SubPools in storage."
|
||||
);
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().count() == Metadata::<T>::iter_values().count(),
|
||||
"There are undecodable Metadata in storage."
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v4 {
|
||||
use super::*;
|
||||
|
||||
#[derive(Decode)]
|
||||
pub struct OldBondedPoolInner<T: Config> {
|
||||
pub points: BalanceOf<T>,
|
||||
pub state: PoolState,
|
||||
pub member_counter: u32,
|
||||
pub roles: PoolRoles<T::AccountId>,
|
||||
}
|
||||
|
||||
impl<T: Config> OldBondedPoolInner<T> {
|
||||
fn migrate_to_v4(self) -> BondedPoolInner<T> {
|
||||
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<T, U> = (v4::MigrateToV4<T, U>, v5::MigrateToV5<T>);
|
||||
|
||||
/// # 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<T, U>(sp_std::marker::PhantomData<(T, U)>);
|
||||
#[allow(deprecated)]
|
||||
impl<T: Config, U: Get<Perbill>> OnRuntimeUpgrade for MigrateToV4<T, U> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::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::<T>::set(Some(initial_global_max_commission));
|
||||
log!(
|
||||
info,
|
||||
"Set initial global max commission to {:?}.",
|
||||
initial_global_max_commission
|
||||
);
|
||||
|
||||
let mut translated = 0u64;
|
||||
BondedPools::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
|
||||
translated.saturating_inc();
|
||||
Some(old_value.migrate_to_v4())
|
||||
});
|
||||
|
||||
StorageVersion::new(4).put::<Pallet<T>>();
|
||||
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<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
// ensure all BondedPools items now contain an `inner.commission: Commission` field.
|
||||
ensure!(
|
||||
BondedPools::<T>::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::<T>::get() == Some(U::get()),
|
||||
"global maximum commission error"
|
||||
);
|
||||
ensure!(
|
||||
Pallet::<T>::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<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV3<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::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::<T>::iter_keys()
|
||||
.filter(|id| {
|
||||
metadata_iterated += 1;
|
||||
!BondedPools::<T>::contains_key(&id)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.for_each(|id| {
|
||||
metadata_removed += 1;
|
||||
Metadata::<T>::remove(&id);
|
||||
});
|
||||
StorageVersion::new(3).put::<Pallet<T>>();
|
||||
// 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<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().all(|id| BondedPools::<T>::contains_key(&id)),
|
||||
"not all of the stale metadata has been removed"
|
||||
);
|
||||
ensure!(
|
||||
Pallet::<T>::on_chain_storage_version() >= 3,
|
||||
"nomination-pools::migration::v3: wrong storage version"
|
||||
);
|
||||
Pallet::<T>::try_state(frame_system::Pallet::<T>::block_number())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -127,7 +418,7 @@ pub mod v2 {
|
||||
use crate::mock::*;
|
||||
ExtBuilder::default().build_and_execute(|| {
|
||||
let join = |x| {
|
||||
Balances::make_free_balance_be(&x, Balances::minimum_balance() + 10);
|
||||
Currency::set_balance(&x, Balances::minimum_balance() + 10);
|
||||
frame_support::assert_ok!(Pools::join(RuntimeOrigin::signed(x), 10, 1));
|
||||
};
|
||||
|
||||
@@ -279,7 +570,7 @@ pub mod v2 {
|
||||
&reward_account,
|
||||
&who,
|
||||
last_claim,
|
||||
ExistenceRequirement::KeepAlive,
|
||||
Preservation::Preserve,
|
||||
);
|
||||
|
||||
if let Err(reason) = outcome {
|
||||
@@ -304,7 +595,7 @@ pub mod v2 {
|
||||
&reward_account,
|
||||
&bonded_pool.roles.depositor,
|
||||
leftover,
|
||||
ExistenceRequirement::KeepAlive,
|
||||
Preservation::Preserve,
|
||||
);
|
||||
log!(warn, "paying {:?} leftover to the depositor: {:?}", leftover, o);
|
||||
}
|
||||
@@ -362,7 +653,7 @@ pub mod v2 {
|
||||
// all reward accounts must have more than ED.
|
||||
RewardPools::<T>::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> {
|
||||
ensure!(
|
||||
T::Currency::free_balance(&Pallet::<T>::create_reward_account(id)) >=
|
||||
<T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(&Pallet::<T>::create_reward_account(id)) >=
|
||||
T::Currency::minimum_balance(),
|
||||
"Reward accounts must have greater balance than ED."
|
||||
);
|
||||
@@ -406,109 +697,55 @@ pub mod v2 {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v3 {
|
||||
pub mod v1 {
|
||||
use super::*;
|
||||
|
||||
/// This migration removes stale bonded-pool metadata, if any.
|
||||
pub struct MigrateToV3<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV3<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::on_chain_storage_version();
|
||||
#[derive(Decode)]
|
||||
pub struct OldPoolRoles<AccountId> {
|
||||
pub depositor: AccountId,
|
||||
pub root: AccountId,
|
||||
pub nominator: AccountId,
|
||||
pub bouncer: AccountId,
|
||||
}
|
||||
|
||||
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::<T>::iter_keys()
|
||||
.filter(|id| {
|
||||
metadata_iterated += 1;
|
||||
!BondedPools::<T>::contains_key(&id)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.for_each(|id| {
|
||||
metadata_removed += 1;
|
||||
Metadata::<T>::remove(&id);
|
||||
});
|
||||
StorageVersion::new(3).put::<Pallet<T>>();
|
||||
// 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)
|
||||
impl<AccountId> OldPoolRoles<AccountId> {
|
||||
fn migrate_to_v1(self) -> PoolRoles<AccountId> {
|
||||
PoolRoles {
|
||||
depositor: self.depositor,
|
||||
root: Some(self.root),
|
||||
nominator: Some(self.nominator),
|
||||
bouncer: Some(self.bouncer),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().all(|id| BondedPools::<T>::contains_key(&id)),
|
||||
"not all of the stale metadata has been removed"
|
||||
);
|
||||
ensure!(
|
||||
Pallet::<T>::on_chain_storage_version() >= 3,
|
||||
"nomination-pools::migration::v3: wrong storage version"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v4 {
|
||||
use super::*;
|
||||
|
||||
#[derive(Decode)]
|
||||
pub struct OldBondedPoolInner<T: Config> {
|
||||
pub points: BalanceOf<T>,
|
||||
pub state: PoolState,
|
||||
pub member_counter: u32,
|
||||
pub roles: PoolRoles<T::AccountId>,
|
||||
pub roles: OldPoolRoles<T::AccountId>,
|
||||
}
|
||||
|
||||
impl<T: Config> OldBondedPoolInner<T> {
|
||||
fn migrate_to_v4(self) -> BondedPoolInner<T> {
|
||||
fn migrate_to_v1(self) -> BondedPoolInner<T> {
|
||||
// Note: `commission` field not introduced to `BondedPoolInner` until
|
||||
// migration 4.
|
||||
BondedPoolInner {
|
||||
points: self.points,
|
||||
commission: Commission::default(),
|
||||
member_counter: self.member_counter,
|
||||
points: self.points,
|
||||
state: self.state,
|
||||
roles: self.roles,
|
||||
roles: self.roles.migrate_to_v1(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Migrates from `v3` directly to `v5` to avoid the broken `v4` migration.
|
||||
#[allow(deprecated)]
|
||||
pub type MigrateV3ToV5<T, U> = (v4::MigrateToV4<T, U>, v5::MigrateToV5<T>);
|
||||
|
||||
/// # Warning
|
||||
/// Trivial migration which makes the roles of each pool optional.
|
||||
///
|
||||
/// 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<T, U>(sp_std::marker::PhantomData<(T, U)>);
|
||||
#[allow(deprecated)]
|
||||
impl<T: Config, U: Get<Perbill>> OnRuntimeUpgrade for MigrateToV4<T, U> {
|
||||
/// Note: The depositor is not optional since they can never change.
|
||||
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::on_chain_storage_version();
|
||||
@@ -520,207 +757,33 @@ pub mod v4 {
|
||||
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::<T>::set(Some(initial_global_max_commission));
|
||||
log!(
|
||||
info,
|
||||
"Set initial global max commission to {:?}.",
|
||||
initial_global_max_commission
|
||||
);
|
||||
|
||||
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::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
|
||||
translated.saturating_inc();
|
||||
Some(old_value.migrate_to_v4())
|
||||
Some(old_value.migrate_to_v1())
|
||||
});
|
||||
|
||||
StorageVersion::new(4).put::<Pallet<T>>();
|
||||
current.put::<Pallet<T>>();
|
||||
|
||||
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)
|
||||
T::DbWeight::get().reads_writes(translated + 1, translated + 1)
|
||||
} else {
|
||||
log!(info, "Migration did not execute. This probably should be removed");
|
||||
log!(info, "Migration did not executed. This probably should be removed");
|
||||
T::DbWeight::get().reads(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
// ensure all BondedPools items now contain an `inner.commission: Commission` field.
|
||||
// new version must be set.
|
||||
ensure!(
|
||||
BondedPools::<T>::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::<T>::get() == Some(U::get()),
|
||||
"global maximum commission error"
|
||||
);
|
||||
ensure!(
|
||||
Pallet::<T>::on_chain_storage_version() >= 4,
|
||||
"nomination-pools::migration::v4: wrong storage version"
|
||||
Pallet::<T>::on_chain_storage_version() == 1,
|
||||
"The onchain version must be updated after the migration."
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v5 {
|
||||
use super::*;
|
||||
|
||||
#[derive(Decode)]
|
||||
pub struct OldRewardPool<T: Config> {
|
||||
last_recorded_reward_counter: T::RewardCounter,
|
||||
last_recorded_total_payouts: BalanceOf<T>,
|
||||
total_rewards_claimed: BalanceOf<T>,
|
||||
}
|
||||
|
||||
impl<T: Config> OldRewardPool<T> {
|
||||
fn migrate_to_v5(self) -> RewardPool<T> {
|
||||
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<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV5<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let current = Pallet::<T>::current_storage_version();
|
||||
let onchain = Pallet::<T>::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::<T>::translate::<OldRewardPool<T>, _>(|_id, old_value| {
|
||||
translated.saturating_inc();
|
||||
Some(old_value.migrate_to_v5())
|
||||
});
|
||||
|
||||
current.put::<Pallet<T>>();
|
||||
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<Vec<u8>, TryRuntimeError> {
|
||||
let rpool_keys = RewardPools::<T>::iter_keys().count();
|
||||
let rpool_values = RewardPools::<T>::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::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
||||
"There are undecodable PoolMembers in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
||||
"There are undecodable BondedPools in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
SubPoolsStorage::<T>::iter_keys().count() ==
|
||||
SubPoolsStorage::<T>::iter_values().count(),
|
||||
"There are undecodable SubPools in storage. This migration will not fix that."
|
||||
);
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().count() == Metadata::<T>::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<u8>) -> Result<(), TryRuntimeError> {
|
||||
let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap();
|
||||
let rpool_keys = RewardPools::<T>::iter_keys().count() as u64;
|
||||
let rpool_values = RewardPools::<T>::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::<T>::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::<T>::on_chain_storage_version() >= 5,
|
||||
"nomination-pools::migration::v5: wrong storage version"
|
||||
);
|
||||
|
||||
// These should not have been touched - just in case.
|
||||
ensure!(
|
||||
PoolMembers::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
||||
"There are undecodable PoolMembers in storage."
|
||||
);
|
||||
ensure!(
|
||||
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
||||
"There are undecodable BondedPools in storage."
|
||||
);
|
||||
ensure!(
|
||||
SubPoolsStorage::<T>::iter_keys().count() ==
|
||||
SubPoolsStorage::<T>::iter_values().count(),
|
||||
"There are undecodable SubPools in storage."
|
||||
);
|
||||
ensure!(
|
||||
Metadata::<T>::iter_keys().count() == Metadata::<T>::iter_values().count(),
|
||||
"There are undecodable Metadata in storage."
|
||||
);
|
||||
|
||||
Pallet::<T>::try_state(frame_system::Pallet::<T>::block_number())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user