Runtime Upgrade ref docs and Single Block Migration example pallet (#1554)

Closes https://github.com/paritytech/polkadot-sdk-docs/issues/55

- Changes 'current storage version' terminology to less ambiguous
'in-code storage version' (suggestion by @ggwpez)
- Adds a new example pallet `pallet-example-single-block-migrations`
- Adds a new reference doc to replace
https://docs.substrate.io/maintain/runtime-upgrades/ (temporarily living
in the pallet while we wait for developer hub PR to merge)
- Adds documentation for the `storage_alias` macro
- Improves `trait Hooks` docs 
- Improves `trait GetStorageVersion` docs
- Update the suggested patterns for using `VersionedMigration`, so that
version unchecked migrations are never exported
- Prevents accidental usage of version unchecked migrations in runtimes

https://github.com/paritytech/substrate/pull/14421#discussion_r1255467895
- Unversioned migration code is kept inside `mod version_unchecked`,
versioned code is kept in `pub mod versioned`
- It is necessary to use modules to limit visibility because the inner
migration must be `pub`. See
https://github.com/rust-lang/rust/issues/30905 and

https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40
for more.

### todo

- [x] move to reference docs to proper place within sdk-docs (now that
https://github.com/paritytech/polkadot-sdk/pull/2102 is merged)
- [x] prdoc

---------

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Juan <juangirini@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: command-bot <>
Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com>
This commit is contained in:
Liam Aharon
2024-02-28 18:32:02 +11:00
committed by GitHub
parent 7ec0b8741b
commit 12ce4f7d04
87 changed files with 1222 additions and 369 deletions
@@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound, Identity};
use sp_runtime::TryRuntimeError;
use sp_std::prelude::*;
mod old {
mod v8 {
use super::*;
#[derive(Encode, Decode)]
@@ -50,14 +50,14 @@ mod old {
#[cfg(feature = "runtime-benchmarks")]
pub fn store_old_dummy_code<T: Config>(len: usize) {
use sp_runtime::traits::Hash;
let module = old::PrefabWasmModule {
let module = v8::PrefabWasmModule {
instruction_weights_version: 0,
initial: 0,
maximum: 0,
code: vec![42u8; len],
};
let hash = T::Hashing::hash(&module.code);
old::CodeStorage::<T>::insert(hash, module);
v8::CodeStorage::<T>::insert(hash, module);
}
#[derive(Encode, Decode)]
@@ -89,9 +89,9 @@ impl<T: Config> MigrationStep for Migration<T> {
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_key) = self.last_code_hash.take() {
old::CodeStorage::<T>::iter_from(old::CodeStorage::<T>::hashed_key_for(last_key))
v8::CodeStorage::<T>::iter_from(v8::CodeStorage::<T>::hashed_key_for(last_key))
} else {
old::CodeStorage::<T>::iter()
v8::CodeStorage::<T>::iter()
};
if let Some((key, old)) = iter.next() {
@@ -115,7 +115,7 @@ impl<T: Config> MigrationStep for Migration<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let sample: Vec<_> = old::CodeStorage::<T>::iter().take(100).collect();
let sample: Vec<_> = v8::CodeStorage::<T>::iter().take(100).collect();
log::debug!(target: LOG_TARGET, "Taking sample of {} contract codes", sample.len());
Ok(sample.encode())
@@ -123,7 +123,7 @@ impl<T: Config> MigrationStep for Migration<T> {
#[cfg(feature = "try-runtime")]
fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let sample = <Vec<(CodeHash<T>, old::PrefabWasmModule)> as Decode>::decode(&mut &state[..])
let sample = <Vec<(CodeHash<T>, v8::PrefabWasmModule)> as Decode>::decode(&mut &state[..])
.expect("pre_upgrade_step provides a valid state; qed");
log::debug!(target: LOG_TARGET, "Validating sample of {} contract codes", sample.len());
+12 -12
View File
@@ -47,7 +47,7 @@ use sp_runtime::{
};
use sp_std::prelude::*;
mod old {
mod v9 {
use super::*;
pub type BalanceOf<T, OldCurrency> = <OldCurrency as frame_support::traits::Currency<
@@ -85,7 +85,7 @@ pub fn store_old_contract_info<T: Config, OldCurrency>(
) where
OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId> + 'static,
{
let info = old::ContractInfo {
let info = v9::ContractInfo {
trie_id: info.trie_id,
code_hash: info.code_hash,
storage_bytes: Default::default(),
@@ -94,7 +94,7 @@ pub fn store_old_contract_info<T: Config, OldCurrency>(
storage_item_deposit: Default::default(),
storage_base_deposit: Default::default(),
};
old::ContractInfoOf::<T, OldCurrency>::insert(account, info);
v9::ContractInfoOf::<T, OldCurrency>::insert(account, info);
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)]
@@ -120,9 +120,9 @@ where
pub code_hash: CodeHash<T>,
storage_bytes: u32,
storage_items: u32,
pub storage_byte_deposit: old::BalanceOf<T, OldCurrency>,
storage_item_deposit: old::BalanceOf<T, OldCurrency>,
storage_base_deposit: old::BalanceOf<T, OldCurrency>,
pub storage_byte_deposit: v9::BalanceOf<T, OldCurrency>,
storage_item_deposit: v9::BalanceOf<T, OldCurrency>,
storage_base_deposit: v9::BalanceOf<T, OldCurrency>,
}
#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)]
@@ -152,7 +152,7 @@ fn deposit_address<T: Config>(
impl<T: Config, OldCurrency: 'static> MigrationStep for Migration<T, OldCurrency>
where
OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId>
+ Inspect<<T as frame_system::Config>::AccountId, Balance = old::BalanceOf<T, OldCurrency>>,
+ Inspect<<T as frame_system::Config>::AccountId, Balance = v9::BalanceOf<T, OldCurrency>>,
{
const VERSION: u16 = 10;
@@ -162,11 +162,11 @@ where
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_account) = self.last_account.take() {
old::ContractInfoOf::<T, OldCurrency>::iter_from(
old::ContractInfoOf::<T, OldCurrency>::hashed_key_for(last_account),
v9::ContractInfoOf::<T, OldCurrency>::iter_from(
v9::ContractInfoOf::<T, OldCurrency>::hashed_key_for(last_account),
)
} else {
old::ContractInfoOf::<T, OldCurrency>::iter()
v9::ContractInfoOf::<T, OldCurrency>::iter()
};
if let Some((account, contract)) = iter.next() {
@@ -276,7 +276,7 @@ where
#[cfg(feature = "try-runtime")]
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let sample: Vec<_> = old::ContractInfoOf::<T, OldCurrency>::iter().take(10).collect();
let sample: Vec<_> = v9::ContractInfoOf::<T, OldCurrency>::iter().take(10).collect();
log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len());
Ok(sample.encode())
@@ -284,7 +284,7 @@ where
#[cfg(feature = "try-runtime")]
fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let sample = <Vec<(T::AccountId, old::ContractInfo<T, OldCurrency>)> as Decode>::decode(
let sample = <Vec<(T::AccountId, v9::ContractInfo<T, OldCurrency>)> as Decode>::decode(
&mut &state[..],
)
.expect("pre_upgrade_step provides a valid state; qed");
@@ -29,7 +29,7 @@ use sp_runtime::TryRuntimeError;
use codec::{Decode, Encode};
use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound};
use sp_std::{marker::PhantomData, prelude::*};
mod old {
mod v10 {
use super::*;
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
@@ -51,11 +51,11 @@ pub struct DeletionQueueManager<T: Config> {
#[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime"))]
pub fn fill_old_queue<T: Config>(len: usize) {
let queue: Vec<old::DeletedContract> =
core::iter::repeat_with(|| old::DeletedContract { trie_id: Default::default() })
let queue: Vec<v10::DeletedContract> =
core::iter::repeat_with(|| v10::DeletedContract { trie_id: Default::default() })
.take(len)
.collect();
old::DeletionQueue::<T>::set(Some(queue));
v10::DeletionQueue::<T>::set(Some(queue));
}
#[storage_alias]
@@ -80,7 +80,7 @@ impl<T: Config> MigrationStep for Migration<T> {
}
fn step(&mut self) -> (IsFinished, Weight) {
let Some(old_queue) = old::DeletionQueue::<T>::take() else {
let Some(old_queue) = v10::DeletionQueue::<T>::take() else {
return (IsFinished::Yes, Weight::zero())
};
let len = old_queue.len();
@@ -106,7 +106,7 @@ impl<T: Config> MigrationStep for Migration<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let old_queue = old::DeletionQueue::<T>::take().unwrap_or_default();
let old_queue = v10::DeletionQueue::<T>::take().unwrap_or_default();
if old_queue.is_empty() {
let len = 10u32;
+20 -20
View File
@@ -34,7 +34,7 @@ use sp_runtime::TryRuntimeError;
use sp_runtime::{traits::Zero, FixedPointNumber, FixedU128, Saturating};
use sp_std::prelude::*;
mod old {
mod v11 {
use super::*;
pub type BalanceOf<T, OldCurrency> = <OldCurrency as frame_support::traits::Currency<
@@ -87,7 +87,7 @@ where
{
owner: AccountIdOf<T>,
#[codec(compact)]
deposit: old::BalanceOf<T, OldCurrency>,
deposit: v11::BalanceOf<T, OldCurrency>,
#[codec(compact)]
refcount: u64,
determinism: Determinism,
@@ -112,17 +112,17 @@ where
let hash = T::Hashing::hash(&code);
PristineCode::<T>::insert(hash, code.clone());
let module = old::PrefabWasmModule {
let module = v11::PrefabWasmModule {
instruction_weights_version: Default::default(),
initial: Default::default(),
maximum: Default::default(),
code,
determinism: Determinism::Enforced,
};
old::CodeStorage::<T>::insert(hash, module);
v11::CodeStorage::<T>::insert(hash, module);
let info = old::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX };
old::OwnerInfoOf::<T, OldCurrency>::insert(hash, info);
let info = v11::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX };
v11::OwnerInfoOf::<T, OldCurrency>::insert(hash, info);
}
#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)]
@@ -148,16 +148,16 @@ where
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_key) = self.last_code_hash.take() {
old::OwnerInfoOf::<T, OldCurrency>::iter_from(
old::OwnerInfoOf::<T, OldCurrency>::hashed_key_for(last_key),
v11::OwnerInfoOf::<T, OldCurrency>::iter_from(
v11::OwnerInfoOf::<T, OldCurrency>::hashed_key_for(last_key),
)
} else {
old::OwnerInfoOf::<T, OldCurrency>::iter()
v11::OwnerInfoOf::<T, OldCurrency>::iter()
};
if let Some((hash, old_info)) = iter.next() {
log::debug!(target: LOG_TARGET, "Migrating OwnerInfo for code_hash {:?}", hash);
let module = old::CodeStorage::<T>::take(hash)
let module = v11::CodeStorage::<T>::take(hash)
.expect(format!("No PrefabWasmModule found for code_hash: {:?}", hash).as_str());
let code_len = module.code.len();
@@ -184,7 +184,7 @@ where
let bytes_before = module
.encoded_size()
.saturating_add(code_len)
.saturating_add(old::OwnerInfo::<T, OldCurrency>::max_encoded_len())
.saturating_add(v11::OwnerInfo::<T, OldCurrency>::max_encoded_len())
as u32;
let items_before = 3u32;
let deposit_expected_before = price_per_byte
@@ -241,10 +241,10 @@ where
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let len = 100;
log::debug!(target: LOG_TARGET, "Taking sample of {} OwnerInfo(s)", len);
let sample: Vec<_> = old::OwnerInfoOf::<T, OldCurrency>::iter()
let sample: Vec<_> = v11::OwnerInfoOf::<T, OldCurrency>::iter()
.take(len)
.map(|(k, v)| {
let module = old::CodeStorage::<T>::get(k)
let module = v11::CodeStorage::<T>::get(k)
.expect("No PrefabWasmModule found for code_hash: {:?}");
let info: CodeInfo<T, OldCurrency> = CodeInfo {
determinism: module.determinism,
@@ -258,9 +258,9 @@ where
.collect();
let storage: u32 =
old::CodeStorage::<T>::iter().map(|(_k, v)| v.encoded_size() as u32).sum();
let mut deposit: old::BalanceOf<T, OldCurrency> = Default::default();
old::OwnerInfoOf::<T, OldCurrency>::iter().for_each(|(_k, v)| deposit += v.deposit);
v11::CodeStorage::<T>::iter().map(|(_k, v)| v.encoded_size() as u32).sum();
let mut deposit: v11::BalanceOf<T, OldCurrency> = Default::default();
v11::OwnerInfoOf::<T, OldCurrency>::iter().for_each(|(_k, v)| deposit += v.deposit);
Ok((sample, deposit, storage).encode())
}
@@ -269,7 +269,7 @@ where
fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let state = <(
Vec<(CodeHash<T>, CodeInfo<T, OldCurrency>)>,
old::BalanceOf<T, OldCurrency>,
v11::BalanceOf<T, OldCurrency>,
u32,
) as Decode>::decode(&mut &state[..])
.unwrap();
@@ -283,7 +283,7 @@ where
ensure!(info.refcount == old.refcount, "invalid refcount");
}
if let Some((k, _)) = old::CodeStorage::<T>::iter().next() {
if let Some((k, _)) = v11::CodeStorage::<T>::iter().next() {
log::warn!(
target: LOG_TARGET,
"CodeStorage is still NOT empty, found code_hash: {:?}",
@@ -292,7 +292,7 @@ where
} else {
log::debug!(target: LOG_TARGET, "CodeStorage is empty.");
}
if let Some((k, _)) = old::OwnerInfoOf::<T, OldCurrency>::iter().next() {
if let Some((k, _)) = v11::OwnerInfoOf::<T, OldCurrency>::iter().next() {
log::warn!(
target: LOG_TARGET,
"OwnerInfoOf is still NOT empty, found code_hash: {:?}",
@@ -302,7 +302,7 @@ where
log::debug!(target: LOG_TARGET, "OwnerInfoOf is empty.");
}
let mut deposit: old::BalanceOf<T, OldCurrency> = Default::default();
let mut deposit: v11::BalanceOf<T, OldCurrency> = Default::default();
let mut items = 0u32;
let mut storage_info = 0u32;
CodeInfoOf::<T, OldCurrency>::iter().for_each(|(_k, v)| {
@@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound};
use sp_runtime::BoundedBTreeMap;
use sp_std::prelude::*;
mod old {
mod v12 {
use super::*;
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
@@ -59,7 +59,7 @@ pub fn store_old_contract_info<T: Config>(account: T::AccountId, info: crate::Co
let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash);
let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
.expect("infinite length input; no invalid inputs for type; qed");
let info = old::ContractInfo {
let info = v12::ContractInfo {
trie_id: info.trie_id.clone(),
deposit_account,
code_hash: info.code_hash,
@@ -69,7 +69,7 @@ pub fn store_old_contract_info<T: Config>(account: T::AccountId, info: crate::Co
storage_item_deposit: Default::default(),
storage_base_deposit: Default::default(),
};
old::ContractInfoOf::<T>::insert(account, info);
v12::ContractInfoOf::<T>::insert(account, info);
}
#[storage_alias]
@@ -104,11 +104,11 @@ impl<T: Config> MigrationStep for Migration<T> {
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_account) = self.last_account.take() {
old::ContractInfoOf::<T>::iter_from(old::ContractInfoOf::<T>::hashed_key_for(
v12::ContractInfoOf::<T>::iter_from(v12::ContractInfoOf::<T>::hashed_key_for(
last_account,
))
} else {
old::ContractInfoOf::<T>::iter()
v12::ContractInfoOf::<T>::iter()
};
if let Some((key, old)) = iter.next() {
+10 -10
View File
@@ -44,7 +44,7 @@ use sp_runtime::{traits::Zero, Saturating};
#[cfg(feature = "try-runtime")]
use sp_std::collections::btree_map::BTreeMap;
mod old {
mod v13 {
use super::*;
pub type BalanceOf<T, OldCurrency> = <OldCurrency as frame_support::traits::Currency<
@@ -61,7 +61,7 @@ mod old {
{
pub owner: AccountIdOf<T>,
#[codec(compact)]
pub deposit: old::BalanceOf<T, OldCurrency>,
pub deposit: v13::BalanceOf<T, OldCurrency>,
#[codec(compact)]
pub refcount: u64,
pub determinism: Determinism,
@@ -86,14 +86,14 @@ where
let code = vec![42u8; len as usize];
let hash = T::Hashing::hash(&code);
let info = old::CodeInfo {
let info = v13::CodeInfo {
owner: account,
deposit: 10_000u32.into(),
refcount: u64::MAX,
determinism: Determinism::Enforced,
code_len: len,
};
old::CodeInfoOf::<T, OldCurrency>::insert(hash, info);
v13::CodeInfoOf::<T, OldCurrency>::insert(hash, info);
}
#[cfg(feature = "try-runtime")]
@@ -105,9 +105,9 @@ where
OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId>,
{
/// Total reserved balance as code upload deposit for the owner.
reserved: old::BalanceOf<T, OldCurrency>,
reserved: v13::BalanceOf<T, OldCurrency>,
/// Total balance of the owner.
total: old::BalanceOf<T, OldCurrency>,
total: v13::BalanceOf<T, OldCurrency>,
}
#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)]
@@ -134,11 +134,11 @@ where
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_hash) = self.last_code_hash.take() {
old::CodeInfoOf::<T, OldCurrency>::iter_from(
old::CodeInfoOf::<T, OldCurrency>::hashed_key_for(last_hash),
v13::CodeInfoOf::<T, OldCurrency>::iter_from(
v13::CodeInfoOf::<T, OldCurrency>::hashed_key_for(last_hash),
)
} else {
old::CodeInfoOf::<T, OldCurrency>::iter()
v13::CodeInfoOf::<T, OldCurrency>::iter()
};
if let Some((hash, code_info)) = iter.next() {
@@ -194,7 +194,7 @@ where
#[cfg(feature = "try-runtime")]
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let info: Vec<_> = old::CodeInfoOf::<T, OldCurrency>::iter().collect();
let info: Vec<_> = v13::CodeInfoOf::<T, OldCurrency>::iter().collect();
let mut owner_balance_allocation =
BTreeMap::<AccountIdOf<T>, BalanceAllocation<T, OldCurrency>>::new();
@@ -46,7 +46,7 @@ use sp_runtime::{traits::Zero, Saturating};
#[cfg(feature = "try-runtime")]
use sp_std::vec::Vec;
mod old {
mod v14 {
use super::*;
#[derive(
@@ -81,7 +81,7 @@ pub fn store_old_contract_info<T: Config>(account: T::AccountId, info: crate::Co
let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash);
let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
.expect("infinite length input; no invalid inputs for type; qed");
let info = old::ContractInfo {
let info = v14::ContractInfo {
trie_id: info.trie_id.clone(),
deposit_account,
code_hash: info.code_hash,
@@ -92,7 +92,7 @@ pub fn store_old_contract_info<T: Config>(account: T::AccountId, info: crate::Co
storage_base_deposit: info.storage_base_deposit(),
delegate_dependencies: info.delegate_dependencies().clone(),
};
old::ContractInfoOf::<T>::insert(account, info);
v14::ContractInfoOf::<T>::insert(account, info);
}
#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
@@ -127,11 +127,11 @@ impl<T: Config> MigrationStep for Migration<T> {
fn step(&mut self) -> (IsFinished, Weight) {
let mut iter = if let Some(last_account) = self.last_account.take() {
old::ContractInfoOf::<T>::iter_from(old::ContractInfoOf::<T>::hashed_key_for(
v14::ContractInfoOf::<T>::iter_from(v14::ContractInfoOf::<T>::hashed_key_for(
last_account,
))
} else {
old::ContractInfoOf::<T>::iter()
v14::ContractInfoOf::<T>::iter()
};
if let Some((account, old_contract)) = iter.next() {
@@ -243,11 +243,11 @@ impl<T: Config> MigrationStep for Migration<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
let sample: Vec<_> = old::ContractInfoOf::<T>::iter().take(100).collect();
let sample: Vec<_> = v14::ContractInfoOf::<T>::iter().take(100).collect();
log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len());
let state: Vec<(T::AccountId, old::ContractInfo<T>, BalanceOf<T>, BalanceOf<T>)> = sample
let state: Vec<(T::AccountId, v14::ContractInfo<T>, BalanceOf<T>, BalanceOf<T>)> = sample
.iter()
.map(|(account, contract)| {
(
@@ -265,7 +265,7 @@ impl<T: Config> MigrationStep for Migration<T> {
#[cfg(feature = "try-runtime")]
fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let sample =
<Vec<(T::AccountId, old::ContractInfo<T>, BalanceOf<T>, BalanceOf<T>)> as Decode>::decode(
<Vec<(T::AccountId, v14::ContractInfo<T>, BalanceOf<T>, BalanceOf<T>)> as Decode>::decode(
&mut &state[..],
)
.expect("pre_upgrade_step provides a valid state; qed");