referrerpolicy=no-referrer-when-downgrade

pallet_alliance/
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
18use crate::{Config, Pallet, Weight, LOG_TARGET};
19use frame_support::{pallet_prelude::*, storage::migration, traits::OnRuntimeUpgrade};
20use log;
21
22/// The in-code storage version.
23pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
24
25/// Wrapper for all migrations of this pallet.
26pub fn migrate<T: Config<I>, I: 'static>() -> Weight {
27	let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
28	let mut weight: Weight = Weight::zero();
29
30	if on_chain_version < 1 {
31		weight = weight.saturating_add(v0_to_v1::migrate::<T, I>());
32	}
33
34	if on_chain_version < 2 {
35		weight = weight.saturating_add(v1_to_v2::migrate::<T, I>());
36	}
37
38	STORAGE_VERSION.put::<Pallet<T, I>>();
39	weight = weight.saturating_add(T::DbWeight::get().writes(1));
40
41	weight
42}
43
44/// Implements `OnRuntimeUpgrade` trait.
45pub struct Migration<T, I = ()>(PhantomData<(T, I)>);
46
47impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for Migration<T, I> {
48	fn on_runtime_upgrade() -> Weight {
49		migrate::<T, I>()
50	}
51}
52
53/// v0_to_v1: `UpForKicking` is replaced by a retirement period.
54mod v0_to_v1 {
55	use super::*;
56
57	pub fn migrate<T: Config<I>, I: 'static>() -> Weight {
58		log::info!(target: LOG_TARGET, "Running migration v0_to_v1.");
59
60		let res = migration::clear_storage_prefix(
61			<Pallet<T, I>>::name().as_bytes(),
62			b"UpForKicking",
63			b"",
64			None,
65			None,
66		);
67
68		log::info!(
69			target: LOG_TARGET,
70			"Cleared '{}' entries from 'UpForKicking' storage prefix",
71			res.unique
72		);
73
74		if res.maybe_cursor.is_some() {
75			log::error!(
76				target: LOG_TARGET,
77				"Storage prefix 'UpForKicking' is not completely cleared."
78			);
79		}
80
81		T::DbWeight::get().writes(res.unique.into())
82	}
83}
84
85/// v1_to_v2: `Members` storage map collapses `Founder` and `Fellow` keys into one `Fellow`.
86/// Total number of `Founder`s and `Fellow`s must not be higher than `T::MaxMembersCount`.
87pub(crate) mod v1_to_v2 {
88	use super::*;
89	use crate::{MemberRole, Members};
90
91	/// V1 Role set.
92	#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)]
93	pub enum MemberRoleV1 {
94		Founder,
95		Fellow,
96		Ally,
97		Retiring,
98	}
99
100	pub fn migrate<T: Config<I>, I: 'static>() -> Weight {
101		log::info!(target: LOG_TARGET, "Running migration v1_to_v2: `Members` storage map collapses `Founder` and `Fellow` keys into one `Fellow`.");
102		// fetch into the scope all members.
103		let founders_vec = take_members::<T, I>(MemberRoleV1::Founder).into_inner();
104		let mut fellows_vec = take_members::<T, I>(MemberRoleV1::Fellow).into_inner();
105		let allies = take_members::<T, I>(MemberRoleV1::Ally);
106		let retiring = take_members::<T, I>(MemberRoleV1::Retiring);
107		if founders_vec
108			.len()
109			.saturating_add(fellows_vec.len())
110			.saturating_add(allies.len())
111			.saturating_add(retiring.len()) ==
112			0
113		{
114			return T::DbWeight::get().reads(4)
115		}
116		log::info!(
117			target: LOG_TARGET,
118			"Members storage v1 contains, '{}' founders, '{}' fellows, '{}' allies, '{}' retiring members.",
119			founders_vec.len(),
120			fellows_vec.len(),
121			allies.len(),
122			retiring.len(),
123		);
124		// merge founders with fellows and sort.
125		fellows_vec.extend(founders_vec);
126		fellows_vec.sort();
127		if fellows_vec.len() as u32 > T::MaxMembersCount::get() {
128			log::error!(
129				target: LOG_TARGET,
130				"Merged list of founders and fellows do not fit into `T::MaxMembersCount` bound. Truncating the merged set into max members count."
131			);
132			fellows_vec.truncate(T::MaxMembersCount::get() as usize);
133		}
134		let fellows: BoundedVec<T::AccountId, T::MaxMembersCount> =
135			fellows_vec.try_into().unwrap_or_default();
136		// insert members with new storage map key.
137		Members::<T, I>::insert(&MemberRole::Fellow, fellows.clone());
138		Members::<T, I>::insert(&MemberRole::Ally, allies.clone());
139		Members::<T, I>::insert(&MemberRole::Retiring, retiring.clone());
140		log::info!(
141			target: LOG_TARGET,
142			"Members storage updated with, '{}' fellows, '{}' allies, '{}' retiring members.",
143			fellows.len(),
144			allies.len(),
145			retiring.len(),
146		);
147		T::DbWeight::get().reads_writes(4, 4)
148	}
149
150	fn take_members<T: Config<I>, I: 'static>(
151		role: MemberRoleV1,
152	) -> BoundedVec<T::AccountId, T::MaxMembersCount> {
153		migration::take_storage_item::<
154			MemberRoleV1,
155			BoundedVec<T::AccountId, T::MaxMembersCount>,
156			Twox64Concat,
157		>(<Pallet<T, I>>::name().as_bytes(), b"Members", role)
158		.unwrap_or_default()
159	}
160}
161
162#[cfg(test)]
163mod test {
164	use super::*;
165	use crate::{mock::*, MemberRole, Members};
166
167	#[test]
168	fn migration_v1_to_v2_works() {
169		new_test_ext().execute_with(|| {
170			assert_ok!(Alliance::join_alliance(RuntimeOrigin::signed(4)));
171			assert_eq!(Members::<Test>::get(MemberRole::Ally), vec![4]);
172			assert_eq!(Members::<Test>::get(MemberRole::Fellow), vec![1, 2, 3]);
173			v1_to_v2::migrate::<Test, ()>();
174			assert_eq!(Members::<Test>::get(MemberRole::Fellow), vec![1, 2, 3, 4]);
175			assert_eq!(Members::<Test>::get(MemberRole::Ally), vec![]);
176			assert_eq!(Members::<Test>::get(MemberRole::Retiring), vec![]);
177		});
178	}
179}