-
Notifications
You must be signed in to change notification settings - Fork 740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MBM try-runtime
support
#4251
MBM try-runtime
support
#4251
Changes from 39 commits
7ec7cc9
1c8416d
623c2b8
675338a
d36ce71
45ce6e4
1403cbc
f5b9210
92053e0
311d994
e0a8152
bf3fbfb
279e4cf
1710410
38dd259
1a8e86c
b10534f
30121c5
b1ccef1
6fbe843
1677cd6
1a61af3
f57b50a
95b7969
7dcad83
8fa87f2
49cd4e1
028aa60
e01c9e4
b38ff1a
06eb247
96860b3
b9bf013
ba768dc
97a1cbd
f610140
1c969d8
ad1811a
666db60
e437426
00de16a
6e0539f
f26c07b
475a976
ea00e35
7c597b6
fbda868
d6ff835
9113c2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
title: MBM `try-runtime` support | ||
doc: | ||
- audience: Runtime Dev | ||
description: "# MBM try-runtime support\n\nThis MR adds support to the try-runtime\ | ||
\ trait such that the try-runtime-CLI will be able to support MBM testing [here](https://github.com/paritytech/try-runtime-cli/pull/90).\ | ||
\ It mainly adds two feature-gated hooks to the `SteppedMigration` hook to facilitate\ | ||
\ testing. These hooks are named `pre_upgrade` and `post_upgrade` and have the\ | ||
\ same signature and implications as for single-block migrations.\n\n## Integration\n\ | ||
\nTo make use of this in your Multi-Block-Migration, just implement the two new\ | ||
\ hooks and test pre- and post-conditions in them:\n\n```rust\n#[cfg(feature =\ | ||
\ \"try-runtime\")]\nfn pre_upgrade() -> Result<Vec<u8>, frame_support::sp_runtime::TryRuntimeError>\ | ||
\ {\n\t// ...\n}\n\n#[cfg(feature = \"try-runtime\")]\nfn post_upgrade(prev: Vec<u8>)\ | ||
\ -> Result<(), frame_support::sp_runtime::TryRuntimeError> {\n // ...\n}\n\ | ||
```\n\nYou may return an error or panic in these functions to indicate failure.\ | ||
\ This will then show up in the try-runtime-CLI and can be used in CI for testing.\n\ | ||
\nChanges:\n- Adds `try-runtime` gated methods `pre_upgrade` and `post_upgrade`\ | ||
\ on `SteppedMigration`\n- Adds `try-runtime` gated methods `nth_pre_upgrade`\ | ||
\ and `nth_post_upgrade` on `SteppedMigrations`\n- Modifies `pallet_migrations`\ | ||
\ implementation to run pre_upgrade and post_upgrade steps at the appropriate\ | ||
\ times, and panic in the event of migration failure." | ||
crates: | ||
- name: asset-hub-rococo-runtime | ||
bump: minor | ||
- name: asset-hub-westend-runtime | ||
bump: minor | ||
- name: bridge-hub-rococo-runtime | ||
bump: minor | ||
- name: bridge-hub-westend-runtime | ||
bump: minor | ||
- name: collectives-westend-runtime | ||
bump: minor | ||
- name: contracts-rococo-runtime | ||
bump: minor | ||
- name: coretime-rococo-runtime | ||
bump: minor | ||
- name: coretime-westend-runtime | ||
bump: minor | ||
- name: people-rococo-runtime | ||
bump: minor | ||
- name: people-westend-runtime | ||
bump: minor | ||
- name: penpal-runtime | ||
bump: minor | ||
- name: polkadot-parachain-bin | ||
bump: minor | ||
- name: rococo-runtime | ||
bump: minor | ||
- name: westend-runtime | ||
bump: minor | ||
- name: frame-executive | ||
bump: minor | ||
- name: pallet-migrations | ||
bump: minor | ||
- name: frame-support | ||
bump: minor | ||
- name: frame-system | ||
bump: minor | ||
- name: frame-try-runtime | ||
bump: minor |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,8 @@ | |
pub mod migrations; | ||
mod mock; | ||
|
||
extern crate alloc; | ||
|
||
pub use pallet::*; | ||
|
||
#[frame_support::pallet] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,21 +70,26 @@ | |
//! points to the currently active migration and stores its inner cursor. The inner cursor can then | ||
//! be used by the migration to store its inner state and advance. Each time when the migration | ||
//! returns `Some(cursor)`, it signals the pallet that it is not done yet. | ||
liamaharon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//! | ||
//! The cursor is reset on each runtime upgrade. This ensures that it starts to execute at the | ||
//! first migration in the vector. The pallets cursor is only ever incremented or set to `Stuck` | ||
//! once it encounters an error (Goal 4). Once in the stuck state, the pallet will stay stuck until | ||
//! it is fixed through manual governance intervention. | ||
//! | ||
//! As soon as the cursor of the pallet becomes `Some(_)`; [`MultiStepMigrator::ongoing`] returns | ||
//! `true` (Goal 2). This can be used by upstream code to possibly pause transactions. | ||
//! In `on_initialize` the pallet will load the current migration and check whether it was already | ||
//! executed in the past by checking for membership of its ID in the [`Historic`] set. Historic | ||
//! migrations are skipped without causing an error. Each successfully executed migration is added | ||
//! to this set (Goal 5). | ||
//! | ||
//! This proceeds until no more migrations remain. At that point, the event `UpgradeCompleted` is | ||
//! emitted (Goal 1). | ||
//! | ||
//! The execution of each migration happens by calling [`SteppedMigration::transactional_step`]. | ||
//! This function wraps the inner `step` function into a transactional layer to allow rollback in | ||
//! the error case (Goal 6). | ||
//! | ||
//! Weight limits must be checked by the migration itself. The pallet provides a [`WeightMeter`] for | ||
//! that purpose. The pallet may return [`SteppedMigrationError::InsufficientWeight`] at any point. | ||
//! In that scenario, one of two things will happen: if that migration was exclusively executed | ||
|
@@ -156,11 +161,15 @@ use core::ops::ControlFlow; | |
use frame_support::{ | ||
defensive, defensive_assert, | ||
migrations::*, | ||
pallet_prelude::*, | ||
traits::Get, | ||
weights::{Weight, WeightMeter}, | ||
BoundedVec, | ||
}; | ||
use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System}; | ||
use frame_system::{ | ||
pallet_prelude::{BlockNumberFor, *}, | ||
Pallet as System, | ||
}; | ||
use sp_runtime::Saturating; | ||
|
||
/// Points to the next migration to execute. | ||
|
@@ -262,18 +271,32 @@ pub type IdentifierOf<T> = BoundedVec<u8, <T as Config>::IdentifierMaxLen>; | |
pub type ActiveCursorOf<T> = ActiveCursor<RawCursorOf<T>, BlockNumberFor<T>>; | ||
|
||
/// Trait for a tuple of No-OP migrations with one element. | ||
#[impl_trait_for_tuples::impl_for_tuples(30)] | ||
pub trait MockedMigrations: SteppedMigrations { | ||
/// The migration should fail after `n` steps. | ||
fn set_fail_after(n: u32); | ||
/// The migration should succeed after `n` steps. | ||
fn set_success_after(n: u32); | ||
} | ||
|
||
#[cfg(feature = "try-runtime")] | ||
/// Wrapper for pre-upgrade bytes, allowing us to impl MEL on it. | ||
liamaharon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// For `try-runtime` testing only. | ||
#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, Default)] | ||
struct PreUpgradeBytesWrapper(pub Vec<u8>); | ||
|
||
/// Data stored by the pre-upgrade hook of the MBMs. Only used for `try-runtime` testing. | ||
/// | ||
/// Define this outside of the pallet so it is not confused with actual storage. | ||
#[cfg(feature = "try-runtime")] | ||
#[frame_support::storage_alias] | ||
type PreUpgradeBytes<T: Config> = | ||
StorageMap<Pallet<T>, Twox64Concat, IdentifierOf<T>, PreUpgradeBytesWrapper, ValueQuery>; | ||
|
||
#[frame_support::pallet] | ||
pub mod pallet { | ||
use super::*; | ||
use frame_support::pallet_prelude::*; | ||
use frame_system::pallet_prelude::*; | ||
|
||
#[pallet::pallet] | ||
pub struct Pallet<T>(_); | ||
|
@@ -700,6 +723,16 @@ impl<T: Config> Pallet<T> { | |
} | ||
|
||
let max_steps = T::Migrations::nth_max_steps(cursor.index); | ||
|
||
// If this is the first time running this migration, exec the pre-upgrade hook. | ||
ggwpez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[cfg(feature = "try-runtime")] | ||
if !PreUpgradeBytes::<T>::contains_key(&bounded_id) { | ||
let bytes = T::Migrations::nth_pre_upgrade(cursor.index) | ||
.expect("Invalid cursor.index") | ||
.expect("Pre-upgrade failed"); | ||
PreUpgradeBytes::<T>::insert(&bounded_id, PreUpgradeBytesWrapper(bytes)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is a bit ugly - since it puts the intermediary state into storage. But i dont think there is a spot where we can put intermediary data that is persisted between blocks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok, it's just |
||
} | ||
|
||
let next_cursor = T::Migrations::nth_transactional_step( | ||
cursor.index, | ||
cursor.inner_cursor.clone().map(|c| c.into_inner()), | ||
|
@@ -734,6 +767,16 @@ impl<T: Config> Pallet<T> { | |
}, | ||
Ok(None) => { | ||
// A migration is done when it returns cursor `None`. | ||
|
||
// Run post-upgrade checks. | ||
#[cfg(feature = "try-runtime")] | ||
T::Migrations::nth_post_upgrade( | ||
cursor.index, | ||
PreUpgradeBytes::<T>::get(&bounded_id).0, | ||
) | ||
.expect("Invalid cursor.index.") | ||
.expect("Post-upgrade failed."); | ||
|
||
Self::deposit_event(Event::MigrationCompleted { index: cursor.index, took }); | ||
Historic::<T>::insert(&bounded_id, ()); | ||
cursor.goto_next_migration(System::<T>::block_number()); | ||
|
@@ -758,14 +801,21 @@ impl<T: Config> Pallet<T> { | |
} | ||
|
||
/// Fail the current runtime upgrade, caused by `migration`. | ||
/// | ||
/// When the `try-runtime` feature is enabled, this function will panic. | ||
// Allow unreachable code so it can compile without warnings when `try-runtime` is enabled. | ||
fn upgrade_failed(migration: Option<u32>) { | ||
use FailedMigrationHandling::*; | ||
Self::deposit_event(Event::UpgradeFailed); | ||
|
||
match T::FailedMigrationHandler::failed(migration) { | ||
KeepStuck => Cursor::<T>::set(Some(MigrationCursor::Stuck)), | ||
ForceUnstuck => Cursor::<T>::kill(), | ||
Ignore => {}, | ||
if cfg!(feature = "try-runtime") { | ||
panic!("Migration with index {:?} failed.", migration); | ||
} else { | ||
match T::FailedMigrationHandler::failed(migration) { | ||
KeepStuck => Cursor::<T>::set(Some(MigrationCursor::Stuck)), | ||
ForceUnstuck => Cursor::<T>::kill(), | ||
Ignore => {}, | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be removed, right?