frame_support/
migrations.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	defensive,
20	storage::{storage_prefix, transactional::with_transaction_opaque_err},
21	traits::{
22		Defensive, GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, SafeMode,
23		StorageVersion,
24	},
25	weights::{RuntimeDbWeight, Weight, WeightMeter},
26};
27use alloc::vec::Vec;
28use codec::{Decode, Encode, MaxEncodedLen};
29use core::marker::PhantomData;
30use impl_trait_for_tuples::impl_for_tuples;
31use sp_arithmetic::traits::Bounded;
32use sp_core::Get;
33use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult};
34use sp_runtime::traits::Zero;
35
36/// Handles storage migration pallet versioning.
37///
38/// [`VersionedMigration`] allows developers to write migrations without worrying about checking and
39/// setting storage versions. Instead, the developer wraps their migration in this struct which
40/// takes care of version handling using best practices.
41///
42/// It takes 5 type parameters:
43/// - `From`: The version being upgraded from.
44/// - `To`: The version being upgraded to.
45/// - `Inner`: An implementation of `UncheckedOnRuntimeUpgrade`.
46/// - `Pallet`: The Pallet being upgraded.
47/// - `Weight`: The runtime's RuntimeDbWeight implementation.
48///
49/// When a [`VersionedMigration`] `on_runtime_upgrade`, `pre_upgrade`, or `post_upgrade` method is
50/// called, the on-chain version of the pallet is compared to `From`. If they match, the `Inner`
51/// `UncheckedOnRuntimeUpgrade` is called and the pallets on-chain version is set to `To`
52/// after the migration. Otherwise, a warning is logged notifying the developer that the upgrade was
53/// a noop and should probably be removed.
54///
55/// By not bounding `Inner` with `OnRuntimeUpgrade`, we prevent developers from
56/// accidentally using the unchecked version of the migration in a runtime upgrade instead of
57/// [`VersionedMigration`].
58///
59/// ### Examples
60/// ```ignore
61/// // In file defining migrations
62///
63/// /// Private module containing *version unchecked* migration logic.
64/// ///
65/// /// Should only be used by the [`VersionedMigration`] type in this module to create something to
66/// /// export.
67/// ///
68/// /// We keep this private so the unversioned migration cannot accidentally be used in any runtimes.
69/// ///
70/// /// For more about this pattern of keeping items private, see
71/// /// - https://github.com/rust-lang/rust/issues/30905
72/// /// - https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40
73/// mod version_unchecked {
74/// 	use super::*;
75/// 	pub struct VersionUncheckedMigrateV5ToV6<T>(core::marker::PhantomData<T>);
76/// 	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> {
77/// 		// `UncheckedOnRuntimeUpgrade` implementation...
78/// 	}
79/// }
80///
81/// pub type MigrateV5ToV6<T, I> =
82/// 	VersionedMigration<
83/// 		5,
84/// 		6,
85/// 		VersionUncheckedMigrateV5ToV6<T, I>,
86/// 		crate::pallet::Pallet<T, I>,
87/// 		<T as frame_system::Config>::DbWeight
88/// 	>;
89///
90/// // Migrations tuple to pass to the Executive pallet:
91/// pub type Migrations = (
92/// 	// other migrations...
93/// 	MigrateV5ToV6<T, ()>,
94/// 	// other migrations...
95/// );
96/// ```
97pub struct VersionedMigration<const FROM: u16, const TO: u16, Inner, Pallet, Weight> {
98	_marker: PhantomData<(Inner, Pallet, Weight)>,
99}
100
101/// A helper enum to wrap the pre_upgrade bytes like an Option before passing them to post_upgrade.
102/// This enum is used rather than an Option to make the API clearer to the developer.
103#[derive(Encode, Decode)]
104pub enum VersionedPostUpgradeData {
105	/// The migration ran, inner vec contains pre_upgrade data.
106	MigrationExecuted(alloc::vec::Vec<u8>),
107	/// This migration is a noop, do not run post_upgrade checks.
108	Noop,
109}
110
111/// Implementation of the `OnRuntimeUpgrade` trait for `VersionedMigration`.
112///
113/// Its main function is to perform the runtime upgrade in `on_runtime_upgrade` only if the on-chain
114/// version of the pallets storage matches `From`, and after the upgrade set the on-chain storage to
115/// `To`. If the versions do not match, it writes a log notifying the developer that the migration
116/// is a noop.
117impl<
118		const FROM: u16,
119		const TO: u16,
120		Inner: crate::traits::UncheckedOnRuntimeUpgrade,
121		Pallet: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess,
122		DbWeight: Get<RuntimeDbWeight>,
123	> crate::traits::OnRuntimeUpgrade for VersionedMigration<FROM, TO, Inner, Pallet, DbWeight>
124{
125	/// Executes pre_upgrade if the migration will run, and wraps the pre_upgrade bytes in
126	/// [`VersionedPostUpgradeData`] before passing them to post_upgrade, so it knows whether the
127	/// migration ran or not.
128	#[cfg(feature = "try-runtime")]
129	fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
130		let on_chain_version = Pallet::on_chain_storage_version();
131		if on_chain_version == FROM {
132			Ok(VersionedPostUpgradeData::MigrationExecuted(Inner::pre_upgrade()?).encode())
133		} else {
134			Ok(VersionedPostUpgradeData::Noop.encode())
135		}
136	}
137
138	/// Executes the versioned runtime upgrade.
139	///
140	/// First checks if the pallets on-chain storage version matches the version of this upgrade. If
141	/// it matches, it calls `Inner::on_runtime_upgrade`, updates the on-chain version, and returns
142	/// the weight. If it does not match, it writes a log notifying the developer that the migration
143	/// is a noop.
144	fn on_runtime_upgrade() -> Weight {
145		let on_chain_version = Pallet::on_chain_storage_version();
146		if on_chain_version == FROM {
147			log::info!(
148				"๐Ÿšš Pallet {:?} VersionedMigration migrating storage version from {:?} to {:?}.",
149				Pallet::name(),
150				FROM,
151				TO
152			);
153
154			// Execute the migration
155			let weight = Inner::on_runtime_upgrade();
156
157			// Update the on-chain version
158			StorageVersion::new(TO).put::<Pallet>();
159
160			weight.saturating_add(DbWeight::get().reads_writes(1, 1))
161		} else {
162			log::warn!(
163				"๐Ÿšš Pallet {:?} VersionedMigration migration {}->{} can be removed; on-chain is already at {:?}.",
164				Pallet::name(),
165				FROM,
166				TO,
167				on_chain_version
168			);
169			DbWeight::get().reads(1)
170		}
171	}
172
173	/// Executes `Inner::post_upgrade` if the migration just ran.
174	///
175	/// pre_upgrade passes [`VersionedPostUpgradeData::MigrationExecuted`] to post_upgrade if
176	/// the migration ran, and [`VersionedPostUpgradeData::Noop`] otherwise.
177	#[cfg(feature = "try-runtime")]
178	fn post_upgrade(
179		versioned_post_upgrade_data_bytes: alloc::vec::Vec<u8>,
180	) -> Result<(), sp_runtime::TryRuntimeError> {
181		use codec::DecodeAll;
182		match <VersionedPostUpgradeData>::decode_all(&mut &versioned_post_upgrade_data_bytes[..])
183			.map_err(|_| "VersionedMigration post_upgrade failed to decode PreUpgradeData")?
184		{
185			VersionedPostUpgradeData::MigrationExecuted(inner_bytes) =>
186				Inner::post_upgrade(inner_bytes),
187			VersionedPostUpgradeData::Noop => Ok(()),
188		}
189	}
190}
191
192/// Can store the in-code pallet version on-chain.
193pub trait StoreInCodeStorageVersion<T: GetStorageVersion + PalletInfoAccess> {
194	/// Write the in-code storage version on-chain.
195	fn store_in_code_storage_version();
196}
197
198impl<T: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess>
199	StoreInCodeStorageVersion<T> for StorageVersion
200{
201	fn store_in_code_storage_version() {
202		let version = <T as GetStorageVersion>::in_code_storage_version();
203		version.put::<T>();
204	}
205}
206
207impl<T: GetStorageVersion<InCodeStorageVersion = NoStorageVersionSet> + PalletInfoAccess>
208	StoreInCodeStorageVersion<T> for NoStorageVersionSet
209{
210	fn store_in_code_storage_version() {
211		StorageVersion::default().put::<T>();
212	}
213}
214
215/// Trait used by [`migrate_from_pallet_version_to_storage_version`] to do the actual migration.
216pub trait PalletVersionToStorageVersionHelper {
217	fn migrate(db_weight: &RuntimeDbWeight) -> Weight;
218}
219
220impl<T: GetStorageVersion + PalletInfoAccess> PalletVersionToStorageVersionHelper for T
221where
222	T::InCodeStorageVersion: StoreInCodeStorageVersion<T>,
223{
224	fn migrate(db_weight: &RuntimeDbWeight) -> Weight {
225		const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:";
226
227		fn pallet_version_key(name: &str) -> [u8; 32] {
228			crate::storage::storage_prefix(name.as_bytes(), PALLET_VERSION_STORAGE_KEY_POSTFIX)
229		}
230
231		sp_io::storage::clear(&pallet_version_key(<T as PalletInfoAccess>::name()));
232
233		<T::InCodeStorageVersion as StoreInCodeStorageVersion<T>>::store_in_code_storage_version();
234
235		db_weight.writes(2)
236	}
237}
238
239#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
240#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
241#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
242impl PalletVersionToStorageVersionHelper for T {
243	fn migrate(db_weight: &RuntimeDbWeight) -> Weight {
244		let mut weight = Weight::zero();
245
246		for_tuples!( #( weight = weight.saturating_add(T::migrate(db_weight)); )* );
247
248		weight
249	}
250}
251
252/// Migrate from the `PalletVersion` struct to the new [`StorageVersion`] struct.
253///
254/// This will remove all `PalletVersion's` from the state and insert the in-code storage version.
255pub fn migrate_from_pallet_version_to_storage_version<
256	Pallets: PalletVersionToStorageVersionHelper,
257>(
258	db_weight: &RuntimeDbWeight,
259) -> Weight {
260	Pallets::migrate(db_weight)
261}
262
263/// `RemovePallet` is a utility struct used to remove all storage items associated with a specific
264/// pallet.
265///
266/// This struct is generic over two parameters:
267/// - `P` is a type that implements the `Get` trait for a static string, representing the pallet's
268///   name.
269/// - `DbWeight` is a type that implements the `Get` trait for `RuntimeDbWeight`, providing the
270///   weight for database operations.
271///
272/// On runtime upgrade, the `on_runtime_upgrade` function will clear all storage items associated
273/// with the specified pallet, logging the number of keys removed. If the `try-runtime` feature is
274/// enabled, the `pre_upgrade` and `post_upgrade` functions can be used to verify the storage
275/// removal before and after the upgrade.
276///
277/// # Examples:
278/// ```ignore
279/// construct_runtime! {
280/// 	pub enum Runtime
281/// 	{
282/// 		System: frame_system = 0,
283///
284/// 		SomePalletToRemove: pallet_something = 1,
285/// 		AnotherPalletToRemove: pallet_something_else = 2,
286///
287/// 		YourOtherPallets...
288/// 	}
289/// };
290///
291/// parameter_types! {
292/// 		pub const SomePalletToRemoveStr: &'static str = "SomePalletToRemove";
293/// 		pub const AnotherPalletToRemoveStr: &'static str = "AnotherPalletToRemove";
294/// }
295///
296/// pub type Migrations = (
297/// 	RemovePallet<SomePalletToRemoveStr, RocksDbWeight>,
298/// 	RemovePallet<AnotherPalletToRemoveStr, RocksDbWeight>,
299/// 	AnyOtherMigrations...
300/// );
301///
302/// pub type Executive = frame_executive::Executive<
303/// 	Runtime,
304/// 	Block,
305/// 	frame_system::ChainContext<Runtime>,
306/// 	Runtime,
307/// 	Migrations
308/// >;
309/// ```
310///
311/// WARNING: `RemovePallet` has no guard rails preventing it from bricking the chain if the
312/// operation of removing storage for the given pallet would exceed the block weight limit.
313///
314/// If your pallet has too many keys to be removed in a single block, it is advised to wait for
315/// a multi-block scheduler currently under development which will allow for removal of storage
316/// items (and performing other heavy migrations) over multiple blocks
317/// (see <https://github.com/paritytech/substrate/issues/13690>).
318pub struct RemovePallet<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>(
319	PhantomData<(P, DbWeight)>,
320);
321impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits::OnRuntimeUpgrade
322	for RemovePallet<P, DbWeight>
323{
324	fn on_runtime_upgrade() -> frame_support::weights::Weight {
325		let hashed_prefix = twox_128(P::get().as_bytes());
326		let keys_removed = match clear_prefix(&hashed_prefix, None) {
327			KillStorageResult::AllRemoved(value) => value,
328			KillStorageResult::SomeRemaining(value) => {
329				log::error!(
330					"`clear_prefix` failed to remove all keys for {}. THIS SHOULD NEVER HAPPEN! ๐Ÿšจ",
331					P::get()
332				);
333				value
334			},
335		} as u64;
336
337		log::info!("Removed {} {} keys ๐Ÿงน", keys_removed, P::get());
338
339		DbWeight::get().reads_writes(keys_removed + 1, keys_removed)
340	}
341
342	#[cfg(feature = "try-runtime")]
343	fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
344		use crate::storage::unhashed::contains_prefixed_key;
345
346		let hashed_prefix = twox_128(P::get().as_bytes());
347		match contains_prefixed_key(&hashed_prefix) {
348			true => log::info!("Found {} keys pre-removal ๐Ÿ‘€", P::get()),
349			false => log::warn!(
350				"Migration RemovePallet<{}> can be removed (no keys found pre-removal).",
351				P::get()
352			),
353		};
354		Ok(alloc::vec::Vec::new())
355	}
356
357	#[cfg(feature = "try-runtime")]
358	fn post_upgrade(_state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
359		use crate::storage::unhashed::contains_prefixed_key;
360
361		let hashed_prefix = twox_128(P::get().as_bytes());
362		match contains_prefixed_key(&hashed_prefix) {
363			true => {
364				log::error!("{} has keys remaining post-removal โ—", P::get());
365				return Err("Keys remaining post-removal, this should never happen ๐Ÿšจ".into())
366			},
367			false => log::info!("No {} keys found post-removal ๐ŸŽ‰", P::get()),
368		};
369		Ok(())
370	}
371}
372
373/// `RemoveStorage` is a utility struct used to remove a storage item from a specific pallet.
374///
375/// This struct is generic over three parameters:
376/// - `P` is a type that implements the [`Get`] trait for a static string, representing the pallet's
377///   name.
378/// - `S` is a type that implements the [`Get`] trait for a static string, representing the storage
379///   name.
380/// - `DbWeight` is a type that implements the [`Get`] trait for [`RuntimeDbWeight`], providing the
381///   weight for database operations.
382///
383/// On runtime upgrade, the `on_runtime_upgrade` function will clear the storage from the specified
384/// storage, logging the number of keys removed. If the `try-runtime` feature is enabled, the
385/// `pre_upgrade` and `post_upgrade` functions can be used to verify the storage removal before and
386/// after the upgrade.
387///
388/// # Examples:
389/// ```ignore
390/// construct_runtime! {
391/// 	pub enum Runtime
392/// 	{
393/// 		System: frame_system = 0,
394///
395/// 		SomePallet: pallet_something = 1,
396///
397/// 		YourOtherPallets...
398/// 	}
399/// };
400///
401/// parameter_types! {
402/// 		pub const SomePallet: &'static str = "SomePallet";
403/// 		pub const StorageAccounts: &'static str = "Accounts";
404/// 		pub const StorageAccountCount: &'static str = "AccountCount";
405/// }
406///
407/// pub type Migrations = (
408/// 	RemoveStorage<SomePallet, StorageAccounts, RocksDbWeight>,
409/// 	RemoveStorage<SomePallet, StorageAccountCount, RocksDbWeight>,
410/// 	AnyOtherMigrations...
411/// );
412///
413/// pub type Executive = frame_executive::Executive<
414/// 	Runtime,
415/// 	Block,
416/// 	frame_system::ChainContext<Runtime>,
417/// 	Runtime,
418/// 	Migrations
419/// >;
420/// ```
421///
422/// WARNING: `RemoveStorage` has no guard rails preventing it from bricking the chain if the
423/// operation of removing storage for the given pallet would exceed the block weight limit.
424///
425/// If your storage has too many keys to be removed in a single block, it is advised to wait for
426/// a multi-block scheduler currently under development which will allow for removal of storage
427/// items (and performing other heavy migrations) over multiple blocks
428/// (see <https://github.com/paritytech/substrate/issues/13690>).
429pub struct RemoveStorage<P: Get<&'static str>, S: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>(
430	PhantomData<(P, S, DbWeight)>,
431);
432impl<P: Get<&'static str>, S: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>
433	frame_support::traits::OnRuntimeUpgrade for RemoveStorage<P, S, DbWeight>
434{
435	fn on_runtime_upgrade() -> frame_support::weights::Weight {
436		let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
437		let keys_removed = match clear_prefix(&hashed_prefix, None) {
438			KillStorageResult::AllRemoved(value) => value,
439			KillStorageResult::SomeRemaining(value) => {
440				log::error!(
441					"`clear_prefix` failed to remove all keys for storage `{}` from pallet `{}`. THIS SHOULD NEVER HAPPEN! ๐Ÿšจ",
442					S::get(), P::get()
443				);
444				value
445			},
446		} as u64;
447
448		log::info!("Removed `{}` `{}` `{}` keys ๐Ÿงน", keys_removed, P::get(), S::get());
449
450		DbWeight::get().reads_writes(keys_removed + 1, keys_removed)
451	}
452
453	#[cfg(feature = "try-runtime")]
454	fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
455		use crate::storage::unhashed::contains_prefixed_key;
456
457		let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
458		match contains_prefixed_key(&hashed_prefix) {
459			true => log::info!("Found `{}` `{}` keys pre-removal ๐Ÿ‘€", P::get(), S::get()),
460			false => log::warn!(
461				"Migration RemoveStorage<{}, {}> can be removed (no keys found pre-removal).",
462				P::get(),
463				S::get()
464			),
465		};
466		Ok(Default::default())
467	}
468
469	#[cfg(feature = "try-runtime")]
470	fn post_upgrade(_state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
471		use crate::storage::unhashed::contains_prefixed_key;
472
473		let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
474		match contains_prefixed_key(&hashed_prefix) {
475			true => {
476				log::error!("`{}` `{}` has keys remaining post-removal โ—", P::get(), S::get());
477				return Err("Keys remaining post-removal, this should never happen ๐Ÿšจ".into())
478			},
479			false => log::info!("No `{}` `{}` keys found post-removal ๐ŸŽ‰", P::get(), S::get()),
480		};
481		Ok(())
482	}
483}
484
485/// A migration that can proceed in multiple steps.
486pub trait SteppedMigration {
487	/// The cursor type that stores the progress (aka. state) of this migration.
488	type Cursor: codec::FullCodec + codec::MaxEncodedLen;
489
490	/// The unique identifier type of this migration.
491	type Identifier: codec::FullCodec + codec::MaxEncodedLen;
492
493	/// The unique identifier of this migration.
494	///
495	/// If two migrations have the same identifier, then they are assumed to be identical.
496	fn id() -> Self::Identifier;
497
498	/// The maximum number of steps that this migration can take.
499	///
500	/// This can be used to enforce progress and prevent migrations becoming stuck forever. A
501	/// migration that exceeds its max steps is treated as failed. `None` means that there is no
502	/// limit.
503	fn max_steps() -> Option<u32> {
504		None
505	}
506
507	/// Try to migrate as much as possible with the given weight.
508	///
509	/// **ANY STORAGE CHANGES MUST BE ROLLED-BACK BY THE CALLER UPON ERROR.** This is necessary
510	/// since the caller cannot return a cursor in the error case. [`Self::transactional_step`] is
511	/// provided as convenience for a caller. A cursor of `None` implies that the migration is at
512	/// its end. A migration that once returned `Nonce` is guaranteed to never be called again.
513	fn step(
514		cursor: Option<Self::Cursor>,
515		meter: &mut WeightMeter,
516	) -> Result<Option<Self::Cursor>, SteppedMigrationError>;
517
518	/// Same as [`Self::step`], but rolls back pending changes in the error case.
519	fn transactional_step(
520		mut cursor: Option<Self::Cursor>,
521		meter: &mut WeightMeter,
522	) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
523		with_transaction_opaque_err(move || match Self::step(cursor, meter) {
524			Ok(new_cursor) => {
525				cursor = new_cursor;
526				sp_runtime::TransactionOutcome::Commit(Ok(cursor))
527			},
528			Err(err) => sp_runtime::TransactionOutcome::Rollback(Err(err)),
529		})
530		.map_err(|()| SteppedMigrationError::Failed)?
531	}
532
533	/// Hook for testing that is run before the migration is started.
534	///
535	/// Returns some bytes which are passed into `post_upgrade` after the migration is completed.
536	/// This is not run for the real migration, so panicking is not an issue here.
537	#[cfg(feature = "try-runtime")]
538	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
539		Ok(Vec::new())
540	}
541
542	/// Hook for testing that is run after the migration is completed.
543	///
544	/// Should be used to verify the state of the chain after the migration. The `state` parameter
545	/// is the return value from `pre_upgrade`. This is not run for the real migration, so panicking
546	/// is not an issue here.
547	#[cfg(feature = "try-runtime")]
548	fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
549		Ok(())
550	}
551}
552
553/// Error that can occur during a [`SteppedMigration`].
554#[derive(Debug, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo)]
555pub enum SteppedMigrationError {
556	// Transient errors:
557	/// The remaining weight is not enough to do anything.
558	///
559	/// Can be resolved by calling with at least `required` weight. Note that calling it with
560	/// exactly `required` weight could cause it to not make any progress.
561	InsufficientWeight {
562		/// Amount of weight required to make progress.
563		required: Weight,
564	},
565	// Permanent errors:
566	/// The migration cannot decode its cursor and therefore not proceed.
567	///
568	/// This should not happen unless (1) the migration itself returned an invalid cursor in a
569	/// previous iteration, (2) the storage got corrupted or (3) there is a bug in the caller's
570	/// code.
571	InvalidCursor,
572	/// The migration encountered a permanent error and cannot continue.
573	Failed,
574}
575
576/// A generic migration identifier that can be used by MBMs.
577///
578/// It is not required that migrations use this identifier type, but it can help.
579#[derive(MaxEncodedLen, Encode, Decode)]
580pub struct MigrationId<const N: usize> {
581	pub pallet_id: [u8; N],
582	pub version_from: u8,
583	pub version_to: u8,
584}
585
586/// Notification handler for status updates regarding Multi-Block-Migrations.
587#[impl_trait_for_tuples::impl_for_tuples(8)]
588pub trait MigrationStatusHandler {
589	/// Notifies of the start of a runtime migration.
590	fn started() {}
591
592	/// Notifies of the completion of a runtime migration.
593	fn completed() {}
594}
595
596/// Handles a failed runtime migration.
597///
598/// This should never happen, but is here for completeness.
599pub trait FailedMigrationHandler {
600	/// Infallibly handle a failed runtime migration.
601	///
602	/// Gets passed in the optional index of the migration in the batch that caused the failure.
603	/// Returning `None` means that no automatic handling should take place and the callee decides
604	/// in the implementation what to do.
605	fn failed(migration: Option<u32>) -> FailedMigrationHandling;
606}
607
608/// Do now allow any transactions to be processed after a runtime upgrade failed.
609///
610/// This is **not a sane default**, since it prevents governance intervention.
611pub struct FreezeChainOnFailedMigration;
612
613impl FailedMigrationHandler for FreezeChainOnFailedMigration {
614	fn failed(_migration: Option<u32>) -> FailedMigrationHandling {
615		FailedMigrationHandling::KeepStuck
616	}
617}
618
619/// Enter safe mode on a failed runtime upgrade.
620///
621/// This can be very useful to manually intervene and fix the chain state. `Else` is used in case
622/// that the safe mode could not be entered.
623pub struct EnterSafeModeOnFailedMigration<SM, Else: FailedMigrationHandler>(
624	PhantomData<(SM, Else)>,
625);
626
627impl<Else: FailedMigrationHandler, SM: SafeMode> FailedMigrationHandler
628	for EnterSafeModeOnFailedMigration<SM, Else>
629where
630	<SM as SafeMode>::BlockNumber: Bounded,
631{
632	fn failed(migration: Option<u32>) -> FailedMigrationHandling {
633		let entered = if SM::is_entered() {
634			SM::extend(Bounded::max_value())
635		} else {
636			SM::enter(Bounded::max_value())
637		};
638
639		// If we could not enter or extend safe mode (for whatever reason), then we try the next.
640		if entered.is_err() {
641			Else::failed(migration)
642		} else {
643			FailedMigrationHandling::KeepStuck
644		}
645	}
646}
647
648/// How to proceed after a runtime upgrade failed.
649///
650/// There is NO SANE DEFAULT HERE. All options are very dangerous and should be used with care.
651#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652pub enum FailedMigrationHandling {
653	/// Resume extrinsic processing of the chain. This will not resume the upgrade.
654	///
655	/// This should be supplemented with additional measures to ensure that the broken chain state
656	/// does not get further messed up by user extrinsics.
657	ForceUnstuck,
658	/// Set the cursor to `Stuck` and keep blocking extrinsics.
659	KeepStuck,
660	/// Don't do anything with the cursor and let the handler decide.
661	///
662	/// This can be useful in cases where the other two options would overwrite any changes that
663	/// were done by the handler to the cursor.
664	Ignore,
665}
666
667/// Something that can do multi step migrations.
668pub trait MultiStepMigrator {
669	/// Hint for whether [`Self::step`] should be called.
670	fn ongoing() -> bool;
671
672	/// Do the next step in the MBM process.
673	///
674	/// Must gracefully handle the case that it is currently not upgrading.
675	fn step() -> Weight;
676}
677
678impl MultiStepMigrator for () {
679	fn ongoing() -> bool {
680		false
681	}
682
683	fn step() -> Weight {
684		Weight::zero()
685	}
686}
687
688/// Multiple [`SteppedMigration`].
689pub trait SteppedMigrations {
690	/// The number of migrations that `Self` aggregates.
691	fn len() -> u32;
692
693	/// The `n`th [`SteppedMigration::id`].
694	///
695	/// Is guaranteed to return `Some` if `n < Self::len()`. Calling this with any index larger or
696	/// equal to `Self::len()` MUST return `None`.
697	fn nth_id(n: u32) -> Option<Vec<u8>>;
698
699	/// The [`SteppedMigration::max_steps`] of the `n`th migration.
700	///
701	/// Is guaranteed to return `Some` if `n < Self::len()`.
702	fn nth_max_steps(n: u32) -> Option<Option<u32>>;
703
704	/// Do a [`SteppedMigration::step`] on the `n`th migration.
705	///
706	/// Is guaranteed to return `Some` if `n < Self::len()`.
707	fn nth_step(
708		n: u32,
709		cursor: Option<Vec<u8>>,
710		meter: &mut WeightMeter,
711	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>>;
712
713	/// Do a [`SteppedMigration::transactional_step`] on the `n`th migration.
714	///
715	/// Is guaranteed to return `Some` if `n < Self::len()`.
716	fn nth_transactional_step(
717		n: u32,
718		cursor: Option<Vec<u8>>,
719		meter: &mut WeightMeter,
720	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>>;
721
722	/// Call the pre-upgrade hooks of the `n`th migration.
723	///
724	/// Returns `None` if the index is out of bounds.
725	#[cfg(feature = "try-runtime")]
726	fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>>;
727
728	/// Call the post-upgrade hooks of the `n`th migration.
729	///
730	/// Returns `None` if the index is out of bounds.
731	#[cfg(feature = "try-runtime")]
732	fn nth_post_upgrade(n: u32, _state: Vec<u8>)
733		-> Option<Result<(), sp_runtime::TryRuntimeError>>;
734
735	/// The maximal encoded length across all cursors.
736	fn cursor_max_encoded_len() -> usize;
737
738	/// The maximal encoded length across all identifiers.
739	fn identifier_max_encoded_len() -> usize;
740
741	/// Assert the integrity of the migrations.
742	///
743	/// Should be executed as part of a test prior to runtime usage. May or may not need
744	/// externalities.
745	#[cfg(feature = "std")]
746	fn integrity_test() -> Result<(), &'static str> {
747		use crate::ensure;
748		let l = Self::len();
749
750		for n in 0..l {
751			ensure!(Self::nth_id(n).is_some(), "id is None");
752			ensure!(Self::nth_max_steps(n).is_some(), "steps is None");
753
754			// The cursor that we use does not matter. Hence use empty.
755			ensure!(
756				Self::nth_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(),
757				"steps is None"
758			);
759			ensure!(
760				Self::nth_transactional_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(),
761				"steps is None"
762			);
763		}
764
765		Ok(())
766	}
767}
768
769impl SteppedMigrations for () {
770	fn len() -> u32 {
771		0
772	}
773
774	fn nth_id(_n: u32) -> Option<Vec<u8>> {
775		None
776	}
777
778	fn nth_max_steps(_n: u32) -> Option<Option<u32>> {
779		None
780	}
781
782	fn nth_step(
783		_n: u32,
784		_cursor: Option<Vec<u8>>,
785		_meter: &mut WeightMeter,
786	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
787		None
788	}
789
790	fn nth_transactional_step(
791		_n: u32,
792		_cursor: Option<Vec<u8>>,
793		_meter: &mut WeightMeter,
794	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
795		None
796	}
797
798	#[cfg(feature = "try-runtime")]
799	fn nth_pre_upgrade(_n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
800		Some(Ok(Vec::new()))
801	}
802
803	#[cfg(feature = "try-runtime")]
804	fn nth_post_upgrade(
805		_n: u32,
806		_state: Vec<u8>,
807	) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
808		Some(Ok(()))
809	}
810
811	fn cursor_max_encoded_len() -> usize {
812		0
813	}
814
815	fn identifier_max_encoded_len() -> usize {
816		0
817	}
818}
819
820// A collection consisting of only a single migration.
821impl<T: SteppedMigration> SteppedMigrations for T {
822	fn len() -> u32 {
823		1
824	}
825
826	fn nth_id(n: u32) -> Option<Vec<u8>> {
827		n.is_zero()
828			.then(|| T::id().encode())
829			.defensive_proof("nth_id should only be called with n==0")
830	}
831
832	fn nth_max_steps(n: u32) -> Option<Option<u32>> {
833		// It should be generally fine to call with n>0, but the code should not attempt to.
834		n.is_zero()
835			.then(|| T::max_steps())
836			.defensive_proof("nth_max_steps should only be called with n==0")
837	}
838
839	fn nth_step(
840		n: u32,
841		cursor: Option<Vec<u8>>,
842		meter: &mut WeightMeter,
843	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
844		if !n.is_zero() {
845			defensive!("nth_step should only be called with n==0");
846			return None
847		}
848
849		let cursor = match cursor {
850			Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) {
851				Ok(cursor) => Some(cursor),
852				Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)),
853			},
854			None => None,
855		};
856
857		Some(T::step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())))
858	}
859
860	fn nth_transactional_step(
861		n: u32,
862		cursor: Option<Vec<u8>>,
863		meter: &mut WeightMeter,
864	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
865		if n != 0 {
866			defensive!("nth_transactional_step should only be called with n==0");
867			return None
868		}
869
870		let cursor = match cursor {
871			Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) {
872				Ok(cursor) => Some(cursor),
873				Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)),
874			},
875			None => None,
876		};
877
878		Some(
879			T::transactional_step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())),
880		)
881	}
882
883	#[cfg(feature = "try-runtime")]
884	fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
885		if n != 0 {
886			defensive!("nth_pre_upgrade should only be called with n==0");
887		}
888
889		Some(T::pre_upgrade())
890	}
891
892	#[cfg(feature = "try-runtime")]
893	fn nth_post_upgrade(n: u32, state: Vec<u8>) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
894		if n != 0 {
895			defensive!("nth_post_upgrade should only be called with n==0");
896		}
897		Some(T::post_upgrade(state))
898	}
899
900	fn cursor_max_encoded_len() -> usize {
901		T::Cursor::max_encoded_len()
902	}
903
904	fn identifier_max_encoded_len() -> usize {
905		T::Identifier::max_encoded_len()
906	}
907}
908
909#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
910impl SteppedMigrations for Tuple {
911	fn len() -> u32 {
912		for_tuples!( #( Tuple::len() )+* )
913	}
914
915	fn nth_id(n: u32) -> Option<Vec<u8>> {
916		let mut i = 0;
917
918		for_tuples!( #(
919			if (i + Tuple::len()) > n {
920				return Tuple::nth_id(n - i)
921			}
922
923			i += Tuple::len();
924		)* );
925
926		None
927	}
928
929	fn nth_step(
930		n: u32,
931		cursor: Option<Vec<u8>>,
932		meter: &mut WeightMeter,
933	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
934		let mut i = 0;
935
936		for_tuples!( #(
937			if (i + Tuple::len()) > n {
938				return Tuple::nth_step(n - i, cursor, meter)
939			}
940
941			i += Tuple::len();
942		)* );
943
944		None
945	}
946
947	fn nth_transactional_step(
948		n: u32,
949		cursor: Option<Vec<u8>>,
950		meter: &mut WeightMeter,
951	) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
952		let mut i = 0;
953
954		for_tuples! ( #(
955			if (i + Tuple::len()) > n {
956				return Tuple::nth_transactional_step(n - i, cursor, meter)
957			}
958
959			i += Tuple::len();
960		)* );
961
962		None
963	}
964
965	#[cfg(feature = "try-runtime")]
966	fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
967		let mut i = 0;
968
969		for_tuples! ( #(
970			if (i + Tuple::len()) > n {
971				return Tuple::nth_pre_upgrade(n - i)
972			}
973
974			i += Tuple::len();
975		)* );
976
977		None
978	}
979
980	#[cfg(feature = "try-runtime")]
981	fn nth_post_upgrade(n: u32, state: Vec<u8>) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
982		let mut i = 0;
983
984		for_tuples! ( #(
985			if (i + Tuple::len()) > n {
986				return Tuple::nth_post_upgrade(n - i, state)
987			}
988
989			i += Tuple::len();
990		)* );
991
992		None
993	}
994
995	fn nth_max_steps(n: u32) -> Option<Option<u32>> {
996		let mut i = 0;
997
998		for_tuples!( #(
999			if (i + Tuple::len()) > n {
1000				return Tuple::nth_max_steps(n - i)
1001			}
1002
1003			i += Tuple::len();
1004		)* );
1005
1006		None
1007	}
1008
1009	fn cursor_max_encoded_len() -> usize {
1010		let mut max_len = 0;
1011
1012		for_tuples!( #(
1013			max_len = max_len.max(Tuple::cursor_max_encoded_len());
1014		)* );
1015
1016		max_len
1017	}
1018
1019	fn identifier_max_encoded_len() -> usize {
1020		let mut max_len = 0;
1021
1022		for_tuples!( #(
1023			max_len = max_len.max(Tuple::identifier_max_encoded_len());
1024		)* );
1025
1026		max_len
1027	}
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032	use super::*;
1033	use crate::{assert_ok, storage::unhashed};
1034
1035	#[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)]
1036	pub enum Either<L, R> {
1037		Left(L),
1038		Right(R),
1039	}
1040
1041	pub struct M0;
1042	impl SteppedMigration for M0 {
1043		type Cursor = ();
1044		type Identifier = u8;
1045
1046		fn id() -> Self::Identifier {
1047			0
1048		}
1049
1050		fn step(
1051			_cursor: Option<Self::Cursor>,
1052			_meter: &mut WeightMeter,
1053		) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1054			log::info!("M0");
1055			unhashed::put(&[0], &());
1056			Ok(None)
1057		}
1058	}
1059
1060	pub struct M1;
1061	impl SteppedMigration for M1 {
1062		type Cursor = ();
1063		type Identifier = u8;
1064
1065		fn id() -> Self::Identifier {
1066			1
1067		}
1068
1069		fn step(
1070			_cursor: Option<Self::Cursor>,
1071			_meter: &mut WeightMeter,
1072		) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1073			log::info!("M1");
1074			unhashed::put(&[1], &());
1075			Ok(None)
1076		}
1077
1078		fn max_steps() -> Option<u32> {
1079			Some(1)
1080		}
1081	}
1082
1083	pub struct M2;
1084	impl SteppedMigration for M2 {
1085		type Cursor = ();
1086		type Identifier = u8;
1087
1088		fn id() -> Self::Identifier {
1089			2
1090		}
1091
1092		fn step(
1093			_cursor: Option<Self::Cursor>,
1094			_meter: &mut WeightMeter,
1095		) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1096			log::info!("M2");
1097			unhashed::put(&[2], &());
1098			Ok(None)
1099		}
1100
1101		fn max_steps() -> Option<u32> {
1102			Some(2)
1103		}
1104	}
1105
1106	pub struct F0;
1107	impl SteppedMigration for F0 {
1108		type Cursor = ();
1109		type Identifier = u8;
1110
1111		fn id() -> Self::Identifier {
1112			3
1113		}
1114
1115		fn step(
1116			_cursor: Option<Self::Cursor>,
1117			_meter: &mut WeightMeter,
1118		) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1119			log::info!("F0");
1120			unhashed::put(&[3], &());
1121			Err(SteppedMigrationError::Failed)
1122		}
1123	}
1124
1125	// Three migrations combined to execute in order:
1126	type Triple = (M0, (M1, M2));
1127	// Six migrations, just concatenating the ones from before:
1128	type Hextuple = (Triple, Triple);
1129
1130	#[test]
1131	fn singular_migrations_work() {
1132		assert_eq!(M0::max_steps(), None);
1133		assert_eq!(M1::max_steps(), Some(1));
1134		assert_eq!(M2::max_steps(), Some(2));
1135
1136		assert_eq!(<(M0, M1)>::nth_max_steps(0), Some(None));
1137		assert_eq!(<(M0, M1)>::nth_max_steps(1), Some(Some(1)));
1138		assert_eq!(<(M0, M1, M2)>::nth_max_steps(2), Some(Some(2)));
1139
1140		assert_eq!(<(M0, M1)>::nth_max_steps(2), None);
1141	}
1142
1143	#[test]
1144	fn tuple_migrations_work() {
1145		assert_eq!(<() as SteppedMigrations>::len(), 0);
1146		assert_eq!(<((), ((), ())) as SteppedMigrations>::len(), 0);
1147		assert_eq!(<Triple as SteppedMigrations>::len(), 3);
1148		assert_eq!(<Hextuple as SteppedMigrations>::len(), 6);
1149
1150		// Check the IDs. The index specific functions all return an Option,
1151		// to account for the out-of-range case.
1152		assert_eq!(<Triple as SteppedMigrations>::nth_id(0), Some(0u8.encode()));
1153		assert_eq!(<Triple as SteppedMigrations>::nth_id(1), Some(1u8.encode()));
1154		assert_eq!(<Triple as SteppedMigrations>::nth_id(2), Some(2u8.encode()));
1155
1156		sp_io::TestExternalities::default().execute_with(|| {
1157			for n in 0..3 {
1158				<Triple as SteppedMigrations>::nth_step(
1159					n,
1160					Default::default(),
1161					&mut WeightMeter::new(),
1162				);
1163			}
1164		});
1165	}
1166
1167	#[test]
1168	fn integrity_test_works() {
1169		sp_io::TestExternalities::default().execute_with(|| {
1170			assert_ok!(<() as SteppedMigrations>::integrity_test());
1171			assert_ok!(<M0 as SteppedMigrations>::integrity_test());
1172			assert_ok!(<M1 as SteppedMigrations>::integrity_test());
1173			assert_ok!(<M2 as SteppedMigrations>::integrity_test());
1174			assert_ok!(<Triple as SteppedMigrations>::integrity_test());
1175			assert_ok!(<Hextuple as SteppedMigrations>::integrity_test());
1176		});
1177	}
1178
1179	#[test]
1180	fn transactional_rollback_works() {
1181		sp_io::TestExternalities::default().execute_with(|| {
1182			assert_ok!(<(M0, F0) as SteppedMigrations>::nth_transactional_step(
1183				0,
1184				Default::default(),
1185				&mut WeightMeter::new()
1186			)
1187			.unwrap());
1188			assert!(unhashed::exists(&[0]));
1189
1190			let _g = crate::StorageNoopGuard::new();
1191			assert!(<(M0, F0) as SteppedMigrations>::nth_transactional_step(
1192				1,
1193				Default::default(),
1194				&mut WeightMeter::new()
1195			)
1196			.unwrap()
1197			.is_err());
1198			assert!(<(F0, M1) as SteppedMigrations>::nth_transactional_step(
1199				0,
1200				Default::default(),
1201				&mut WeightMeter::new()
1202			)
1203			.unwrap()
1204			.is_err());
1205		});
1206	}
1207}