git.delta.rocks / unique-network / refs/commits / 0d52e2cbc000

difftreelog

Merge pull request #1068 from UniqueNetwork/chore/migrate-foreign-asset-to-v4

Yaroslav Bolyukin2024-06-06parents: #f78f490 #9a73a32.patch.diff
in: master
migrate foreign asset to v4 + fix foreign flags

3 files changed

modifiedCargo.lockdiffbeforeafterboth
after · Cargo.lock
1206 packageslockfile v3
modifiedpallets/foreign-assets/Cargo.tomldiffbeforeafterboth
--- a/pallets/foreign-assets/Cargo.toml
+++ b/pallets/foreign-assets/Cargo.toml
@@ -21,6 +21,7 @@
 staging-xcm = { workspace = true }
 staging-xcm-executor = { workspace = true }
 up-data-structs = { workspace = true }
+derivative = { workspace = true }
 
 [features]
 default = ["std"]
modifiedpallets/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)