referrerpolicy=no-referrer-when-downgrade

pallet_example_single_block_migrations/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: MIT-0
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy of
7// this software and associated documentation files (the "Software"), to deal in
8// the Software without restriction, including without limitation the rights to
9// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10// of the Software, and to permit persons to whom the Software is furnished to do
11// so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in all
14// copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23
24//! # Single Block Migration Example Pallet
25//!
26//! An example pallet demonstrating best-practices for writing single-block migrations in the
27//! context of upgrading pallet storage.
28//!
29//! ## Forewarning
30//!
31//! Single block migrations **MUST** execute in a single block, therefore when executed on a
32//! parachain are only appropriate when guaranteed to not exceed block weight limits. If a
33//! parachain submits a block that exceeds the block weight limit it will **brick the chain**!
34//!
35//! If weight is a concern or you are not sure which type of migration to use, you should probably
36//! use a multi-block migration.
37//!
38//! See the [`pallet-example-mbm`](https://paritytech.github.io/polkadot-sdk/master/pallet_example_mbm/index.html)
39//! pallet for an example of a multi-block migration.
40//!
41//! ## Pallet Overview
42//!
43//! This example pallet contains a single storage item [`Value`](pallet::Value), which may be set by
44//! any signed origin by calling the [`set_value`](crate::Call::set_value) extrinsic.
45//!
46//! For the purposes of this exercise, we imagine that in [`StorageVersion`] V0 of this pallet
47//! [`Value`](pallet::Value) is a `u32`, and this what is currently stored on-chain.
48//!
49//! ```ignore
50//! // (Old) Storage Version V0 representation of `Value`
51//! #[pallet::storage]
52//! pub type Value<T: Config> = StorageValue<_, u32>;
53//! ```
54//!
55//! In [`StorageVersion`] V1 of the pallet a new struct [`CurrentAndPreviousValue`] is introduced:
56#![doc = docify::embed!("src/lib.rs", CurrentAndPreviousValue)]
57//! and [`Value`](pallet::Value) is updated to store this new struct instead of a `u32`:
58#![doc = docify::embed!("src/lib.rs", Value)]
59//! In StorageVersion V1 of the pallet when [`set_value`](crate::Call::set_value) is called, the
60//! new value is stored in the `current` field of [`CurrentAndPreviousValue`], and the previous
61//! value (if it exists) is stored in the `previous` field.
62#![doc = docify::embed!("src/lib.rs", pallet_calls)]
63//! ## Why a migration is necessary
64//!
65//! Without a migration, there will be a discrepancy between the on-chain storage for [`Value`] (in
66//! V0 it is a `u32`) and the current storage for [`Value`] (in V1 it was changed to a
67//! [`CurrentAndPreviousValue`] struct).
68//!
69//! The on-chain storage for [`Value`] would be a `u32` but the runtime would try to read it as a
70//! [`CurrentAndPreviousValue`]. This would result in unacceptable undefined behavior.
71//!
72//! ## Adding a migration module
73//!
74//! Writing a pallets migrations in a separate module is strongly recommended.
75//!
76//! Here's how the migration module is defined for this pallet:
77//!
78//! ```text
79//! substrate/frame/examples/single-block-migrations/src/
80//! ├── lib.rs       <-- pallet definition
81//! ├── Cargo.toml   <-- pallet manifest
82//! └── migrations/
83//!    ├── mod.rs    <-- migrations module definition
84//!    └── v1.rs     <-- migration logic for the V0 to V1 transition
85//! ```
86//!
87//! This structure allows keeping migration logic separate from the pallet logic and
88//! easily adding new migrations in the future.
89//!
90//! ## Writing the Migration
91//!
92//! All code related to the migration can be found under
93//! [`v1.rs`](migrations::v1).
94//!
95//! See the migration source code for detailed comments.
96//!
97//! Here's a brief overview of modules and types defined in `v1.rs`:
98//!
99//! ### `mod v0`
100//!
101//! Here we define a [`storage_alias`](frame_support::storage_alias) for the old v0 [`Value`]
102//! format.
103//!
104//! This allows reading the old v0 value from storage during the migration.
105//!
106//! ### `InnerMigrateV0ToV1`
107//!
108//! Here we define our raw migration logic,
109//! `InnerMigrateV0ToV1` which implements the [`UncheckedOnRuntimeUpgrade`] trait.
110//!
111//! #### Why [`UncheckedOnRuntimeUpgrade`]?
112//!
113//! Otherwise, we would have two implementations of [`OnRuntimeUpgrade`] which could be confusing,
114//! and may lead to accidentally using the wrong one.
115//!
116//! #### Standalone Struct or Pallet Hook?
117//!
118//! Note that the storage migration logic is attached to a standalone struct implementing
119//! [`UncheckedOnRuntimeUpgrade`], rather than implementing the
120//! [`Hooks::on_runtime_upgrade`](frame_support::traits::Hooks::on_runtime_upgrade) hook directly on
121//! the pallet. The pallet hook is better suited for special types of logic that need to execute on
122//! every runtime upgrade, but not so much for one-off storage migrations.
123//!
124//! ### `MigrateV0ToV1`
125//!
126//! Here, `InnerMigrateV0ToV1` is wrapped in a
127//! [`VersionedMigration`] to define
128//! [`MigrateV0ToV1`](crate::migrations::v1::MigrateV0ToV1), which may be used
129//! in runtimes.
130//!
131//! Using [`VersionedMigration`] ensures that
132//! - The migration only runs once when the on-chain storage version is `0`
133//! - The on-chain storage version is updated to `1` after the migration executes
134//! - Reads and writes from checking and setting the on-chain storage version are accounted for in
135//!   the final [`Weight`](frame_support::weights::Weight)
136//!
137//! ### `mod test`
138//!
139//! Here basic unit tests are defined for the migration.
140//!
141//! When writing migration tests, don't forget to check:
142//! - `on_runtime_upgrade` returns the expected weight
143//! - `post_upgrade` succeeds when given the bytes returned by `pre_upgrade`
144//! - Pallet storage is in the expected state after the migration
145//!
146//! [`VersionedMigration`]: frame_support::migrations::VersionedMigration
147//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion
148//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade
149//! [`UncheckedOnRuntimeUpgrade`]: frame_support::traits::UncheckedOnRuntimeUpgrade
150//! [`MigrateV0ToV1`]: crate::migrations::v1::MigrateV0ToV1
151
152// We make sure this pallet uses `no_std` for compiling to Wasm.
153#![cfg_attr(not(feature = "std"), no_std)]
154// allow non-camel-case names for storage version V0 value
155#![allow(non_camel_case_types)]
156
157// Re-export pallet items so that they can be accessed from the crate namespace.
158pub use pallet::*;
159
160// Export migrations so they may be used in the runtime.
161pub mod migrations;
162#[doc(hidden)]
163mod mock;
164
165extern crate alloc;
166
167use codec::{Decode, Encode, MaxEncodedLen};
168use frame_support::traits::StorageVersion;
169
170/// Example struct holding the most recently set [`u32`] and the
171/// second most recently set [`u32`] (if one existed).
172#[docify::export]
173#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, scale_info::TypeInfo, MaxEncodedLen)]
174pub struct CurrentAndPreviousValue {
175	/// The most recently set value.
176	pub current: u32,
177	/// The previous value, if one existed.
178	pub previous: Option<u32>,
179}
180
181// Pallet for demonstrating storage migrations.
182#[frame_support::pallet(dev_mode)]
183pub mod pallet {
184	use super::*;
185	use frame_support::pallet_prelude::*;
186	use frame_system::pallet_prelude::*;
187
188	/// Define the current [`StorageVersion`] of the pallet.
189	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
190
191	#[pallet::pallet]
192	#[pallet::storage_version(STORAGE_VERSION)]
193	pub struct Pallet<T>(_);
194
195	#[pallet::config]
196	pub trait Config: frame_system::Config {}
197
198	/// [`StorageVersion`] V1 of [`Value`].
199	///
200	/// Currently used.
201	#[docify::export]
202	#[pallet::storage]
203	pub type Value<T: Config> = StorageValue<_, CurrentAndPreviousValue>;
204
205	#[docify::export(pallet_calls)]
206	#[pallet::call]
207	impl<T: Config> Pallet<T> {
208		pub fn set_value(origin: OriginFor<T>, value: u32) -> DispatchResult {
209			ensure_signed(origin)?;
210
211			let previous = Value::<T>::get().map(|v| v.current);
212			let new_struct = CurrentAndPreviousValue { current: value, previous };
213			<Value<T>>::put(new_struct);
214
215			Ok(())
216		}
217	}
218}