pallet_example_single_block_migrations/migrations/v1.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
24use frame_support::{
25 storage_alias,
26 traits::{Get, UncheckedOnRuntimeUpgrade},
27};
28
29#[cfg(feature = "try-runtime")]
30use alloc::vec::Vec;
31
32/// Collection of storage item formats from the previous storage version.
33///
34/// Required so we can read values in the v0 storage format during the migration.
35mod v0 {
36 use super::*;
37
38 /// V0 type for [`crate::Value`].
39 #[storage_alias]
40 pub type Value<T: crate::Config> = StorageValue<crate::Pallet<T>, u32>;
41}
42
43/// Implements [`UncheckedOnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1.
44///
45/// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to
46/// contain the struct [`crate::CurrentAndPreviousValue`].
47///
48/// In this migration, update the on-chain storage for the pallet to reflect the new storage
49/// layout.
50pub struct InnerMigrateV0ToV1<T: crate::Config>(core::marker::PhantomData<T>);
51
52impl<T: crate::Config> UncheckedOnRuntimeUpgrade for InnerMigrateV0ToV1<T> {
53 /// Return the existing [`crate::Value`] so we can check that it was correctly set in
54 /// `InnerMigrateV0ToV1::post_upgrade`.
55 #[cfg(feature = "try-runtime")]
56 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
57 use codec::Encode;
58
59 // Access the old value using the `storage_alias` type
60 let old_value = v0::Value::<T>::get();
61 // Return it as an encoded `Vec<u8>`
62 Ok(old_value.encode())
63 }
64
65 /// Migrate the storage from V0 to V1.
66 ///
67 /// - If the value doesn't exist, there is nothing to do.
68 /// - If the value exists, it is read and then written back to storage inside a
69 /// [`crate::CurrentAndPreviousValue`].
70 fn on_runtime_upgrade() -> frame_support::weights::Weight {
71 // Read the old value from storage
72 if let Some(old_value) = v0::Value::<T>::take() {
73 // Write the new value to storage
74 let new = crate::CurrentAndPreviousValue { current: old_value, previous: None };
75 crate::Value::<T>::put(new);
76 // One read + write for taking the old value, and one write for setting the new value
77 T::DbWeight::get().reads_writes(1, 2)
78 } else {
79 // No writes since there was no old value, just one read for checking
80 T::DbWeight::get().reads(1)
81 }
82 }
83
84 /// Verifies the storage was migrated correctly.
85 ///
86 /// - If there was no old value, the new value should not be set.
87 /// - If there was an old value, the new value should be a [`crate::CurrentAndPreviousValue`].
88 #[cfg(feature = "try-runtime")]
89 fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
90 use codec::Decode;
91 use frame_support::ensure;
92
93 let maybe_old_value = Option::<u32>::decode(&mut &state[..]).map_err(|_| {
94 sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage")
95 })?;
96
97 match maybe_old_value {
98 Some(old_value) => {
99 let expected_new_value =
100 crate::CurrentAndPreviousValue { current: old_value, previous: None };
101 let actual_new_value = crate::Value::<T>::get();
102
103 ensure!(actual_new_value.is_some(), "New value not set");
104 ensure!(
105 actual_new_value == Some(expected_new_value),
106 "New value not set correctly"
107 );
108 },
109 None => {
110 ensure!(crate::Value::<T>::get().is_none(), "New value unexpectedly set");
111 },
112 };
113 Ok(())
114 }
115}
116
117/// [`UncheckedOnRuntimeUpgrade`] implementation [`InnerMigrateV0ToV1`] wrapped in a
118/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
119/// - The migration only runs once when the on-chain storage version is 0
120/// - The on-chain storage version is updated to `1` after the migration executes
121/// - Reads/Writes from checking/settings the on-chain storage version are accounted for
122pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
123 0, // The migration will only execute when the on-chain storage version is 0
124 1, // The on-chain storage version will be set to 1 after the migration is complete
125 InnerMigrateV0ToV1<T>,
126 crate::pallet::Pallet<T>,
127 <T as frame_system::Config>::DbWeight,
128>;
129
130/// Tests for our migration.
131///
132/// When writing migration tests, it is important to check:
133/// 1. `on_runtime_upgrade` returns the expected weight
134/// 2. `post_upgrade` succeeds when given the bytes returned by `pre_upgrade`
135/// 3. The storage is in the expected state after the migration
136#[cfg(any(all(feature = "try-runtime", test), doc))]
137mod test {
138 use self::InnerMigrateV0ToV1;
139 use super::*;
140 use crate::mock::{new_test_ext, MockRuntime};
141 use frame_support::assert_ok;
142
143 #[test]
144 fn handles_no_existing_value() {
145 new_test_ext().execute_with(|| {
146 // By default, no value should be set. Verify this assumption.
147 assert!(crate::Value::<MockRuntime>::get().is_none());
148 assert!(v0::Value::<MockRuntime>::get().is_none());
149
150 // Get the pre_upgrade bytes
151 let bytes = match InnerMigrateV0ToV1::<MockRuntime>::pre_upgrade() {
152 Ok(bytes) => bytes,
153 Err(e) => panic!("pre_upgrade failed: {e:?}"),
154 };
155
156 // Execute the migration
157 let weight = InnerMigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
158
159 // Verify post_upgrade succeeds
160 assert_ok!(InnerMigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
161
162 // The weight should be just 1 read for trying to access the old value.
163 assert_eq!(weight, <MockRuntime as frame_system::Config>::DbWeight::get().reads(1));
164
165 // After the migration, no value should have been set.
166 assert!(crate::Value::<MockRuntime>::get().is_none());
167 })
168 }
169
170 #[test]
171 fn handles_existing_value() {
172 new_test_ext().execute_with(|| {
173 // Set up an initial value
174 let initial_value = 42;
175 v0::Value::<MockRuntime>::put(initial_value);
176
177 // Get the pre_upgrade bytes
178 let bytes = match InnerMigrateV0ToV1::<MockRuntime>::pre_upgrade() {
179 Ok(bytes) => bytes,
180 Err(e) => panic!("pre_upgrade failed: {e:?}"),
181 };
182
183 // Execute the migration
184 let weight = InnerMigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
185
186 // Verify post_upgrade succeeds
187 assert_ok!(InnerMigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
188
189 // The weight used should be 1 read for the old value, and 1 write for the new
190 // value.
191 assert_eq!(
192 weight,
193 <MockRuntime as frame_system::Config>::DbWeight::get().reads_writes(1, 2)
194 );
195
196 // After the migration, the new value should be set as the `current` value.
197 let expected_new_value =
198 crate::CurrentAndPreviousValue { current: initial_value, previous: None };
199 assert_eq!(crate::Value::<MockRuntime>::get(), Some(expected_new_value));
200 })
201 }
202}