extern crate alloc;
use super::PALLET_MIGRATIONS_ID;
use crate::{weights::WeightInfo, AccountInfo, AccountInfoOf, Config, H160};
use frame_support::{
migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
pallet_prelude::PhantomData,
weights::WeightMeter,
};
#[cfg(feature = "try-runtime")]
use alloc::collections::btree_map::BTreeMap;
#[cfg(feature = "try-runtime")]
use alloc::vec::Vec;
pub mod old {
use super::Config;
use crate::{pallet::Pallet, ContractInfo, H160};
use frame_support::{storage_alias, Identity};
#[storage_alias]
pub type ContractInfoOf<T: Config> = StorageMap<Pallet<T>, Identity, H160, ContractInfo<T>>;
}
pub struct Migration<T: Config>(PhantomData<T>);
impl<T: Config> SteppedMigration for Migration<T> {
type Cursor = H160;
type Identifier = MigrationId<17>;
fn id() -> Self::Identifier {
MigrationId { pallet_id: *PALLET_MIGRATIONS_ID, version_from: 0, version_to: 1 }
}
fn step(
mut cursor: Option<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
let required = <T as Config>::WeightInfo::v1_migration_step();
if meter.remaining().any_lt(required) {
return Err(SteppedMigrationError::InsufficientWeight { required });
}
loop {
if meter.try_consume(required).is_err() {
break;
}
let iter = if let Some(last_key) = cursor {
old::ContractInfoOf::<T>::iter_from(old::ContractInfoOf::<T>::hashed_key_for(
last_key,
))
} else {
old::ContractInfoOf::<T>::iter()
};
if let Some((last_key, value)) = iter.drain().next() {
AccountInfoOf::<T>::insert(
last_key,
AccountInfo { account_type: value.into(), ..Default::default() },
);
cursor = Some(last_key)
} else {
cursor = None;
break
}
}
Ok(cursor)
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, frame_support::sp_runtime::TryRuntimeError> {
use codec::Encode;
Ok(old::ContractInfoOf::<T>::iter().collect::<BTreeMap<_, _>>().encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(prev: Vec<u8>) -> Result<(), frame_support::sp_runtime::TryRuntimeError> {
use codec::Decode;
let prev_map = BTreeMap::<H160, crate::ContractInfo<T>>::decode(&mut &prev[..])
.expect("Failed to decode the previous storage state");
assert_eq!(
AccountInfoOf::<T>::iter().count(),
prev_map.len(),
"Migration failed: the number of items in the storage after the migration is not the same as before"
);
for (key, value) in prev_map {
let new_value = AccountInfo::<T>::load_contract(&key);
assert_eq!(
Some(value),
new_value,
"Migration failed: the value after the migration is not the same as before"
);
}
Ok(())
}
}
#[test]
fn migrate_to_v1() {
use crate::{
tests::{ExtBuilder, Test},
ContractInfo,
};
ExtBuilder::default().build().execute_with(|| {
for i in 0..10u8 {
let addr = H160::from([i; 20]);
old::ContractInfoOf::<Test>::insert(
addr,
ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap(),
);
}
let mut cursor = None;
let mut weight_meter = WeightMeter::new();
while let Some(new_cursor) = Migration::<Test>::step(cursor, &mut weight_meter).unwrap() {
cursor = Some(new_cursor);
}
assert_eq!(old::ContractInfoOf::<Test>::iter().count(), 0);
assert_eq!(AccountInfoOf::<Test>::iter().count(), 10);
})
}