Assume we have a StorageMap MyMap of which we want to extend the type of the decoded keys from KeyTypeOld to an enum including the old type as well as a new one.
Before
pub type MyMap = StorageMap<_, Blake2_128Concat, KeyTypeOld, V>;
After
pub enum MyEnum<A, B> {
Old(A),
New(B),
}
pub type MyMap = StorageMap<_, Blake2_128Concat, MyEnum<KeyTypeOld, KeyTypeAddition>, V>;
- Is it possible to migrate from the old map to the new one without keeping the definition of the old one in the WASM blob, e.g., only in-memory in the migration? This would keep the API of this storage entry consistent.
Unfortunately, my attempt (see below) fails to decode the keys when iterating over the in-memory created MyMapOld
, at least when testing with the try-runtime
feature.
mod migration {
use crate::MyMap as MyMapNew;
#[storage_alias]
type MyMap = StorageMap<_, Blake2_128Concat, KeyTypeOld, V>;
#[storage_alias]
type MyMapTmp = StorageMap<_, Blake2_128Concat, MyEnum<KeyTypeOld, KeyTypeAddition>, V>;
pub struct MyMigration(PhantomData<P>);
impl<P: crate::pallet::Config> OnRuntimeUpgrade for MyMigration<P>{
fn on_runtime_upgrade() -> frame_support::weights::Weight {
// Should not write to same storage item during drain
// Thus, write to temporary storage and move at the end
MyMap::<P>::drain().for_each(|(key_old, value)| {
let key_new = MyEnum::<KeyTypeOld, KeyTypeAddition>::Old(key_old);
MyMapTmp::<R>::insert(key_new, value);
});
// Move items from MyMapTmp to MyMapNew similar to
// frame_support::storage::migration::move_storage_from_pallet
...
}
}
The only solution I have found so far is to keep the old definition of MyMap
and have a co-existing MyMapNew
. However, that would break the API for services which I would like to avoid.
// inside pallet macro
#[pallet::storage]
pub type MyMap = StorageMap<_, Blake2_128Concat, KeyTypeOld, V>;
#[pallet::storage]
pub type MyMapNew = StorageMap<_, Blake2_128Concat, MyEnum<KeyTypeOld, KeyTypeAddition>, V>;
- Moreover, is my assumption correct that we can we expect services, which periodically connect to my chain and query MyMap storage via Polkadot-JS, to be able to automatically understand the correct decoding by the provided post-upgrade metadata?
MyMapTmp
for? Why not directly insert intocrate::MyMap
? Otherwise you insert into the same map that you drain.