pub mod foreign_assets_reserves {
use crate::*;
use codec::{Decode, Encode, MaxEncodedLen};
use core::{fmt::Debug, marker::PhantomData};
use frame_support::{
migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
weights::WeightMeter,
};
use pallet_assets::WeightInfo;
const MIGRATIONS_ID: &[u8; 23] = b"foreign-assets-reserves";
#[cfg(feature = "try-runtime")]
#[derive(Encode, Decode)]
struct TryRuntimeState<T: pallet_assets::Config<I>, I: 'static> {
assets: Vec<T::AssetId>,
}
#[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)]
pub enum MigrationState<A> {
Asset(A),
Finished,
}
pub trait ForeignAssetsReservesProvider {
type ReserveData: Debug;
fn reserves_for(asset_id: &Location) -> Vec<Self::ReserveData>;
#[cfg(feature = "try-runtime")]
fn check_reserves_for(asset_id: &Location, reserves: Vec<Self::ReserveData>) -> bool;
}
type StepResultOf<T, I> = MigrationState<<T as pallet_assets::Config<I>>::AssetId>;
pub struct ForeignAssetsReservesMigration<T, I, ReservesProvider>(
PhantomData<(T, I, ReservesProvider)>,
);
impl<T, I, ReservesProvider> SteppedMigration
for ForeignAssetsReservesMigration<T, I, ReservesProvider>
where
ReservesProvider: ForeignAssetsReservesProvider,
T: pallet_assets::Config<
I,
AssetId = Location,
ReserveData = ReservesProvider::ReserveData,
>,
I: 'static,
{
type Cursor = StepResultOf<T, I>;
type Identifier = MigrationId<23>;
fn id() -> Self::Identifier {
MigrationId { pallet_id: *MIGRATIONS_ID, version_from: 1, version_to: 1 }
}
fn step(
mut cursor: Option<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
let required =
<T as pallet_assets::Config<I>>::WeightInfo::migration_v2_foreign_asset_set_reserve_weight();
tracing::debug!(target: "runtime::ForeignAssetsReservesMigration", ?meter, ?required);
if !meter.can_consume(required) {
return Err(SteppedMigrationError::InsufficientWeight { required });
}
loop {
if !meter.can_consume(required) {
break;
}
let next = match &cursor {
None => Self::asset_step(None),
Some(MigrationState::Asset(maybe_last_asset)) =>
Self::asset_step(Some(maybe_last_asset)),
Some(MigrationState::Finished) => {
tracing::info!(target: "runtime::ForeignAssetsReservesMigration", "migration finished");
return Ok(None)
},
};
cursor = Some(next);
meter.consume(required);
}
Ok(cursor)
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
let assets = pallet_assets::Asset::<T, I>::iter_keys().collect();
tracing::info!(target: "runtime::ForeignAssetsReservesMigration::pre_upgrade", ?assets);
let state = TryRuntimeState::<T, I> { assets };
Ok(state.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
let prev_state = TryRuntimeState::<T, I>::decode(&mut &state[..])
.expect("Failed to decode the previous storage state");
for id in prev_state.assets {
let reserves = pallet_assets::Pallet::<T, I>::get_reserves_data(id.clone());
tracing::info!(target: "runtime::ForeignAssetsReservesMigration::post_upgrade", ?id, ?reserves, "verify asset");
assert!(ReservesProvider::check_reserves_for(&id, reserves));
}
Ok(())
}
}
impl<T, I, ReservesProvider> ForeignAssetsReservesMigration<T, I, ReservesProvider>
where
ReservesProvider: ForeignAssetsReservesProvider,
T: pallet_assets::Config<
I,
AssetId = Location,
ReserveData = ReservesProvider::ReserveData,
>,
I: 'static,
{
fn asset_step(maybe_last_key: Option<&T::AssetId>) -> StepResultOf<T, I> {
tracing::debug!(target: "runtime::ForeignAssetsReservesMigration::asset_step", ?maybe_last_key);
let mut iter = if let Some(last_key) = maybe_last_key {
pallet_assets::Asset::<T, I>::iter_keys_from(
pallet_assets::Asset::<T, I>::hashed_key_for(last_key),
)
} else {
pallet_assets::Asset::<T, I>::iter_keys()
};
if let Some(asset_id) = iter.next() {
let reserves = ReservesProvider::reserves_for(&asset_id);
tracing::info!(
target: "runtime::ForeignAssetsReservesMigration::asset_step",
?asset_id, ?reserves, "updating reserves for"
);
if let Err(e) = pallet_assets::Pallet::<T, I>::unchecked_update_reserves(
asset_id.clone(),
reserves,
) {
tracing::error!(
target: "runtime::ForeignAssetsReservesMigration::asset_step",
?e, ?asset_id, "failed migrating reserves for asset"
);
}
MigrationState::Asset(asset_id)
} else {
MigrationState::Finished
}
}
}
}