mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 05:51:02 +00:00
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:
@@ -555,6 +555,42 @@ pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
|
||||
tt_macro::create_tt_return_macro(input)
|
||||
}
|
||||
|
||||
/// Allows accessing on-chain pallet storage that is no longer accessible via the pallet.
|
||||
///
|
||||
/// This is especially useful when writing storage migrations, when types of storage items are
|
||||
/// modified or outright removed, but the previous definition is required to perform the migration.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// Imagine a pallet with the following storage definition:
|
||||
/// ```ignore
|
||||
/// #[pallet::storage]
|
||||
/// pub type Value<T: Config> = StorageValue<_, u32>;
|
||||
/// ```
|
||||
/// `Value` can be accessed by calling `Value::<T>::get()`.
|
||||
///
|
||||
/// Now imagine the definition of `Value` is updated to a `(u32, u32)`:
|
||||
/// ```ignore
|
||||
/// #[pallet::storage]
|
||||
/// pub type Value<T: Config> = StorageValue<_, (u32, u32)>;
|
||||
/// ```
|
||||
/// The on-chain value of `Value` is `u32`, but `Value::<T>::get()` expects it to be `(u32, u32)`.
|
||||
///
|
||||
/// In this instance the developer must write a storage migration to reading the old value of
|
||||
/// `Value` and writing it back to storage in the new format, so that the on-chain storage layout is
|
||||
/// consistent with what is defined in the pallet.
|
||||
///
|
||||
/// We can read the old v0 value of `Value` in the migration by creating a `storage_alias`:
|
||||
/// ```ignore
|
||||
/// pub(crate) mod v0 {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[storage_alias]
|
||||
/// pub type Value<T: crate::Config> = StorageValue<crate::Pallet<T>, u32>;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The developer can now access the old value of `Value` by calling `v0::Value::<T>::get()`.
|
||||
#[proc_macro_attribute]
|
||||
pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream {
|
||||
storage_alias::storage_alias(attributes.into(), input.into())
|
||||
@@ -1058,7 +1094,7 @@ pub fn generate_store(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
/// pub struct Pallet<T>(_);
|
||||
/// ```
|
||||
///
|
||||
/// If not present, the current storage version is set to the default value.
|
||||
/// If not present, the in-code storage version is set to the default value.
|
||||
#[proc_macro_attribute]
|
||||
pub fn storage_version(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
pallet_macro_stub()
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
>::name::<Self>().unwrap_or("<unknown pallet name>")
|
||||
};
|
||||
|
||||
let initialize_on_chain_storage_version = if let Some(current_version) =
|
||||
let initialize_on_chain_storage_version = if let Some(in_code_version) =
|
||||
&def.pallet_struct.storage_version
|
||||
{
|
||||
quote::quote! {
|
||||
@@ -50,9 +50,9 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
target: #frame_support::LOG_TARGET,
|
||||
"🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}",
|
||||
#pallet_name,
|
||||
#current_version
|
||||
#in_code_version
|
||||
);
|
||||
#current_version.put::<Self>();
|
||||
#in_code_version.put::<Self>();
|
||||
}
|
||||
} else {
|
||||
quote::quote! {
|
||||
@@ -73,10 +73,10 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
#frame_support::__private::log::info!(
|
||||
target: #frame_support::LOG_TARGET,
|
||||
"⚠️ {} declares internal migrations (which *might* execute). \
|
||||
On-chain `{:?}` vs current storage version `{:?}`",
|
||||
On-chain `{:?}` vs in-code storage version `{:?}`",
|
||||
#pallet_name,
|
||||
<Self as #frame_support::traits::GetStorageVersion>::on_chain_storage_version(),
|
||||
<Self as #frame_support::traits::GetStorageVersion>::current_storage_version(),
|
||||
<Self as #frame_support::traits::GetStorageVersion>::in_code_storage_version(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -102,23 +102,23 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
};
|
||||
|
||||
// If a storage version is set, we should ensure that the storage version on chain matches the
|
||||
// current storage version. This assumes that `Executive` is running custom migrations before
|
||||
// in-code storage version. This assumes that `Executive` is running custom migrations before
|
||||
// the pallets are called.
|
||||
let post_storage_version_check = if def.pallet_struct.storage_version.is_some() {
|
||||
quote::quote! {
|
||||
let on_chain_version = <Self as #frame_support::traits::GetStorageVersion>::on_chain_storage_version();
|
||||
let current_version = <Self as #frame_support::traits::GetStorageVersion>::current_storage_version();
|
||||
let in_code_version = <Self as #frame_support::traits::GetStorageVersion>::in_code_storage_version();
|
||||
|
||||
if on_chain_version != current_version {
|
||||
if on_chain_version != in_code_version {
|
||||
#frame_support::__private::log::error!(
|
||||
target: #frame_support::LOG_TARGET,
|
||||
"{}: On chain storage version {:?} doesn't match current storage version {:?}.",
|
||||
"{}: On chain storage version {:?} doesn't match in-code storage version {:?}.",
|
||||
#pallet_name,
|
||||
on_chain_version,
|
||||
current_version,
|
||||
in_code_version,
|
||||
);
|
||||
|
||||
return Err("On chain and current storage version do not match. Missing runtime upgrade?".into());
|
||||
return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -160,7 +160,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
}
|
||||
);
|
||||
|
||||
let (storage_version, current_storage_version_ty) =
|
||||
let (storage_version, in_code_storage_version_ty) =
|
||||
if let Some(v) = def.pallet_struct.storage_version.as_ref() {
|
||||
(quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion })
|
||||
} else {
|
||||
@@ -203,9 +203,9 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
for #pallet_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
type CurrentStorageVersion = #current_storage_version_ty;
|
||||
type InCodeStorageVersion = #in_code_storage_version_ty;
|
||||
|
||||
fn current_storage_version() -> Self::CurrentStorageVersion {
|
||||
fn in_code_storage_version() -> Self::InCodeStorageVersion {
|
||||
#storage_version
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ pub struct PalletStructDef {
|
||||
/// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`.
|
||||
/// Contains the span of the attribute.
|
||||
pub without_storage_info: Option<proc_macro2::Span>,
|
||||
/// The current storage version of the pallet.
|
||||
/// The in-code storage version of the pallet.
|
||||
pub storage_version: Option<syn::Path>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1178,7 +1178,7 @@ pub mod pallet_prelude {
|
||||
/// # `pallet::storage_version`
|
||||
///
|
||||
/// Because the [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) macro
|
||||
/// implements [`traits::GetStorageVersion`], the current storage version needs to be
|
||||
/// implements [`traits::GetStorageVersion`], the in-code storage version needs to be
|
||||
/// communicated to the macro. This can be done by using the `pallet::storage_version`
|
||||
/// attribute:
|
||||
///
|
||||
@@ -1190,7 +1190,7 @@ pub mod pallet_prelude {
|
||||
/// pub struct Pallet<T>(_);
|
||||
/// ```
|
||||
///
|
||||
/// If not present, the current storage version is set to the default value.
|
||||
/// If not present, the in-code storage version is set to the default value.
|
||||
///
|
||||
/// Also see [`pallet::storage_version`](`frame_support::pallet_macros::storage_version`)
|
||||
///
|
||||
|
||||
@@ -43,12 +43,30 @@ use sp_std::marker::PhantomData;
|
||||
/// Otherwise, a warning is logged notifying the developer that the upgrade was a noop and should
|
||||
/// probably be removed.
|
||||
///
|
||||
/// It is STRONGLY RECOMMENDED to write the unversioned migration logic in a private module and
|
||||
/// only export the versioned migration logic to prevent accidentally using the unversioned
|
||||
/// migration in any runtimes.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```ignore
|
||||
/// // In file defining migrations
|
||||
/// pub struct VersionUncheckedMigrateV5ToV6<T>(sp_std::marker::PhantomData<T>);
|
||||
/// impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> {
|
||||
/// // OnRuntimeUpgrade implementation...
|
||||
///
|
||||
/// /// Private module containing *version unchecked* migration logic.
|
||||
/// ///
|
||||
/// /// Should only be used by the [`VersionedMigration`] type in this module to create something to
|
||||
/// /// export.
|
||||
/// ///
|
||||
/// /// We keep this private so the unversioned migration cannot accidentally be used in any runtimes.
|
||||
/// ///
|
||||
/// /// For more about this pattern of keeping items private, see
|
||||
/// /// - https://github.com/rust-lang/rust/issues/30905
|
||||
/// /// - https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40
|
||||
/// mod version_unchecked {
|
||||
/// use super::*;
|
||||
/// pub struct MigrateV5ToV6<T>(sp_std::marker::PhantomData<T>);
|
||||
/// impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> {
|
||||
/// // OnRuntimeUpgrade implementation...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub type MigrateV5ToV6<T, I> =
|
||||
@@ -91,7 +109,7 @@ impl<
|
||||
const FROM: u16,
|
||||
const TO: u16,
|
||||
Inner: crate::traits::OnRuntimeUpgrade,
|
||||
Pallet: GetStorageVersion<CurrentStorageVersion = StorageVersion> + PalletInfoAccess,
|
||||
Pallet: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess,
|
||||
DbWeight: Get<RuntimeDbWeight>,
|
||||
> crate::traits::OnRuntimeUpgrade for VersionedMigration<FROM, TO, Inner, Pallet, DbWeight>
|
||||
{
|
||||
@@ -163,25 +181,25 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
/// Can store the current pallet version in storage.
|
||||
pub trait StoreCurrentStorageVersion<T: GetStorageVersion + PalletInfoAccess> {
|
||||
/// Write the current storage version to the storage.
|
||||
fn store_current_storage_version();
|
||||
/// Can store the in-code pallet version on-chain.
|
||||
pub trait StoreInCodeStorageVersion<T: GetStorageVersion + PalletInfoAccess> {
|
||||
/// Write the in-code storage version on-chain.
|
||||
fn store_in_code_storage_version();
|
||||
}
|
||||
|
||||
impl<T: GetStorageVersion<CurrentStorageVersion = StorageVersion> + PalletInfoAccess>
|
||||
StoreCurrentStorageVersion<T> for StorageVersion
|
||||
impl<T: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess>
|
||||
StoreInCodeStorageVersion<T> for StorageVersion
|
||||
{
|
||||
fn store_current_storage_version() {
|
||||
let version = <T as GetStorageVersion>::current_storage_version();
|
||||
fn store_in_code_storage_version() {
|
||||
let version = <T as GetStorageVersion>::in_code_storage_version();
|
||||
version.put::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetStorageVersion<CurrentStorageVersion = NoStorageVersionSet> + PalletInfoAccess>
|
||||
StoreCurrentStorageVersion<T> for NoStorageVersionSet
|
||||
impl<T: GetStorageVersion<InCodeStorageVersion = NoStorageVersionSet> + PalletInfoAccess>
|
||||
StoreInCodeStorageVersion<T> for NoStorageVersionSet
|
||||
{
|
||||
fn store_current_storage_version() {
|
||||
fn store_in_code_storage_version() {
|
||||
StorageVersion::default().put::<T>();
|
||||
}
|
||||
}
|
||||
@@ -193,7 +211,7 @@ pub trait PalletVersionToStorageVersionHelper {
|
||||
|
||||
impl<T: GetStorageVersion + PalletInfoAccess> PalletVersionToStorageVersionHelper for T
|
||||
where
|
||||
T::CurrentStorageVersion: StoreCurrentStorageVersion<T>,
|
||||
T::InCodeStorageVersion: StoreInCodeStorageVersion<T>,
|
||||
{
|
||||
fn migrate(db_weight: &RuntimeDbWeight) -> Weight {
|
||||
const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:";
|
||||
@@ -204,8 +222,7 @@ where
|
||||
|
||||
sp_io::storage::clear(&pallet_version_key(<T as PalletInfoAccess>::name()));
|
||||
|
||||
<T::CurrentStorageVersion as StoreCurrentStorageVersion<T>>::store_current_storage_version(
|
||||
);
|
||||
<T::InCodeStorageVersion as StoreInCodeStorageVersion<T>>::store_in_code_storage_version();
|
||||
|
||||
db_weight.writes(2)
|
||||
}
|
||||
@@ -226,7 +243,7 @@ impl PalletVersionToStorageVersionHelper for T {
|
||||
|
||||
/// Migrate from the `PalletVersion` struct to the new [`StorageVersion`] struct.
|
||||
///
|
||||
/// This will remove all `PalletVersion's` from the state and insert the current storage version.
|
||||
/// This will remove all `PalletVersion's` from the state and insert the in-code storage version.
|
||||
pub fn migrate_from_pallet_version_to_storage_version<
|
||||
Pallets: PalletVersionToStorageVersionHelper,
|
||||
>(
|
||||
|
||||
@@ -90,7 +90,7 @@ impl<BlockNumber: Copy + AtLeast32BitUnsigned> OnIdle<BlockNumber> for Tuple {
|
||||
///
|
||||
/// Implementing this trait for a pallet let's you express operations that should
|
||||
/// happen at genesis. It will be called in an externalities provided environment and
|
||||
/// will see the genesis state after all pallets have written their genesis state.
|
||||
/// will set the genesis state after all pallets have written their genesis state.
|
||||
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
|
||||
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
|
||||
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
|
||||
@@ -306,19 +306,23 @@ pub trait IntegrityTest {
|
||||
/// end
|
||||
/// ```
|
||||
///
|
||||
/// * `OnRuntimeUpgrade` is only executed before everything else if a code
|
||||
/// * `OnRuntimeUpgrade` is mandatorily at the beginning of the block body (extrinsics) being
|
||||
/// processed. change is detected.
|
||||
/// * Extrinsics start with inherents, and continue with other signed or unsigned extrinsics.
|
||||
/// * `OnIdle` optionally comes after extrinsics.
|
||||
/// `OnFinalize` mandatorily comes after `OnIdle`.
|
||||
/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are only executed when a code change is
|
||||
/// detected.
|
||||
/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are mandatorily executed at the very
|
||||
/// beginning of the block body, before any extrinsics are processed.
|
||||
/// * [`Inherents`](sp_inherents) are always executed before any other other signed or unsigned
|
||||
/// extrinsics.
|
||||
/// * [`OnIdle`](Hooks::OnIdle) hooks are executed after extrinsics if there is weight remaining in
|
||||
/// the block.
|
||||
/// * [`OnFinalize`](Hooks::OnFinalize) hooks are mandatorily executed after
|
||||
/// [`OnIdle`](Hooks::OnIdle).
|
||||
///
|
||||
/// > `OffchainWorker` is not part of this flow, as it is not really part of the consensus/main
|
||||
/// > block import path, and is called optionally, and in other circumstances. See
|
||||
/// > [`crate::traits::misc::OffchainWorker`] for more information.
|
||||
/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) hooks are not part of this flow,
|
||||
/// > because they are not part of the consensus/main block building logic. See
|
||||
/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) for more information.
|
||||
///
|
||||
/// To learn more about the execution of hooks see `frame-executive` as this component is is charge
|
||||
/// of dispatching extrinsics and placing the hooks in the correct order.
|
||||
/// To learn more about the execution of hooks see the FRAME `Executive` pallet which is in charge
|
||||
/// of dispatching extrinsics and calling hooks in the correct order.
|
||||
pub trait Hooks<BlockNumber> {
|
||||
/// Block initialization hook. This is called at the very beginning of block execution.
|
||||
///
|
||||
@@ -370,30 +374,38 @@ pub trait Hooks<BlockNumber> {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
/// Hook executed when a code change (aka. a "runtime upgrade") is detected by FRAME.
|
||||
/// Hook executed when a code change (aka. a "runtime upgrade") is detected by the FRAME
|
||||
/// `Executive` pallet.
|
||||
///
|
||||
/// Be aware that this is called before [`Hooks::on_initialize`] of any pallet; therefore, a lot
|
||||
/// of the critical storage items such as `block_number` in system pallet might have not been
|
||||
/// set.
|
||||
/// set yet.
|
||||
///
|
||||
/// Vert similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST
|
||||
/// execute. Use with care.
|
||||
/// Similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST execute.
|
||||
/// It is strongly recommended to dry-run the execution of these hooks using
|
||||
/// [try-runtime-cli](https://github.com/paritytech/try-runtime-cli) to ensure they will not
|
||||
/// produce and overweight block which can brick your chain. Use with care!
|
||||
///
|
||||
/// ## Implementation Note: Versioning
|
||||
/// ## Implementation Note: Standalone Migrations
|
||||
///
|
||||
/// 1. An implementation of this should typically follow a pattern where the version of the
|
||||
/// pallet is checked against the onchain version, and a decision is made about what needs to be
|
||||
/// done. This is helpful to prevent accidental repetitive execution of this hook, which can be
|
||||
/// catastrophic.
|
||||
/// Additional migrations can be created by directly implementing [`OnRuntimeUpgrade`] on
|
||||
/// structs and passing them to `Executive`.
|
||||
///
|
||||
/// Alternatively, [`frame_support::migrations::VersionedMigration`] can be used to assist with
|
||||
/// this.
|
||||
/// ## Implementation Note: Pallet Versioning
|
||||
///
|
||||
/// ## Implementation Note: Runtime Level Migration
|
||||
/// Implementations of this hook are typically wrapped in
|
||||
/// [`crate::migrations::VersionedMigration`] to ensure the migration is executed exactly
|
||||
/// once and only when it is supposed to.
|
||||
///
|
||||
/// Additional "upgrade hooks" can be created by pallets by a manual implementation of
|
||||
/// [`Hooks::on_runtime_upgrade`] which can be passed on to `Executive` at the top level
|
||||
/// runtime.
|
||||
/// Alternatively, developers can manually implement version checks.
|
||||
///
|
||||
/// Failure to adequately check storage versions can result in accidental repetitive execution
|
||||
/// of the hook, which can be catastrophic.
|
||||
///
|
||||
/// ## Implementation Note: Weight
|
||||
///
|
||||
/// Typically, implementations of this method are simple enough that weights can be calculated
|
||||
/// manually. However, if required, a benchmark can also be used.
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
@@ -403,7 +415,7 @@ pub trait Hooks<BlockNumber> {
|
||||
/// It should focus on certain checks to ensure that the state is sensible. This is never
|
||||
/// executed in a consensus code-path, therefore it can consume as much weight as it needs.
|
||||
///
|
||||
/// This hook should not alter any storage.
|
||||
/// This hook must not alter any storage.
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> {
|
||||
Ok(())
|
||||
@@ -415,7 +427,7 @@ pub trait Hooks<BlockNumber> {
|
||||
/// which will be passed to `post_upgrade` after upgrading for post-check. An empty vector
|
||||
/// should be returned if there is no such need.
|
||||
///
|
||||
/// This hook is never meant to be executed on-chain but is meant to be used by testing tools.
|
||||
/// This hook is never executed on-chain but instead used by testing tools.
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
|
||||
@@ -261,41 +261,64 @@ impl Add<u16> for StorageVersion {
|
||||
}
|
||||
}
|
||||
|
||||
/// Special marker struct if no storage version is set for a pallet.
|
||||
/// Special marker struct used when [`storage_version`](crate::pallet_macros::storage_version) is
|
||||
/// not defined for a pallet.
|
||||
///
|
||||
/// If you (the reader) end up here, it probably means that you tried to compare
|
||||
/// [`GetStorageVersion::on_chain_storage_version`] against
|
||||
/// [`GetStorageVersion::current_storage_version`]. This basically means that the
|
||||
/// [`storage_version`](crate::pallet_macros::storage_version) is missing in the pallet where the
|
||||
/// mentioned functions are being called.
|
||||
/// [`GetStorageVersion::in_code_storage_version`]. This basically means that the
|
||||
/// [`storage_version`](crate::pallet_macros::storage_version) is missing from the pallet where the
|
||||
/// mentioned functions are being called, and needs to be defined.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NoStorageVersionSet;
|
||||
|
||||
/// Provides information about the storage version of a pallet.
|
||||
/// Provides information about a pallet's storage versions.
|
||||
///
|
||||
/// It differentiates between current and on-chain storage version. Both should be only out of sync
|
||||
/// when a new runtime upgrade was applied and the runtime migrations did not yet executed.
|
||||
/// Otherwise it means that the pallet works with an unsupported storage version and unforeseen
|
||||
/// stuff can happen.
|
||||
/// Every pallet has two storage versions:
|
||||
/// 1. An in-code storage version
|
||||
/// 2. An on-chain storage version
|
||||
///
|
||||
/// The current storage version is the version of the pallet as supported at runtime. The active
|
||||
/// storage version is the version of the pallet in the storage.
|
||||
/// The in-code storage version is the version of the pallet as defined in the runtime blob, and the
|
||||
/// on-chain storage version is the version of the pallet stored on-chain.
|
||||
///
|
||||
/// It is required to update the on-chain storage version manually when a migration was applied.
|
||||
/// Storage versions should be only ever be out of sync when a pallet has been updated to a new
|
||||
/// version and the in-code version is incremented, but the migration has not yet been executed
|
||||
/// on-chain as part of a runtime upgrade.
|
||||
///
|
||||
/// It is the responsibility of the developer to ensure that the on-chain storage version is set
|
||||
/// correctly during a migration so that it matches the in-code storage version.
|
||||
pub trait GetStorageVersion {
|
||||
/// This will be filled out by the [`pallet`](crate::pallet) macro.
|
||||
/// This type is generated by the [`pallet`](crate::pallet) macro.
|
||||
///
|
||||
/// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't given
|
||||
/// this is set to [`NoStorageVersionSet`] to inform the user that the attribute is missing.
|
||||
/// This should prevent that the user forgets to set a storage version when required. However,
|
||||
/// this will only work when the user actually tries to call [`Self::current_storage_version`]
|
||||
/// to compare it against the [`Self::on_chain_storage_version`]. If the attribute is given,
|
||||
/// this will be set to [`StorageVersion`].
|
||||
type CurrentStorageVersion;
|
||||
/// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't specified,
|
||||
/// this is set to [`NoStorageVersionSet`] to signify that it is missing.
|
||||
///
|
||||
/// If the [`storage_version`](crate::pallet_macros::storage_version) attribute is specified,
|
||||
/// this is be set to a [`StorageVersion`] corresponding to the attribute.
|
||||
///
|
||||
/// The intention of using [`NoStorageVersionSet`] instead of defaulting to a [`StorageVersion`]
|
||||
/// of zero is to prevent developers from forgetting to set
|
||||
/// [`storage_version`](crate::pallet_macros::storage_version) when it is required, like in the
|
||||
/// case that they wish to compare the in-code storage version to the on-chain storage version.
|
||||
type InCodeStorageVersion;
|
||||
|
||||
/// Returns the current storage version as supported by the pallet.
|
||||
fn current_storage_version() -> Self::CurrentStorageVersion;
|
||||
/// Returns the on-chain storage version of the pallet as stored in the storage.
|
||||
#[deprecated(
|
||||
note = "This method has been renamed to `in_code_storage_version` and will be removed after March 2024."
|
||||
)]
|
||||
/// DEPRECATED: Use [`Self::current_storage_version`] instead.
|
||||
///
|
||||
/// Returns the in-code storage version as specified in the
|
||||
/// [`storage_version`](crate::pallet_macros::storage_version) attribute, or
|
||||
/// [`NoStorageVersionSet`] if the attribute is missing.
|
||||
fn current_storage_version() -> Self::InCodeStorageVersion {
|
||||
Self::in_code_storage_version()
|
||||
}
|
||||
|
||||
/// Returns the in-code storage version as specified in the
|
||||
/// [`storage_version`](crate::pallet_macros::storage_version) attribute, or
|
||||
/// [`NoStorageVersionSet`] if the attribute is missing.
|
||||
fn in_code_storage_version() -> Self::InCodeStorageVersion;
|
||||
/// Returns the storage version of the pallet as last set in the actual on-chain storage.
|
||||
fn on_chain_storage_version() -> StorageVersion;
|
||||
}
|
||||
|
||||
|
||||
@@ -683,7 +683,7 @@ fn test_metadata() {
|
||||
name: "Version",
|
||||
ty: meta_type::<RuntimeVersion>(),
|
||||
value: RuntimeVersion::default().encode(),
|
||||
docs: maybe_docs(vec![ " Get the chain's current version."]),
|
||||
docs: maybe_docs(vec![ " Get the chain's in-code version."]),
|
||||
},
|
||||
PalletConstantMetadata {
|
||||
name: "SS58Prefix",
|
||||
|
||||
@@ -590,7 +590,7 @@ pub mod pallet2 {
|
||||
Self::deposit_event(Event::Something(31));
|
||||
|
||||
if UpdateStorageVersion::get() {
|
||||
Self::current_storage_version().put::<Self>();
|
||||
Self::in_code_storage_version().put::<Self>();
|
||||
}
|
||||
|
||||
Weight::zero()
|
||||
@@ -1310,7 +1310,7 @@ fn pallet_on_genesis() {
|
||||
assert_eq!(pallet::Pallet::<Runtime>::on_chain_storage_version(), StorageVersion::new(0));
|
||||
pallet::Pallet::<Runtime>::on_genesis();
|
||||
assert_eq!(
|
||||
pallet::Pallet::<Runtime>::current_storage_version(),
|
||||
pallet::Pallet::<Runtime>::in_code_storage_version(),
|
||||
pallet::Pallet::<Runtime>::on_chain_storage_version(),
|
||||
);
|
||||
})
|
||||
@@ -2257,10 +2257,10 @@ fn pallet_on_chain_storage_version_initializes_correctly() {
|
||||
AllPalletsWithSystem,
|
||||
>;
|
||||
|
||||
// Simple example of a pallet with current version 10 being added to the runtime for the first
|
||||
// Simple example of a pallet with in-code version 10 being added to the runtime for the first
|
||||
// time.
|
||||
TestExternalities::default().execute_with(|| {
|
||||
let current_version = Example::current_storage_version();
|
||||
let in_code_version = Example::in_code_storage_version();
|
||||
|
||||
// Check the pallet has no storage items set.
|
||||
let pallet_hashed_prefix = twox_128(Example::name().as_bytes());
|
||||
@@ -2271,14 +2271,14 @@ fn pallet_on_chain_storage_version_initializes_correctly() {
|
||||
// version.
|
||||
Executive::execute_on_runtime_upgrade();
|
||||
|
||||
// Check that the storage version was initialized to the current version
|
||||
// Check that the storage version was initialized to the in-code version
|
||||
let on_chain_version_after = StorageVersion::get::<Example>();
|
||||
assert_eq!(on_chain_version_after, current_version);
|
||||
assert_eq!(on_chain_version_after, in_code_version);
|
||||
});
|
||||
|
||||
// Pallet with no current storage version should have the on-chain version initialized to 0.
|
||||
// Pallet with no in-code storage version should have the on-chain version initialized to 0.
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Example4 current_storage_version is NoStorageVersionSet.
|
||||
// Example4 in_code_storage_version is NoStorageVersionSet.
|
||||
|
||||
// Check the pallet has no storage items set.
|
||||
let pallet_hashed_prefix = twox_128(Example4::name().as_bytes());
|
||||
@@ -2308,7 +2308,7 @@ fn post_runtime_upgrade_detects_storage_version_issues() {
|
||||
|
||||
impl OnRuntimeUpgrade for CustomUpgrade {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
Example2::current_storage_version().put::<Example2>();
|
||||
Example2::in_code_storage_version().put::<Example2>();
|
||||
|
||||
Default::default()
|
||||
}
|
||||
@@ -2351,14 +2351,14 @@ fn post_runtime_upgrade_detects_storage_version_issues() {
|
||||
>;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Set the on-chain version to one less than the current version for `Example`, simulating a
|
||||
// Set the on-chain version to one less than the in-code version for `Example`, simulating a
|
||||
// forgotten migration
|
||||
StorageVersion::new(9).put::<Example2>();
|
||||
|
||||
// The version isn't changed, we should detect it.
|
||||
assert!(
|
||||
Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() ==
|
||||
"On chain and current storage version do not match. Missing runtime upgrade?"
|
||||
"On chain and in-code storage version do not match. Missing runtime upgrade?"
|
||||
.into()
|
||||
);
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ mod pallet {
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
if Self::current_storage_version() != Self::on_chain_storage_version() {
|
||||
if Self::in_code_storage_version() != Self::on_chain_storage_version() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
error[E0369]: binary operation `!=` cannot be applied to type `NoStorageVersionSet`
|
||||
--> tests/pallet_ui/compare_unset_storage_version.rs:32:39
|
||||
|
|
||||
32 | if Self::current_storage_version() != Self::on_chain_storage_version() {
|
||||
32 | if Self::in_code_storage_version() != Self::on_chain_storage_version() {
|
||||
| ------------------------------- ^^ -------------------------------- StorageVersion
|
||||
| |
|
||||
| NoStorageVersionSet
|
||||
|
||||
Reference in New Issue
Block a user