diff --git a/Cargo.lock b/Cargo.lock index 297c4a8079..0051c3dcad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12336,9 +12336,11 @@ dependencies = [ "frame-system", "frame-try-runtime", "hex-literal 0.3.4", + "log", "pallet-balances", "pallet-configuration", "pallet-data-preservers", + "pallet-foreign-asset-creator", "pallet-invulnerables", "pallet-migrations", "pallet-pooled-staking", @@ -12351,6 +12353,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-std", + "staging-xcm", ] [[package]] diff --git a/container-chains/runtime-templates/simple/src/migrations.rs b/container-chains/runtime-templates/simple/src/migrations.rs index e0224f1945..fccab776cb 100644 --- a/container-chains/runtime-templates/simple/src/migrations.rs +++ b/container-chains/runtime-templates/simple/src/migrations.rs @@ -27,8 +27,8 @@ use { }, pallet_migrations::{GetMigrations, Migration}, runtime_common::migrations::{ - PolkadotXcmMigrationFixVersion, XcmpQueueMigrationFixVersion, XcmpQueueMigrationV3, - XcmpQueueMigrationV4, + ForeignAssetCreatorMigration, PolkadotXcmMigrationFixVersion, XcmpQueueMigrationFixVersion, + XcmpQueueMigrationV3, XcmpQueueMigrationV4, }, sp_std::{marker::PhantomData, prelude::*}, }; @@ -71,6 +71,9 @@ where Runtime: cumulus_pallet_xcmp_queue::Config, Runtime: pallet_xcm_executor_utils::Config, Runtime: pallet_xcm::Config, + Runtime: pallet_foreign_asset_creator::Config, + ::ForeignAsset: + TryFrom, { fn get_migrations() -> Vec> { let migrate_polkadot_xcm_v1 = @@ -82,6 +85,8 @@ where let migrate_xcm_executor_utils_v4 = pallet_xcm_executor_utils::migrations::MigrateToV1::(Default::default()); let migrate_pallet_xcm_v4 = MigrateToLatestXcmVersion::(Default::default()); + let foreign_asset_creator_migration = + ForeignAssetCreatorMigration::(Default::default()); vec![ Box::new(migrate_polkadot_xcm_v1), Box::new(migrate_xcmp_queue_v2), @@ -89,6 +94,7 @@ where Box::new(migrate_xcmp_queue_v4), Box::new(migrate_xcm_executor_utils_v4), Box::new(migrate_pallet_xcm_v4), + Box::new(foreign_asset_creator_migration), ] } } diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index b6e001d197..745d922c87 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -14,12 +14,14 @@ workspace = true [dependencies] hex-literal = { workspace = true } +log = { workspace = true } parity-scale-codec = { workspace = true, features = [ "derive" ] } scale-info = { workspace = true, features = [ "derive" ] } # Own pallet-configuration = { workspace = true } pallet-data-preservers = { workspace = true } +pallet-foreign-asset-creator = { workspace = true } pallet-invulnerables = { workspace = true } pallet-pooled-staking = { workspace = true } pallet-registrar = { workspace = true } @@ -41,6 +43,7 @@ cumulus-pallet-xcmp-queue = { workspace = true } # Polkadot pallet-xcm = { workspace = true } +staging-xcm = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } @@ -59,9 +62,11 @@ std = [ "frame-support/std", "frame-system/std", "frame-try-runtime?/std", + "log/std", "pallet-balances/std", "pallet-configuration/std", "pallet-data-preservers/std", + "pallet-foreign-asset-creator/std", "pallet-invulnerables/std", "pallet-migrations/std", "pallet-pooled-staking/std", @@ -74,6 +79,7 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + "staging-xcm/std", ] runtime-benchmarks = [ @@ -84,6 +90,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-configuration/runtime-benchmarks", "pallet-data-preservers/runtime-benchmarks", + "pallet-foreign-asset-creator/runtime-benchmarks", "pallet-invulnerables/runtime-benchmarks", "pallet-migrations/runtime-benchmarks", "pallet-pooled-staking/runtime-benchmarks", @@ -102,6 +109,7 @@ try-runtime = [ "pallet-balances/try-runtime", "pallet-configuration/try-runtime", "pallet-data-preservers/try-runtime", + "pallet-foreign-asset-creator/try-runtime", "pallet-invulnerables/try-runtime", "pallet-migrations/try-runtime", "pallet-pooled-staking/try-runtime", diff --git a/runtime/common/src/migrations.rs b/runtime/common/src/migrations.rs index d2126b3daf..c02d7d699e 100644 --- a/runtime/common/src/migrations.rs +++ b/runtime/common/src/migrations.rs @@ -36,6 +36,7 @@ #[cfg(feature = "try-runtime")] use frame_support::ensure; +use frame_support::migration::storage_key_iter; use { cumulus_primitives_core::ParaId, @@ -43,8 +44,11 @@ use { pallet_prelude::GetStorageVersion, traits::{OnRuntimeUpgrade, PalletInfoAccess, StorageVersion}, weights::Weight, + StoragePrefixedMap, }, pallet_configuration::{weights::WeightInfo as _, HostConfiguration}, + pallet_foreign_asset_creator::AssetId, + pallet_foreign_asset_creator::{AssetIdToForeignAsset, ForeignAssetToAssetId}, pallet_migrations::{GetMigrations, Migration}, sp_core::Get, sp_std::{collections::btree_set::BTreeSet, marker::PhantomData, prelude::*}, @@ -516,6 +520,9 @@ where Runtime: pallet_xcm::Config, ::RuntimeHoldReason: From, + Runtime: pallet_foreign_asset_creator::Config, + ::ForeignAsset: + TryFrom, { fn get_migrations() -> Vec> { // let migrate_invulnerables = MigrateInvulnerables::(Default::default()); @@ -540,6 +547,8 @@ where RegistrarParaManagerMigration::(Default::default()); let migrate_pallet_xcm_v4 = MigrateToLatestXcmVersion::(Default::default()); + let foreign_asset_creator_migration = + ForeignAssetCreatorMigration::(Default::default()); vec![ // Applied in runtime 200 @@ -564,6 +573,86 @@ where Box::new(migrate_registrar_pending_verification), Box::new(migrate_registrar_manager), Box::new(migrate_pallet_xcm_v4), + Box::new(foreign_asset_creator_migration), ] } } + +pub struct ForeignAssetCreatorMigration(pub PhantomData); + +impl Migration for ForeignAssetCreatorMigration +where + Runtime: pallet_foreign_asset_creator::Config, + ::ForeignAsset: + TryFrom, +{ + fn friendly_name(&self) -> &str { + "TM_ForeignAssetCreatorMigration" + } + + fn migrate(&self, _available_weight: Weight) -> Weight { + use frame_support::pallet_prelude::*; + + use staging_xcm::v3::MultiLocation as OldLocation; + + let pallet_prefix = AssetIdToForeignAsset::::pallet_prefix(); + let asset_id_to_foreign_asset_storage_prefix = + AssetIdToForeignAsset::::storage_prefix(); + let foreign_asset_to_asset_id_prefix = ForeignAssetToAssetId::::storage_prefix(); + + // Data required to migrate ForeignAsset values + // Read all the data into memory. + let asset_id_to_foreign_asset_data: Vec<_> = + storage_key_iter::, OldLocation, Blake2_128Concat>( + pallet_prefix, + asset_id_to_foreign_asset_storage_prefix, + ) + .drain() + .collect(); + + // Data required to migrate ForeignAsset keys + let foreign_asset_to_asset_id_data: Vec<_> = + storage_key_iter::, Blake2_128Concat>( + pallet_prefix, + foreign_asset_to_asset_id_prefix, + ) + .drain() + .collect(); + + let migrated_count = asset_id_to_foreign_asset_data + .len() + .saturating_add(foreign_asset_to_asset_id_data.len()); + + log::info!("Migrating {:?} elements", migrated_count); + + // Write to the new storage with removed and added fields + for (asset_id, old_location) in asset_id_to_foreign_asset_data { + if let Ok(new_location) = Runtime::ForeignAsset::try_from(old_location) { + AssetIdToForeignAsset::::insert(asset_id, new_location); + } else { + log::warn!("Location could not be converted safely to xcmV4") + } + } + + for (old_location, asset_id) in foreign_asset_to_asset_id_data { + if let Ok(new_location) = Runtime::ForeignAsset::try_from(old_location) { + ForeignAssetToAssetId::::insert(new_location, asset_id); + } else { + log::warn!("Location could not be converted safely to xcmV4") + } + } + + // One db read and one db write per element, plus the on-chain storage + Runtime::DbWeight::get().reads_writes(migrated_count as u64, 2 * migrated_count as u64) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade(&self) -> Result, DispatchError> { + Ok(vec![]) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(&self, _state: Vec) -> Result<(), DispatchError> { + Ok(()) + } +} diff --git a/runtime/dancebox/tests/integration_test.rs b/runtime/dancebox/tests/integration_test.rs index 342ea711ec..202af40899 100644 --- a/runtime/dancebox/tests/integration_test.rs +++ b/runtime/dancebox/tests/integration_test.rs @@ -16,6 +16,17 @@ #![cfg(test)] +use dancebox_runtime::xcm_config::ForeignAssetsInstance; +use frame_support::migration::put_storage_value; +use frame_support::storage::generator::StorageMap; +use frame_support::Hashable; +use pallet_foreign_asset_creator::{AssetIdToForeignAsset, ForeignAssetToAssetId}; +use runtime_common::migrations::ForeignAssetCreatorMigration; +use staging_xcm::v3::{ + Junction as V3Junction, Junctions as V3Junctions, MultiLocation as V3MultiLocation, + NetworkId as V3NetworkId, +}; +use std::marker::PhantomData; use { common::*, cumulus_primitives_core::ParaId, @@ -5581,6 +5592,94 @@ fn test_migration_services_collator_assignment_payment() { }); } +#[test] +fn test_migration_foreign_asset_creator() { + ExtBuilder::default().build().execute_with(|| { + // Sample pairs of asset id with v3 location + let (asset_id1, location_1) = ( + >::AssetId::from(13u16), + V3MultiLocation::new( + 1, + V3Junctions::X2( + V3Junction::PalletInstance(1), + V3Junction::AccountIndex64 { + network: Some(V3NetworkId::BitcoinCore), + index: 5, + }, + ), + ), + ); + + let (asset_id2, location_2) = ( + >::AssetId::from(14u16), + V3MultiLocation::new( + 1, + V3Junctions::X2( + V3Junction::PalletInstance(2), + V3Junction::AccountIndex64 { + network: Some(V3NetworkId::Kusama), + index: 10, + }, + ), + ), + ); + + put_storage_value( + AssetIdToForeignAsset::::pallet_prefix(), + AssetIdToForeignAsset::::storage_prefix(), + &asset_id1.blake2_128_concat(), + location_1, + ); + put_storage_value( + AssetIdToForeignAsset::::pallet_prefix(), + AssetIdToForeignAsset::::storage_prefix(), + &asset_id2.blake2_128_concat(), + location_2, + ); + + put_storage_value( + ForeignAssetToAssetId::::pallet_prefix(), + ForeignAssetToAssetId::::storage_prefix(), + &location_1.blake2_128_concat(), + asset_id1, + ); + put_storage_value( + ForeignAssetToAssetId::::pallet_prefix(), + ForeignAssetToAssetId::::storage_prefix(), + &location_2.blake2_128_concat(), + asset_id2, + ); + + // Let's run the migration now + let foreign_asset_creator_migration: ForeignAssetCreatorMigration = + ForeignAssetCreatorMigration(PhantomData); + let weight_consumed = foreign_asset_creator_migration.migrate(Default::default()); + assert_eq!( + weight_consumed, + ::DbWeight::get().reads_writes(1 * 4, 2 * 4) + ); + + // Let's check if everything is migrated properly + assert_eq!( + AssetIdToForeignAsset::::get(asset_id1), + Some(Location::try_from(location_1).unwrap()) + ); + assert_eq!( + AssetIdToForeignAsset::::get(asset_id2), + Some(Location::try_from(location_2).unwrap()) + ); + + assert_eq!( + ForeignAssetToAssetId::::get(Location::try_from(location_1).unwrap()), + Some(asset_id1) + ); + assert_eq!( + ForeignAssetToAssetId::::get(Location::try_from(location_2).unwrap()), + Some(asset_id2) + ); + }); +} + #[test] fn test_max_collators_uses_pending_value() { // Start with max_collators = 100, and collators_per_container = 2