diff --git a/runtime/altair/src/migrations.rs b/runtime/altair/src/migrations.rs index 949dd37e10..6de3291c7f 100644 --- a/runtime/altair/src/migrations.rs +++ b/runtime/altair/src/migrations.rs @@ -12,6 +12,9 @@ use crate::{ForeignInvestments, OraclePriceCollection, OraclePriceFeed, OrderBook}; +// Number of identities on Altair Chain on 30.05.2024 was 34 +const IDENTITY_MIGRATION_KEY_LIMIT: u64 = 1000; + /// The migration set for Altair @ Kusama. /// It includes all the migrations that have to be applied on that chain. pub type UpgradeAltair1035 = ( @@ -20,4 +23,15 @@ pub type UpgradeAltair1035 = ( runtime_common::migrations::increase_storage_version::Migration, runtime_common::migrations::increase_storage_version::Migration, pallet_collator_selection::migration::v1::MigrateToV1, + runtime_common::migrations::loans::AddWithLinearPricing, + // As of May 2024, the `pallet_balances::Hold` storage was empty. But better be safe. + runtime_common::migrations::hold_reason::MigrateTransferAllowListHolds< + crate::Runtime, + crate::RuntimeHoldReason, + >, + // Migrations below this comment originate from Polkadot SDK + pallet_xcm::migration::MigrateToLatestXcmVersion, + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + pallet_identity::migration::versioned::V0ToV1, + pallet_uniques::migration::MigrateV0ToV1, ); diff --git a/runtime/centrifuge/src/migrations.rs b/runtime/centrifuge/src/migrations.rs index 7bba719806..38880827ae 100644 --- a/runtime/centrifuge/src/migrations.rs +++ b/runtime/centrifuge/src/migrations.rs @@ -12,6 +12,9 @@ use crate::{OraclePriceCollection, OraclePriceFeed}; +// Number of identities on Centrifuge Chain on 29.05.2024 was 34 +const IDENTITY_MIGRATION_KEY_LIMIT: u64 = 1000; + /// The migration set for Centrifuge @ Polkadot. /// It includes all the migrations that have to be applied on that chain. pub type UpgradeCentrifuge1029 = ( @@ -19,4 +22,13 @@ pub type UpgradeCentrifuge1029 = ( runtime_common::migrations::increase_storage_version::Migration, pallet_collator_selection::migration::v1::MigrateToV1, runtime_common::migrations::loans::AddWithLinearPricing, + runtime_common::migrations::hold_reason::MigrateTransferAllowListHolds< + crate::Runtime, + crate::RuntimeHoldReason, + >, + // Migrations below this comment originate from Polkadot SDK + pallet_xcm::migration::MigrateToLatestXcmVersion, + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + pallet_identity::migration::versioned::V0ToV1, + pallet_uniques::migration::MigrateV0ToV1, ); diff --git a/runtime/common/src/migrations/hold_reason.rs b/runtime/common/src/migrations/hold_reason.rs new file mode 100644 index 0000000000..da29b24d5a --- /dev/null +++ b/runtime/common/src/migrations/hold_reason.rs @@ -0,0 +1,151 @@ +// Copyright 2023 Centrifuge Foundation (centrifuge.io). +// +// This file is part of the Centrifuge chain project. +// Centrifuge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version (see http://www.gnu.org/licenses). +// Centrifuge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use cfg_primitives::{AccountId, Balance}; +use frame_support::{ + pallet_prelude::ValueQuery, + storage_alias, + traits::{ConstU32, Get, Len, OnRuntimeUpgrade, VariantCountOf}, + Blake2_128Concat, BoundedVec, Parameter, +}; +use pallet_balances::IdAmount; +use pallet_order_book::weights::Weight; +use pallet_transfer_allowlist::HoldReason; +#[cfg(feature = "try-runtime")] +use parity_scale_codec::{Decode, Encode}; +use parity_scale_codec::{FullCodec, FullEncode}; +#[cfg(feature = "try-runtime")] +use sp_runtime::Saturating; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; +use sp_runtime::{traits::Member, SaturatedConversion}; +use sp_std::{vec, vec::Vec}; + +const LOG_PREFIX: &str = "MigrateTransferAllowList HoldReason:"; + +pub struct MigrateTransferAllowListHolds( + sp_std::marker::PhantomData<(T, RuntimeHoldReason)>, +); + +type OldHolds = BoundedVec, ConstU32<10>>; +type NewHolds = + BoundedVec, VariantCountOf>; + +#[storage_alias] +pub type Holds = + StorageMap, Blake2_128Concat, AccountId, OldHolds, ValueQuery>; + +impl OnRuntimeUpgrade for MigrateTransferAllowListHolds +where + T: pallet_balances::Config + + pallet_transfer_allowlist::Config + + frame_system::Config, + ::RuntimeHoldReason: From, + RuntimeHoldReason: frame_support::traits::VariantCount + + FullCodec + + FullEncode + + Parameter + + Member + + sp_std::fmt::Debug, +{ + fn on_runtime_upgrade() -> Weight { + let transfer_allowlist_accounts: Vec = + pallet_transfer_allowlist::AccountCurrencyTransferAllowance::::iter_keys() + .map(|(a, _, _)| a) + .collect(); + let mut weight = + T::DbWeight::get().reads(transfer_allowlist_accounts.len().saturated_into()); + + pallet_balances::Holds::::translate::(|who, holds| { + if Self::account_can_be_migrated(&who, &transfer_allowlist_accounts) { + log::info!("{LOG_PREFIX} Migrating hold for account {who:?}"); + let id_amount = IdAmount:: { + id: HoldReason::TransferAllowance.into(), + // Non-emptiness ensured above + amount: holds[0].amount, + }; + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + + Some(NewHolds::truncate_from(vec![id_amount])) + } else { + None + } + }); + + log::info!( + "{LOG_PREFIX} migrated {:?} accounts", + transfer_allowlist_accounts.len() + ); + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + let transfer_allowlist_accounts: Vec = + pallet_transfer_allowlist::AccountCurrencyTransferAllowance::::iter_keys() + .map(|(a, _, _)| a) + .collect(); + + let mut n = 0u64; + for (acc, _) in Holds::::iter() { + assert!(Self::account_can_be_migrated( + &acc, + &transfer_allowlist_accounts + )); + n.saturating_accrue(1); + } + + log::info!("{LOG_PREFIX} pre checks done"); + Ok(n.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(pre_state: Vec) -> Result<(), TryRuntimeError> { + let count_pre: u64 = Decode::decode(&mut pre_state.as_slice()) + .expect("pre_upgrade provides a valid state; qed"); + + let holds_post = pallet_balances::Holds::::iter(); + let count_post: u64 = holds_post.count().saturated_into(); + assert_eq!(count_pre, count_post); + + for (_, hold) in pallet_balances::Holds::::iter() { + assert_eq!(hold.len(), 1); + assert_eq!(hold[0].id, HoldReason::TransferAllowance.into()); + } + + log::info!("{LOG_PREFIX} post checks done"); + Ok(()) + } +} + +impl MigrateTransferAllowListHolds +where + T: pallet_balances::Config + + pallet_transfer_allowlist::Config + + frame_system::Config, +{ + fn account_can_be_migrated(who: &AccountId, whitelist: &[AccountId]) -> bool { + if !whitelist.iter().any(|a| a == who) { + log::warn!("{LOG_PREFIX} Account {who:?} is skipped due to missing AccountCurrencyTransferAllowance storage entry"); + return false; + } + + match Holds::::get(who) { + holds if holds.len() == 1 => true, + _ => { + log::warn!("{LOG_PREFIX} Account {who:?} does not meet Hold storage assumptions"); + false + } + } + } +} diff --git a/runtime/common/src/migrations/mod.rs b/runtime/common/src/migrations/mod.rs index b4bbaa8069..dc12bdeb04 100644 --- a/runtime/common/src/migrations/mod.rs +++ b/runtime/common/src/migrations/mod.rs @@ -12,6 +12,7 @@ //! Centrifuge Runtime-Common Migrations +pub mod hold_reason; pub mod increase_storage_version; pub mod loans; pub mod nuke; diff --git a/runtime/development/src/migrations.rs b/runtime/development/src/migrations.rs index 0f3d1912b6..87e8267783 100644 --- a/runtime/development/src/migrations.rs +++ b/runtime/development/src/migrations.rs @@ -16,6 +16,9 @@ parameter_types! { pub const AnnualTreasuryInflationPercent: u32 = 3; } +// Number of identities on Dev and Demo Chain on 30.05.2024 was both 0 +const IDENTITY_MIGRATION_KEY_LIMIT: u64 = 1000; + /// The migration set for Development & Demo. /// It includes all the migrations that have to be applied on that chain. pub type UpgradeDevelopment1047 = ( @@ -30,11 +33,10 @@ pub type UpgradeDevelopment1047 = ( // v0 -> v1 runtime_common::migrations::nuke::ResetPallet, // v0 -> v1 - pallet_xcm::migration::v1::VersionUncheckedMigrateToV1, + runtime_common::migrations::nuke::ResetPallet, runtime_common::migrations::increase_storage_version::Migration, runtime_common::migrations::increase_storage_version::Migration, runtime_common::migrations::increase_storage_version::Migration, - runtime_common::migrations::increase_storage_version::Migration, runtime_common::migrations::increase_storage_version::Migration, runtime_common::migrations::increase_storage_version::Migration< crate::OraclePriceCollection, @@ -42,14 +44,25 @@ pub type UpgradeDevelopment1047 = ( 1, >, runtime_common::migrations::increase_storage_version::Migration, - // Reset Block rewards + // Reset Block rewards on Demo which is at v0 runtime_common::migrations::nuke::ResetPallet, + // Reset Block rewards on Dev which is at v2 + runtime_common::migrations::nuke::ResetPallet, pallet_block_rewards::migrations::init::InitBlockRewards< crate::Runtime, CollatorReward, AnnualTreasuryInflationPercent, >, runtime_common::migrations::loans::AddWithLinearPricing, + runtime_common::migrations::hold_reason::MigrateTransferAllowListHolds< + crate::Runtime, + crate::RuntimeHoldReason, + >, + // Migrations below this comment originate from Polkadot SDK + pallet_xcm::migration::MigrateToLatestXcmVersion, + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + pallet_identity::migration::versioned::V0ToV1, + pallet_uniques::migration::MigrateV0ToV1, ); mod cleanup_foreign_investments {