mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user