mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 07:37:57 +00:00
Initialise on-chain StorageVersion for pallets added after genesis (#1297)
Original PR https://github.com/paritytech/substrate/pull/14641 --- Closes https://github.com/paritytech/polkadot-sdk/issues/109 ### Problem Quoting from the above issue: > When adding a pallet to chain after genesis we currently don't set the StorageVersion. So, when calling on_chain_storage_version it returns 0 while the pallet is maybe already at storage version 9 when it was added to the chain. This could lead to issues when running migrations. ### Solution - Create a new trait `BeforeAllRuntimeMigrations` with a single method `fn before_all_runtime_migrations() -> Weight` trait with a noop default implementation - Modify `Executive` to call `BeforeAllRuntimeMigrations::before_all_runtime_migrations` for all pallets before running any other hooks - Implement `BeforeAllRuntimeMigrations` in the pallet proc macro to initialize the on-chain version to the current pallet version if the pallet has no storage set (indicating it has been recently added to the runtime and needs to have its version initialised). ### Other changes in this PR - Abstracted repeated boilerplate to access the `pallet_name` in the pallet expand proc macro. ### FAQ #### Why create a new hook instead of adding this logic to the pallet `pre_upgrade`? `Executive` currently runs `COnRuntimeUpgrade` (custom migrations) before `AllPalletsWithSystem` migrations. We need versions to be initialized before the `COnRuntimeUpgrade` migrations are run, because `COnRuntimeUpgrade` migrations may use the on-chain version for critical logic. e.g. `VersionedRuntimeUpgrade` uses it to decide whether or not to execute. We cannot reorder `COnRuntimeUpgrade` and `AllPalletsWithSystem` so `AllPalletsWithSystem` runs first, because `AllPalletsWithSystem` have some logic in their `post_upgrade` hooks to verify that the on-chain version and current pallet version match. A common use case of `COnRuntimeUpgrade` migrations is to perform a migration which will result in the versions matching, so if they were reordered these `post_upgrade` checks would fail. #### Why init the on-chain version for pallets without a current storage version? We must init the on-chain version for pallets even if they don't have a defined storage version so if there is a future version bump, the on-chain version is not automatically set to that new version without a proper migration. e.g. bad scenario: 1. A pallet with no 'current version' is added to the runtime 2. Later, the pallet is upgraded with the 'current version' getting set to 1 and a migration is added to Executive Migrations to migrate the storage from 0 to 1 a. Runtime upgrade occurs b. `before_all` hook initializes the on-chain version to 1 c. `on_runtime_upgrade` of the migration executes, and sees the on-chain version is already 1 therefore think storage is already migrated and does not execute the storage migration Now, on-chain version is 1 but storage is still at version 0. By always initializing the on-chain version when the pallet is added to the runtime we avoid that scenario. --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
@@ -47,7 +47,7 @@ pub mod __private {
|
||||
pub use sp_core::{OpaqueMetadata, Void};
|
||||
pub use sp_core_hashing_proc_macro;
|
||||
pub use sp_inherents;
|
||||
pub use sp_io::{self, storage::root as storage_root};
|
||||
pub use sp_io::{self, hashing, storage::root as storage_root};
|
||||
pub use sp_metadata_ir as metadata_ir;
|
||||
#[cfg(feature = "std")]
|
||||
pub use sp_runtime::{bounded_btree_map, bounded_vec};
|
||||
|
||||
@@ -119,7 +119,7 @@ impl<
|
||||
let on_chain_version = Pallet::on_chain_storage_version();
|
||||
if on_chain_version == FROM {
|
||||
log::info!(
|
||||
"🚚 Pallet {:?} migrating storage version from {:?} to {:?}.",
|
||||
"🚚 Pallet {:?} VersionedMigration migrating storage version from {:?} to {:?}.",
|
||||
Pallet::name(),
|
||||
FROM,
|
||||
TO
|
||||
@@ -134,7 +134,7 @@ impl<
|
||||
weight.saturating_add(DbWeight::get().reads_writes(1, 1))
|
||||
} else {
|
||||
log::warn!(
|
||||
"🚚 Pallet {:?} migration {}->{} can be removed; on-chain is already at {:?}.",
|
||||
"🚚 Pallet {:?} VersionedMigration migration {}->{} can be removed; on-chain is already at {:?}.",
|
||||
Pallet::name(),
|
||||
FROM,
|
||||
TO,
|
||||
|
||||
@@ -84,8 +84,8 @@ mod hooks;
|
||||
#[allow(deprecated)]
|
||||
pub use hooks::GenesisBuild;
|
||||
pub use hooks::{
|
||||
BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis, OnIdle, OnInitialize,
|
||||
OnRuntimeUpgrade, OnTimestampSet,
|
||||
BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis,
|
||||
OnIdle, OnInitialize, OnRuntimeUpgrade, OnTimestampSet,
|
||||
};
|
||||
|
||||
pub mod schedule;
|
||||
|
||||
@@ -99,6 +99,19 @@ pub trait OnGenesis {
|
||||
fn on_genesis() {}
|
||||
}
|
||||
|
||||
/// Implemented by pallets, allows defining logic to run prior to any [`OnRuntimeUpgrade`] logic.
|
||||
///
|
||||
/// This hook is intended to be used internally in FRAME and not be exposed to FRAME developers.
|
||||
///
|
||||
/// It is defined as a seperate trait from [`OnRuntimeUpgrade`] precisely to not pollute the public
|
||||
/// API.
|
||||
pub trait BeforeAllRuntimeMigrations {
|
||||
/// Something that should happen before runtime migrations are executed.
|
||||
fn before_all_runtime_migrations() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`Hooks::on_runtime_upgrade`].
|
||||
pub trait OnRuntimeUpgrade {
|
||||
/// See [`Hooks::on_runtime_upgrade`].
|
||||
@@ -150,10 +163,24 @@ pub trait OnRuntimeUpgrade {
|
||||
}
|
||||
}
|
||||
|
||||
#[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))]
|
||||
impl BeforeAllRuntimeMigrations for Tuple {
|
||||
/// Implements the default behavior of
|
||||
/// [`BeforeAllRuntimeMigrations::before_all_runtime_migrations`] for tuples.
|
||||
fn before_all_runtime_migrations() -> Weight {
|
||||
let mut weight = Weight::zero();
|
||||
for_tuples!( #( weight = weight.saturating_add(Tuple::before_all_runtime_migrations()); )* );
|
||||
weight
|
||||
}
|
||||
}
|
||||
|
||||
#[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))]
|
||||
impl OnRuntimeUpgrade for Tuple {
|
||||
/// Implements the default behavior of [`OnRuntimeUpgrade::on_runtime_upgrade`] for tuples.
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let mut weight = Weight::zero();
|
||||
for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* );
|
||||
|
||||
@@ -222,6 +222,23 @@ impl StorageVersion {
|
||||
|
||||
crate::storage::unhashed::get_or_default(&key)
|
||||
}
|
||||
|
||||
/// Returns if the storage version key for the given pallet exists in storage.
|
||||
///
|
||||
/// See [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic iff `Pallet` can not be found by `PalletInfo`.
|
||||
/// In a runtime that is put together using
|
||||
/// [`construct_runtime!`](crate::construct_runtime) this should never happen.
|
||||
///
|
||||
/// It will also panic if this function isn't executed in an externalities
|
||||
/// provided environment.
|
||||
pub fn exists<P: PalletInfoAccess>() -> bool {
|
||||
let key = Self::storage_key::<P>();
|
||||
crate::storage::unhashed::exists(&key)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u16> for StorageVersion {
|
||||
|
||||
Reference in New Issue
Block a user