mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 01:11:10 +00:00
migrations: prevent accidentally using unversioned migrations instead of VersionedMigration (#3835)
closes #1324 #### Problem Currently, it is possible to accidentally use inner unversioned migration instead of `VersionedMigration` since both implement `OnRuntimeUpgrade`. #### Solution With this change, we make it clear that value of `Inner` is not intended to be used directly. It is achieved by bounding `Inner` to new trait `UncheckedOnRuntimeUpgrade`, which has the same interface (except `unchecked_` prefix) as `OnRuntimeUpgrade`. #### `try-runtime` functions Since developers can implement `try-runtime` for `Inner` value in `VersionedMigration` and have custom logic for it, I added the same `try-runtime` functions to `UncheckedOnRuntimeUpgrade`. I looked for a ways to not duplicate functions, but couldn't find anything that doesn't significantly change the codebase. So I would appreciate If you have any suggestions to improve this cc @liamaharon @xlc polkadot address: 16FqwPZ8GRC5U5D4Fu7W33nA55ZXzXGWHwmbnE1eT6pxuqcT --------- Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
This commit is contained in:
@@ -41,19 +41,19 @@ use sp_std::{marker::PhantomData, vec::Vec};
|
||||
/// It takes 5 type parameters:
|
||||
/// - `From`: The version being upgraded from.
|
||||
/// - `To`: The version being upgraded to.
|
||||
/// - `Inner`: An implementation of `OnRuntimeUpgrade`.
|
||||
/// - `Inner`: An implementation of `UncheckedOnRuntimeUpgrade`.
|
||||
/// - `Pallet`: The Pallet being upgraded.
|
||||
/// - `Weight`: The runtime's RuntimeDbWeight implementation.
|
||||
///
|
||||
/// When a [`VersionedMigration`] `on_runtime_upgrade`, `pre_upgrade`, or `post_upgrade` method is
|
||||
/// called, the on-chain version of the pallet is compared to `From`. If they match, the `Inner`
|
||||
/// equivalent is called and the pallets on-chain version is set to `To` after the migration.
|
||||
/// Otherwise, a warning is logged notifying the developer that the upgrade was a noop and should
|
||||
/// probably be removed.
|
||||
/// `UncheckedOnRuntimeUpgrade` is called and the pallets on-chain version is set to `To`
|
||||
/// after the migration. 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.
|
||||
/// By not bounding `Inner` with `OnRuntimeUpgrade`, we prevent developers from
|
||||
/// accidentally using the unchecked version of the migration in a runtime upgrade instead of
|
||||
/// [`VersionedMigration`].
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```ignore
|
||||
@@ -71,9 +71,9 @@ use sp_std::{marker::PhantomData, vec::Vec};
|
||||
/// /// - 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 struct VersionUncheckedMigrateV5ToV6<T>(sp_std::marker::PhantomData<T>);
|
||||
/// impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> {
|
||||
/// // `UncheckedOnRuntimeUpgrade` implementation...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@@ -116,7 +116,7 @@ pub enum VersionedPostUpgradeData {
|
||||
impl<
|
||||
const FROM: u16,
|
||||
const TO: u16,
|
||||
Inner: crate::traits::OnRuntimeUpgrade,
|
||||
Inner: crate::traits::UncheckedOnRuntimeUpgrade,
|
||||
Pallet: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess,
|
||||
DbWeight: Get<RuntimeDbWeight>,
|
||||
> crate::traits::OnRuntimeUpgrade for VersionedMigration<FROM, TO, Inner, Pallet, DbWeight>
|
||||
|
||||
@@ -87,7 +87,7 @@ pub use hooks::GenesisBuild;
|
||||
pub use hooks::{
|
||||
BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis,
|
||||
OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, OnTimestampSet, PostInherents,
|
||||
PostTransactions, PreInherents,
|
||||
PostTransactions, PreInherents, UncheckedOnRuntimeUpgrade,
|
||||
};
|
||||
|
||||
pub mod schedule;
|
||||
|
||||
@@ -227,6 +227,30 @@ pub trait OnRuntimeUpgrade {
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is intended for use within `VersionedMigration` to execute storage migrations without
|
||||
/// automatic version checks. Implementations should ensure migration logic is safe and idempotent.
|
||||
pub trait UncheckedOnRuntimeUpgrade {
|
||||
/// Called within `VersionedMigration` to execute the actual migration. It is also
|
||||
/// expected that no version checks are performed within this function.
|
||||
///
|
||||
/// See also [`Hooks::on_runtime_upgrade`].
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
/// See [`Hooks::pre_upgrade`].
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// See [`Hooks::post_upgrade`].
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[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))]
|
||||
@@ -459,7 +483,9 @@ pub trait Hooks<BlockNumber> {
|
||||
/// ## Implementation Note: Standalone Migrations
|
||||
///
|
||||
/// Additional migrations can be created by directly implementing [`OnRuntimeUpgrade`] on
|
||||
/// structs and passing them to `Executive`.
|
||||
/// structs and passing them to `Executive`. Or alternatively, by implementing
|
||||
/// [`UncheckedOnRuntimeUpgrade`], passing it to [`crate::migrations::VersionedMigration`],
|
||||
/// which already implements [`OnRuntimeUpgrade`].
|
||||
///
|
||||
/// ## Implementation Note: Pallet Versioning
|
||||
///
|
||||
|
||||
@@ -23,7 +23,7 @@ use frame_support::{
|
||||
construct_runtime, derive_impl,
|
||||
migrations::VersionedMigration,
|
||||
parameter_types,
|
||||
traits::{GetStorageVersion, OnRuntimeUpgrade, StorageVersion},
|
||||
traits::{GetStorageVersion, OnRuntimeUpgrade, StorageVersion, UncheckedOnRuntimeUpgrade},
|
||||
weights::constants::RocksDbWeight,
|
||||
};
|
||||
use frame_system::Config;
|
||||
@@ -103,9 +103,11 @@ parameter_types! {
|
||||
static PostUpgradeCalledWith: Vec<u8> = Vec::new();
|
||||
}
|
||||
|
||||
/// Implement `OnRuntimeUpgrade` for `SomeUnversionedMigration`.
|
||||
/// Implement `UncheckedOnRuntimeUpgrade` for `SomeUnversionedMigration`.
|
||||
/// It sets SomeStorage to S, and returns a weight derived from UpgradeReads and UpgradeWrites.
|
||||
impl<T: dummy_pallet::Config, const S: u32> OnRuntimeUpgrade for SomeUnversionedMigration<T, S> {
|
||||
impl<T: dummy_pallet::Config, const S: u32> UncheckedOnRuntimeUpgrade
|
||||
for SomeUnversionedMigration<T, S>
|
||||
{
|
||||
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
|
||||
PreUpgradeCalled::set(true);
|
||||
Ok(PreUpgradeReturnBytes::get().to_vec())
|
||||
|
||||
Reference in New Issue
Block a user