difftreelog
Merge pull request #1068 from UniqueNetwork/chore/migrate-foreign-asset-to-v4
in: master
migrate foreign asset to v4 + fix foreign flags
3 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7279,6 +7279,7 @@
name = "pallet-foreign-assets"
version = "0.1.0"
dependencies = [
+ "derivative",
"frame-benchmarking",
"frame-support",
"frame-system",
pallets/foreign-assets/Cargo.tomldiffbeforeafterboth1[package]2edition = "2021"3license = "GPLv3"4name = "pallet-foreign-assets"5version = "0.1.0"67[dependencies]8parity-scale-codec = { workspace = true }9scale-info = { workspace = true }1011frame-benchmarking = { workspace = true, optional = true }12frame-support = { workspace = true }13frame-system = { workspace = true }14log = { workspace = true }15pallet-balances = { features = ["insecure_zero_ed"], workspace = true }16pallet-common = { workspace = true }17pallet-fungible = { workspace = true }18serde = { workspace = true }19sp-runtime = { workspace = true }20sp-std = { workspace = true }21staging-xcm = { workspace = true }22staging-xcm-executor = { workspace = true }23up-data-structs = { workspace = true }24derivative = { workspace = true }2526[features]27default = ["std"]28runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']29std = [30 "frame-support/std",31 "frame-system/std",32 "log/std",33 "pallet-balances/std",34 "pallet-common/std",35 "pallet-fungible/std",36 "parity-scale-codec/std",37 "scale-info/std",38 "sp-runtime/std",39 "sp-std/std",40 "staging-xcm-executor/std",41 "up-data-structs/std",42]43try-runtime = ["frame-support/try-runtime"]pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -25,7 +25,10 @@
use core::ops::Deref;
-use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};
+use derivative::Derivative;
+use frame_support::{
+ dispatch::DispatchResult, pallet_prelude::*, storage_alias, traits::EnsureOrigin, PalletId,
+};
use frame_system::pallet_prelude::*;
use pallet_common::{
dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,
@@ -38,8 +41,8 @@
AssetsInHolding,
};
use up_data_structs::{
- budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CollectionTokenPrefix,
- CreateCollectionData, CreateFungibleData, CreateItemData, TokenId,
+ budget::ZeroBudget, CollectionFlags, CollectionId, CollectionMode, CollectionName,
+ CollectionTokenPrefix, CreateCollectionData, CreateFungibleData, CreateItemData, TokenId,
};
pub mod weights;
@@ -50,8 +53,44 @@
pub use module::*;
pub use weights::WeightInfo;
+/// Status of storage migration from an old XCM version to a new one.
+#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+pub enum MigrationStatus {
+ V3ToV4(MigrationStatusV3ToV4),
+}
+
+/// Status of storage migration from XCMv3 to XCMv4.
+#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
+pub enum MigrationStatusV3ToV4 {
+ /// The migration is completed.
+ Done,
+
+ /// An asset is skipped during the migration
+ /// due to its inconsistent state.
+ SkippedInconsistentAssetData(staging_xcm::v3::AssetId),
+
+ /// An asset instance is skipped during the migration
+ /// due to its inconsistent state.
+ SkippedInconsistentAssetInstanceData {
+ asset_id: staging_xcm::v3::AssetId,
+ asset_instance: staging_xcm::v3::AssetInstance,
+ },
+
+ /// An asset is skipped during the migration
+ /// because it couldn't be converted to the new XCM version.
+ SkippedNotConvertibleAssetId(staging_xcm::v3::AssetId),
+
+ /// An asset instance is skipped during the migration
+ /// because it couldn't be converted to the new XCM version.
+ SkippedNotConvertibleAssetInstance {
+ asset_id: staging_xcm::v3::AssetId,
+ asset_instance: staging_xcm::v3::AssetInstance,
+ },
+}
+
#[frame_support::pallet]
pub mod module {
+ use frame_support::traits::BuildGenesisConfig;
use pallet_common::CollectionIssuer;
use up_data_structs::CollectionDescription;
@@ -93,35 +132,37 @@
}
#[pallet::event]
- #[pallet::generate_deposit(fn deposit_event)]
+ #[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
/// The foreign asset registered.
ForeignAssetRegistered {
collection_id: CollectionId,
asset_id: Box<VersionedAssetId>,
},
+
+ /// The migration status.
+ MigrationStatus(MigrationStatus),
}
/// The corresponding collections of foreign assets.
#[pallet::storage]
#[pallet::getter(fn foreign_asset_to_collection)]
pub type ForeignAssetToCollection<T: Config> =
- StorageMap<_, Twox64Concat, staging_xcm::v4::AssetId, CollectionId, OptionQuery>;
+ StorageMap<_, Blake2_128Concat, staging_xcm::v4::AssetId, CollectionId, OptionQuery>;
/// The corresponding foreign assets of collections.
#[pallet::storage]
#[pallet::getter(fn collection_to_foreign_asset)]
pub type CollectionToForeignAsset<T: Config> =
- StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v4::AssetId, OptionQuery>;
+ StorageMap<_, Blake2_128Concat, CollectionId, staging_xcm::v4::AssetId, OptionQuery>;
/// The correponding NFT token id of reserve NFTs
#[pallet::storage]
#[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]
pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<
- Hasher1 = Twox64Concat,
+ Hasher1 = Blake2_128Concat,
Key1 = CollectionId,
Hasher2 = Blake2_128Concat,
- // Key2 = staging_xcm::v3::AssetInstance,
Key2 = staging_xcm::v4::AssetInstance,
Value = TokenId,
QueryKind = OptionQuery,
@@ -131,16 +172,18 @@
#[pallet::storage]
#[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]
pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<
- Hasher1 = Twox64Concat,
+ Hasher1 = Blake2_128Concat,
Key1 = CollectionId,
Hasher2 = Blake2_128Concat,
Key2 = TokenId,
- // Value = staging_xcm::v3::AssetInstance,
Value = staging_xcm::v4::AssetInstance,
QueryKind = OptionQuery,
>;
+ const STORAGE_VERSION: StorageVersion = StorageVersion::new(staging_xcm::v4::VERSION as u16);
+
#[pallet::pallet]
+ #[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);
#[pallet::call]
@@ -183,6 +226,10 @@
token_prefix,
description,
mode: mode.into(),
+ flags: CollectionFlags {
+ foreign: true,
+ ..Default::default()
+ },
..Default::default()
},
)?;
@@ -198,9 +245,217 @@
Ok(())
}
}
+
+ #[pallet::genesis_config]
+ #[derive(Derivative)]
+ #[derivative(Default(bound = ""))]
+ pub struct GenesisConfig<T: Config>(PhantomData<T>);
+
+ #[pallet::genesis_build]
+ impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+ fn build(&self) {
+ <Pallet<T>>::in_code_storage_version().put::<Pallet<T>>();
+ }
+ }
+
+ #[pallet::hooks]
+ impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+ fn on_runtime_upgrade() -> Weight {
+ if Self::on_chain_storage_version() < staging_xcm::v4::VERSION as u16 {
+ let put_version_weight = T::DbWeight::get().writes(1);
+ let fix_foreign_flag_weight = Self::fix_foreign_flag();
+ let weight_v3_to_v4 = Self::migrate_v3_to_v4();
+
+ Self::in_code_storage_version().put::<Self>();
+
+ put_version_weight
+ .saturating_add(fix_foreign_flag_weight)
+ .saturating_add(weight_v3_to_v4)
+ } else {
+ Weight::zero()
+ }
+ }
+ }
}
+mod v3_storage {
+ use super::*;
+
+ #[storage_alias]
+ pub type ForeignAssetToCollection<T: Config> =
+ StorageMap<Pallet<T>, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;
+
+ #[storage_alias]
+ pub type CollectionToForeignAsset<T: Config> =
+ StorageMap<Pallet<T>, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;
+
+ #[storage_alias]
+ pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<
+ Pallet<T>,
+ Twox64Concat,
+ CollectionId,
+ Blake2_128Concat,
+ staging_xcm::v3::AssetInstance,
+ TokenId,
+ OptionQuery,
+ >;
+
+ #[storage_alias]
+ pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<
+ Pallet<T>,
+ Twox64Concat,
+ CollectionId,
+ Blake2_128Concat,
+ TokenId,
+ staging_xcm::v3::AssetInstance,
+ OptionQuery,
+ >;
+}
+
impl<T: Config> Pallet<T> {
+ fn fix_foreign_flag() -> Weight {
+ let mut weight = Weight::zero();
+
+ for (_, collection_id) in v3_storage::ForeignAssetToCollection::<T>::iter() {
+ pallet_common::CollectionById::<T>::mutate(collection_id, |collection| {
+ if let Some(collection) = collection {
+ collection.flags.foreign = true;
+ }
+ });
+
+ weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1));
+ }
+
+ weight
+ }
+
+ fn migrate_v3_to_v4() -> Weight {
+ let event_weight = T::DbWeight::get().writes(1);
+ let collection_migration_weight = Self::migrate_collections();
+
+ Self::deposit_event(Event::<T>::MigrationStatus(MigrationStatus::V3ToV4(
+ MigrationStatusV3ToV4::Done,
+ )));
+
+ collection_migration_weight.saturating_add(event_weight)
+ }
+
+ fn migrate_collections() -> Weight {
+ use MigrationStatus::*;
+ use MigrationStatusV3ToV4::*;
+
+ let mut weight = Weight::zero();
+
+ for (fwd_asset_id, collection_id) in v3_storage::ForeignAssetToCollection::<T>::drain() {
+ let bwd_asset_id = v3_storage::CollectionToForeignAsset::<T>::take(collection_id);
+ weight = weight.saturating_add(T::DbWeight::get().reads(2));
+
+ let Some(bwd_asset_id) = bwd_asset_id else {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedInconsistentAssetData(fwd_asset_id),
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ };
+
+ if fwd_asset_id != bwd_asset_id {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedInconsistentAssetData(fwd_asset_id),
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ }
+
+ let Ok(asset_id) = staging_xcm::v4::AssetId::try_from(fwd_asset_id) else {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedNotConvertibleAssetId(fwd_asset_id),
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ };
+
+ <ForeignAssetToCollection<T>>::insert(&asset_id, collection_id);
+ <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);
+ weight = weight.saturating_add(T::DbWeight::get().writes(2));
+
+ let migrate_tokens_weight = Self::migrate_tokens(&fwd_asset_id, collection_id);
+ weight = weight.saturating_add(migrate_tokens_weight);
+ }
+
+ weight
+ }
+
+ fn migrate_tokens(asset_id: &staging_xcm::v3::AssetId, collection_id: CollectionId) -> Weight {
+ use MigrationStatus::*;
+ use MigrationStatusV3ToV4::*;
+
+ let mut weight = Weight::zero();
+
+ for (fwd_asset_instance, token_id) in
+ v3_storage::ForeignReserveAssetInstanceToTokenId::<T>::drain_prefix(collection_id)
+ {
+ let bwd_asset_instance = v3_storage::TokenIdToForeignReserveAssetInstance::<T>::take(
+ collection_id,
+ token_id,
+ );
+ weight = weight.saturating_add(T::DbWeight::get().reads(2));
+
+ let Some(bwd_asset_instance) = bwd_asset_instance else {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedInconsistentAssetInstanceData {
+ asset_id: *asset_id,
+ asset_instance: fwd_asset_instance,
+ },
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ };
+
+ if fwd_asset_instance != bwd_asset_instance {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedInconsistentAssetInstanceData {
+ asset_id: *asset_id,
+ asset_instance: fwd_asset_instance,
+ },
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ }
+
+ let Ok(asset_instance) = staging_xcm::v4::AssetInstance::try_from(fwd_asset_instance)
+ else {
+ Self::deposit_event(Event::<T>::MigrationStatus(V3ToV4(
+ SkippedNotConvertibleAssetInstance {
+ asset_id: *asset_id,
+ asset_instance: fwd_asset_instance,
+ },
+ )));
+
+ weight = weight.saturating_add(T::DbWeight::get().writes(1));
+ continue;
+ };
+
+ <ForeignReserveAssetInstanceToTokenId<T>>::insert(
+ collection_id,
+ &asset_instance,
+ token_id,
+ );
+ <TokenIdToForeignReserveAssetInstance<T>>::insert(
+ collection_id,
+ token_id,
+ asset_instance,
+ );
+ weight = weight.saturating_add(T::DbWeight::get().writes(2));
+ }
+
+ weight
+ }
+
fn pallet_account() -> T::CrossAccountId {
let owner: T::AccountId = T::PalletId::get().into_account_truncating();
T::CrossAccountId::from_sub(owner)