# Storage Migration ## References ### Docs: - https://substrate.dev/docs/en/knowledgebase/runtime/upgrades - https://substrate.dev/rustdocs/v2.0.0/frame_support/traits/trait.OnRuntimeUpgrade.html ### Video - https://www.youtube.com/watch?v=MQgDV37JrIY&t=5105s ## Some built-in functions - on_initialize, trait: OnInitialize - on_finalize, trait: OnFinalize - offchain_worker - on_runtime_upgrade, trait: OnRuntimeUpgrade ## Example ### original storages ```rust #[derive(Encode, Decode, Clone, Debug, PartialEq)] pub enum Version { V0, V1, V2, } impl Default for Version { fn default() -> Self { Self::V1 } } #[derive(Encode, Decode, Clone, Debug, Default)] pub struct Foo { a: u32 } decl_storage! { trait Store for Module<T: Trait> as Migration { pub FooStorage: map hasher(blake2_128_concat) T::AccountId => Foo; pub StorageVersion: Version = Version::V0; } } ``` ### New storages There're some data types added and changed. ```rust #[derive(Encode, Decode, Clone, Debug, PartialEq)] pub enum Status { Open, Close, Unknown, } impl Default for Status { fn default() -> Self { Self::Close } } #[derive(Encode, Decode, Clone, Debug, Default)] pub struct Foo { a: u64, b: Status, } // but no storage changes. decl_storage! { trait Store for Module<T: Trait> as Migration { pub FooStorage: map hasher(blake2_128_concat) T::AccountId => Foo; pub StorageVersion: Version = Version::V0; } } ``` ### How to migrate Use module to include the deprecated data type ```rust pub mod storage_migration { use crate::{Trait}; use codec::{Decode, Encode}; use frame_support::{weights::Weight, decl_module, decl_storage, IterableStorageMap, StorageValue}; #[derive(Encode, Decode, Clone, Debug, Default)] pub struct Foo { a: u32, } decl_storage! { trait Store for Module<T: Trait> as Assets { pub FooStorage: map hasher(blake2_128_concat) T::AccountId => Foo; } } decl_module! { pub struct Module<T: Trait> for enum Call where origin: T::Origin {} } pub fn migration_to_new_foo<T: Trait>() -> Weight { if crate::StorageVersion::get() == crate::Version::V0 { // take all old storages from previous state for (who, old_val) in FooStorage::<T>::drain() { let new_val = crate::Foo { a: old_val.a as u64, b: crate::Status::Open }; crate::FooStorage::<T>::insert(who, new_val); crate::StorageVersion::put(crate::Version::V1); } 1000 } else { 0 } } } decl_module! { // ... fn on_runtime_upgrade() -> Weight { storage_migration::migration_to_new_foo::<T>() } // ... } ``` ## Demonstration Remember to update type definition to front-end. - Old types ``` "Foo": { "a": "u32" }, "Version": { "_enum": [ "V0", "V1", "V2" ] } ``` - New types ``` "Foo": { "a": "u64", "b": "Status" }, "Version": { "_enum": [ "V0", "V1", "V2" ] }, "Status": { "_enum": [ "Open", "Close", "Unknown" ] } ```