referrerpolicy=no-referrer-when-downgrade

pallet_core_fellowship/
migration.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
18//! Storage migrations for `pallet-core-fellowship`.
19
20use super::*;
21use frame_support::{
22	pallet_prelude::*,
23	storage_alias,
24	traits::{DefensiveTruncateFrom, UncheckedOnRuntimeUpgrade},
25	BoundedVec,
26};
27
28#[cfg(feature = "try-runtime")]
29use alloc::vec::Vec;
30#[cfg(feature = "try-runtime")]
31use sp_runtime::TryRuntimeError;
32
33mod v0 {
34	use frame_system::pallet_prelude::BlockNumberFor;
35
36	use super::*;
37
38	#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, Debug)]
39	pub struct ParamsType<Balance, BlockNumber, const RANKS: usize> {
40		pub active_salary: [Balance; RANKS],
41		pub passive_salary: [Balance; RANKS],
42		pub demotion_period: [BlockNumber; RANKS],
43		pub min_promotion_period: [BlockNumber; RANKS],
44		pub offboard_timeout: BlockNumber,
45	}
46
47	impl<Balance: Default + Copy, BlockNumber: Default + Copy, const RANKS: usize> Default
48		for ParamsType<Balance, BlockNumber, RANKS>
49	{
50		fn default() -> Self {
51			Self {
52				active_salary: [Balance::default(); RANKS],
53				passive_salary: [Balance::default(); RANKS],
54				demotion_period: [BlockNumber::default(); RANKS],
55				min_promotion_period: [BlockNumber::default(); RANKS],
56				offboard_timeout: BlockNumber::default(),
57			}
58		}
59	}
60
61	/// Number of available ranks from old version.
62	pub(crate) const RANK_COUNT: usize = 9;
63
64	pub type ParamsOf<T, I> = ParamsType<<T as Config<I>>::Balance, BlockNumberFor<T>, RANK_COUNT>;
65
66	/// V0 type for [`crate::Params`].
67	#[storage_alias]
68	pub type Params<T: Config<I>, I: 'static> =
69		StorageValue<Pallet<T, I>, ParamsOf<T, I>, ValueQuery>;
70}
71
72pub mod v1 {
73	use super::*;
74	use frame_system::pallet_prelude::BlockNumberFor as LocalBlockNumberFor;
75
76	pub type MemberStatusOf<T> = MemberStatus<LocalBlockNumberFor<T>>;
77	/// V1 type for [`crate::Member`].
78	#[storage_alias]
79	pub type Member<T: Config<I>, I: 'static> = StorageMap<
80		Pallet<T, I>,
81		Twox64Concat,
82		<T as frame_system::Config>::AccountId,
83		MemberStatusOf<T>,
84		OptionQuery,
85	>;
86
87	pub type ParamsOf<T, I> = ParamsType<
88		<T as Config<I>>::Balance,
89		LocalBlockNumberFor<T>,
90		ConvertU16ToU32<<T as Config<I>>::MaxRank>,
91	>;
92	/// V1 type for [`crate::Params`].
93	#[storage_alias]
94	pub type Params<T: Config<I>, I: 'static> =
95		StorageValue<Pallet<T, I>, ParamsOf<T, I>, ValueQuery>;
96
97	pub struct MigrateToV1<T, I = ()>(PhantomData<(T, I)>);
98	impl<T: Config<I>, I: 'static> UncheckedOnRuntimeUpgrade for MigrateToV1<T, I> {
99		#[cfg(feature = "try-runtime")]
100		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
101			ensure!(
102				T::MaxRank::get() as usize >= v0::RANK_COUNT,
103				"pallet-core-fellowship: new bound should not truncate"
104			);
105			Ok(Default::default())
106		}
107
108		fn on_runtime_upgrade() -> frame_support::weights::Weight {
109			// Read the old value from storage
110			let old_value = v0::Params::<T, I>::take();
111			// Write the new value to storage
112			let new = ParamsOf::<T, I> {
113				active_salary: BoundedVec::defensive_truncate_from(
114					old_value.active_salary.to_vec(),
115				),
116				passive_salary: BoundedVec::defensive_truncate_from(
117					old_value.passive_salary.to_vec(),
118				),
119				demotion_period: BoundedVec::defensive_truncate_from(
120					old_value.demotion_period.to_vec(),
121				),
122				min_promotion_period: BoundedVec::defensive_truncate_from(
123					old_value.min_promotion_period.to_vec(),
124				),
125				offboard_timeout: old_value.offboard_timeout,
126			};
127			Params::<T, I>::put(new);
128			T::DbWeight::get().reads_writes(1, 1)
129		}
130	}
131}
132
133pub mod v2 {
134	use super::*;
135	use crate::BlockNumberFor as NewBlockNumberFor;
136	use frame_system::pallet_prelude::BlockNumberFor as LocalBlockNumberFor;
137
138	/// Converts previous (local) block number into the new one. May just be identity functions
139	/// if sticking with the local block number.
140	pub trait ConvertBlockNumber<L, N> {
141		/// Converts to the new type and finds the equivalent moment in time as from the view of the
142		/// new block provider
143		///
144		/// # Example usage
145		///
146		/// ```rust,ignore
147		/// // Let's say you are a parachain and switching block providers to the relay chain.
148		/// // This will return what the relay block number was at the moment the previous provider's
149		/// // number was `local_moment`.
150		/// fn equivalent_moment_in_time(local_moment: u32) -> u32 {
151		/// 	// How long it's been since 'local_moment' from the parachains pov.
152		/// 	let local_block_number = System::block_number();
153		/// 	let local_duration = u32::abs_diff(local_block_number, local_moment);
154		/// 	// How many blocks that is from the relay's pov.
155		/// 	let relay_duration = Self::equivalent_block_duration(local_duration);
156		/// 	// What the relay block number must have been at 'local_moment'.
157		/// 	let relay_block_number = ParachainSystem::last_relay_block_number();
158		/// 	if local_block_number >= local_moment {
159		/// 		// Moment was in past.
160		/// 		relay_block_number.saturating_sub(relay_duration)
161		/// 	} else {
162		/// 		// Moment is in future.
163		/// 		relay_block_number.saturating_add(relay_duration)
164		/// 	}
165		/// }
166		/// ```
167		fn equivalent_moment_in_time(local_moment: L) -> N;
168
169		/// Returns the equivalent number of new blocks it would take to fulfill the same
170		/// amount of time in seconds as the old blocks.
171		///
172		/// For instance - If you previously had 12s blocks and are now following the relay chain's
173		/// 6, one local block is equivalent to 2 relay blocks in duration.
174		///
175		/// # Visualized
176		///
177		/// ```text
178		/// 
179		///     6s         6s
180		/// |---------||---------|
181		///
182		///          12s
183		/// |--------------------|
184		///
185		/// ^ Two 6s relay blocks passed per one 12s local block.
186		/// ```  
187		///
188		/// # Example Usage
189		///
190		/// ```rust,ignore
191		/// // Following the scenerio above.
192		/// fn equivalent_block_duration(local_duration: u32) -> u32 {
193		/// 	local_duration.saturating_mul(2)
194		/// }
195		/// ```
196		fn equivalent_block_duration(local_duration: L) -> N;
197	}
198
199	pub struct MigrateToV2<T, BlockNumberConverter, I = ()>(
200		PhantomData<(T, BlockNumberConverter, I)>,
201	);
202
203	impl<T: Config<I>, BlockNumberConverter, I: 'static> UncheckedOnRuntimeUpgrade
204		for MigrateToV2<T, BlockNumberConverter, I>
205	where
206		BlockNumberConverter: ConvertBlockNumber<LocalBlockNumberFor<T>, NewBlockNumberFor<T, I>>,
207	{
208		#[cfg(feature = "try-runtime")]
209		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
210			let params_exists = v1::Params::<T, I>::exists();
211			let member_count = v1::Member::<T, I>::iter().count() as u32;
212			Ok((params_exists, member_count).encode())
213		}
214
215		fn on_runtime_upgrade() -> frame_support::weights::Weight {
216			let mut translation_count = 0;
217
218			// Params conversion
219			let old_params = v1::Params::<T, I>::take();
220			let new_params = crate::ParamsOf::<T, I> {
221				active_salary: old_params.active_salary,
222				passive_salary: old_params.passive_salary,
223				demotion_period: BoundedVec::defensive_truncate_from(
224					old_params
225						.demotion_period
226						.into_iter()
227						.map(|original| BlockNumberConverter::equivalent_block_duration(original))
228						.collect(),
229				),
230				min_promotion_period: BoundedVec::defensive_truncate_from(
231					old_params
232						.min_promotion_period
233						.into_iter()
234						.map(|original| BlockNumberConverter::equivalent_block_duration(original))
235						.collect(),
236				),
237				offboard_timeout: BlockNumberConverter::equivalent_block_duration(
238					old_params.offboard_timeout,
239				),
240			};
241			crate::Params::<T, I>::put(new_params);
242			translation_count.saturating_inc();
243
244			// Member conversion
245			crate::Member::<T, I>::translate::<v1::MemberStatusOf<T>, _>(|_, member_data| {
246				translation_count.saturating_inc();
247				Some(crate::MemberStatus {
248					is_active: member_data.is_active,
249					last_promotion: BlockNumberConverter::equivalent_moment_in_time(
250						member_data.last_promotion,
251					),
252					last_proof: BlockNumberConverter::equivalent_moment_in_time(
253						member_data.last_proof,
254					),
255				})
256			});
257
258			T::DbWeight::get().reads_writes(translation_count, translation_count)
259		}
260
261		#[cfg(feature = "try-runtime")]
262		fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
263			let (params_existed, pre_member_count): (bool, u32) =
264				Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
265
266			ensure!(crate::Params::<T, I>::exists() == params_existed, "The Params storage's existence should remain the same before and after the upgrade.");
267			let post_member_count = crate::Member::<T, I>::iter().count() as u32;
268			ensure!(
269				post_member_count == pre_member_count,
270				"The member count should remain the same before and after the upgrade."
271			);
272			Ok(())
273		}
274	}
275}
276
277/// [`UncheckedOnRuntimeUpgrade`] implementation [`MigrateToV1`](v1::MigrateToV1) wrapped in a
278/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
279/// - The migration only runs once when the on-chain storage version is 0
280/// - The on-chain storage version is updated to `1` after the migration executes
281/// - Reads/Writes from checking/settings the on-chain storage version are accounted for
282pub type MigrateV0ToV1<T, I> = frame_support::migrations::VersionedMigration<
283	0, // The migration will only execute when the on-chain storage version is 0
284	1, // The on-chain storage version will be set to 1 after the migration is complete
285	v1::MigrateToV1<T, I>,
286	crate::pallet::Pallet<T, I>,
287	<T as frame_system::Config>::DbWeight,
288>;
289
290/// [`UncheckedOnRuntimeUpgrade`] implementation [`MigrateToV2`](v2::MigrateToV2) wrapped in a
291/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
292/// - The migration only runs once when the on-chain storage version is `1`.
293/// - The on-chain storage version is updated to `2` after the migration executes.
294/// - Reads/Writes from checking/settings the on-chain storage version are accounted for.
295pub type MigrateV1ToV2<T, BC, I> = frame_support::migrations::VersionedMigration<
296	1, // The migration will only execute when the on-chain storage version is 0
297	2, // The on-chain storage version will be set to 1 after the migration is complete
298	v2::MigrateToV2<T, BC, I>,
299	crate::pallet::Pallet<T, I>,
300	<T as frame_system::Config>::DbWeight,
301>;