referrerpolicy=no-referrer-when-downgrade

pallet_staking/
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
17//! Storage migrations for the Staking pallet. The changelog for this is maintained at
18//! [CHANGELOG.md](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/staking/CHANGELOG.md).
19
20use super::*;
21use frame_support::{
22	migrations::VersionedMigration,
23	pallet_prelude::ValueQuery,
24	storage_alias,
25	traits::{GetStorageVersion, OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade},
26};
27
28#[cfg(feature = "try-runtime")]
29use sp_runtime::TryRuntimeError;
30
31/// Used for release versioning up to v12.
32///
33/// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier.
34#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
35enum ObsoleteReleases {
36	V5_0_0,  // blockable validators.
37	V6_0_0,  // removal of all storage associated with offchain phragmen.
38	V7_0_0,  // keep track of number of nominators / validators in map
39	V8_0_0,  // populate `VoterList`.
40	V9_0_0,  // inject validators into `VoterList` as well.
41	V10_0_0, // remove `EarliestUnappliedSlash`.
42	V11_0_0, // Move pallet storage prefix, e.g. BagsList -> VoterBagsList
43	V12_0_0, // remove `HistoryDepth`.
44}
45
46impl Default for ObsoleteReleases {
47	fn default() -> Self {
48		ObsoleteReleases::V12_0_0
49	}
50}
51
52/// Alias to the old storage item used for release versioning. Obsolete since v13.
53#[storage_alias]
54type StorageVersion<T: Config> = StorageValue<Pallet<T>, ObsoleteReleases, ValueQuery>;
55
56/// Supports the migration of Validator Disabling from pallet-staking to pallet-session
57pub mod v17 {
58	use super::*;
59
60	#[frame_support::storage_alias]
61	pub type DisabledValidators<T: Config> =
62		StorageValue<Pallet<T>, BoundedVec<(u32, OffenceSeverity), ConstU32<333>>, ValueQuery>;
63
64	pub struct MigrateDisabledToSession<T>(core::marker::PhantomData<T>);
65	impl<T: Config> pallet_session::migrations::v1::MigrateDisabledValidators
66		for MigrateDisabledToSession<T>
67	{
68		#[cfg(feature = "try-runtime")]
69		fn peek_disabled() -> Vec<(u32, OffenceSeverity)> {
70			DisabledValidators::<T>::get().into()
71		}
72
73		fn take_disabled() -> Vec<(u32, OffenceSeverity)> {
74			DisabledValidators::<T>::take().into()
75		}
76	}
77}
78
79/// Migrating `DisabledValidators` from `Vec<u32>` to `Vec<(u32, OffenceSeverity)>` to track offense
80/// severity for re-enabling purposes.
81pub mod v16 {
82	use super::*;
83	use sp_staking::offence::OffenceSeverity;
84
85	#[frame_support::storage_alias]
86	pub(crate) type DisabledValidators<T: Config> =
87		StorageValue<Pallet<T>, Vec<(u32, OffenceSeverity)>, ValueQuery>;
88
89	pub struct VersionUncheckedMigrateV15ToV16<T>(core::marker::PhantomData<T>);
90	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV15ToV16<T> {
91		#[cfg(feature = "try-runtime")]
92		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
93			let old_disabled_validators = v15::DisabledValidators::<T>::get();
94			Ok(old_disabled_validators.encode())
95		}
96
97		fn on_runtime_upgrade() -> Weight {
98			// Migrating `DisabledValidators` from `Vec<u32>` to `Vec<(u32, OffenceSeverity)>`.
99			// Using max severity (PerBill 100%) for the migration which effectively makes it so
100			// offenders before the migration will not be re-enabled this era unless there are
101			// other 100% offenders.
102			let max_offence = OffenceSeverity(Perbill::from_percent(100));
103			// Inject severity
104			let migrated = v15::DisabledValidators::<T>::take()
105				.into_iter()
106				.map(|v| (v, max_offence))
107				.collect::<Vec<_>>();
108
109			v16::DisabledValidators::<T>::set(migrated);
110
111			log!(info, "v16 applied successfully.");
112			T::DbWeight::get().reads_writes(1, 1)
113		}
114
115		#[cfg(feature = "try-runtime")]
116		fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
117			// Decode state to get old_disabled_validators in a format of Vec<u32>
118			let old_disabled_validators =
119				Vec::<u32>::decode(&mut state.as_slice()).expect("Failed to decode state");
120			let new_disabled_validators = v17::DisabledValidators::<T>::get();
121
122			// Compare lengths
123			frame_support::ensure!(
124				old_disabled_validators.len() == new_disabled_validators.len(),
125				"DisabledValidators length mismatch"
126			);
127
128			// Compare contents
129			let new_disabled_validators =
130				new_disabled_validators.into_iter().map(|(v, _)| v).collect::<Vec<_>>();
131			frame_support::ensure!(
132				old_disabled_validators == new_disabled_validators,
133				"DisabledValidator ids mismatch"
134			);
135
136			// Verify severity
137			let max_severity = OffenceSeverity(Perbill::from_percent(100));
138			let new_disabled_validators = v17::DisabledValidators::<T>::get();
139			for (_, severity) in new_disabled_validators {
140				frame_support::ensure!(severity == max_severity, "Severity mismatch");
141			}
142
143			Ok(())
144		}
145	}
146
147	pub type MigrateV15ToV16<T> = VersionedMigration<
148		15,
149		16,
150		VersionUncheckedMigrateV15ToV16<T>,
151		Pallet<T>,
152		<T as frame_system::Config>::DbWeight,
153	>;
154}
155
156/// Migrating `OffendingValidators` from `Vec<(u32, bool)>` to `Vec<u32>`
157pub mod v15 {
158	use super::*;
159
160	// The disabling strategy used by staking pallet
161	type DefaultDisablingStrategy = pallet_session::disabling::UpToLimitDisablingStrategy;
162
163	#[storage_alias]
164	pub(crate) type DisabledValidators<T: Config> = StorageValue<Pallet<T>, Vec<u32>, ValueQuery>;
165
166	pub struct VersionUncheckedMigrateV14ToV15<T>(core::marker::PhantomData<T>);
167	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV14ToV15<T> {
168		fn on_runtime_upgrade() -> Weight {
169			let mut migrated = v14::OffendingValidators::<T>::take()
170				.into_iter()
171				.filter(|p| p.1) // take only disabled validators
172				.map(|p| p.0)
173				.collect::<Vec<_>>();
174
175			// Respect disabling limit
176			migrated.truncate(DefaultDisablingStrategy::disable_limit(
177				T::SessionInterface::validators().len(),
178			));
179
180			DisabledValidators::<T>::set(migrated);
181
182			log!(info, "v15 applied successfully.");
183			T::DbWeight::get().reads_writes(1, 1)
184		}
185
186		#[cfg(feature = "try-runtime")]
187		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
188			frame_support::ensure!(
189				v14::OffendingValidators::<T>::decode_len().is_none(),
190				"OffendingValidators is not empty after the migration"
191			);
192			Ok(())
193		}
194	}
195
196	pub type MigrateV14ToV15<T> = VersionedMigration<
197		14,
198		15,
199		VersionUncheckedMigrateV14ToV15<T>,
200		Pallet<T>,
201		<T as frame_system::Config>::DbWeight,
202	>;
203}
204
205/// Migration of era exposure storage items to paged exposures.
206/// Changelog: [v14.](https://github.com/paritytech/substrate/blob/ankan/paged-rewards-rebased2/frame/staking/CHANGELOG.md#14)
207pub mod v14 {
208	use super::*;
209
210	#[frame_support::storage_alias]
211	pub(crate) type OffendingValidators<T: Config> =
212		StorageValue<Pallet<T>, Vec<(u32, bool)>, ValueQuery>;
213
214	pub struct MigrateToV14<T>(core::marker::PhantomData<T>);
215	impl<T: Config> OnRuntimeUpgrade for MigrateToV14<T> {
216		fn on_runtime_upgrade() -> Weight {
217			let in_code = Pallet::<T>::in_code_storage_version();
218			let on_chain = Pallet::<T>::on_chain_storage_version();
219
220			if in_code == 14 && on_chain == 13 {
221				in_code.put::<Pallet<T>>();
222
223				log!(info, "staking v14 applied successfully.");
224				T::DbWeight::get().reads_writes(1, 1)
225			} else {
226				log!(warn, "staking v14 not applied.");
227				T::DbWeight::get().reads(1)
228			}
229		}
230
231		#[cfg(feature = "try-runtime")]
232		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
233			frame_support::ensure!(
234				Pallet::<T>::on_chain_storage_version() >= 14,
235				"v14 not applied"
236			);
237			Ok(())
238		}
239	}
240}
241
242pub mod v13 {
243	use super::*;
244
245	pub struct MigrateToV13<T>(core::marker::PhantomData<T>);
246	impl<T: Config> OnRuntimeUpgrade for MigrateToV13<T> {
247		#[cfg(feature = "try-runtime")]
248		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
249			frame_support::ensure!(
250				StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0,
251				"Required v12 before upgrading to v13"
252			);
253
254			Ok(Default::default())
255		}
256
257		fn on_runtime_upgrade() -> Weight {
258			let in_code = Pallet::<T>::in_code_storage_version();
259			let onchain = StorageVersion::<T>::get();
260
261			if in_code == 13 && onchain == ObsoleteReleases::V12_0_0 {
262				StorageVersion::<T>::kill();
263				in_code.put::<Pallet<T>>();
264
265				log!(info, "v13 applied successfully");
266				T::DbWeight::get().reads_writes(1, 2)
267			} else {
268				log!(warn, "Skipping v13, should be removed");
269				T::DbWeight::get().reads(1)
270			}
271		}
272
273		#[cfg(feature = "try-runtime")]
274		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
275			frame_support::ensure!(
276				Pallet::<T>::on_chain_storage_version() == 13,
277				"v13 not applied"
278			);
279
280			frame_support::ensure!(
281				!StorageVersion::<T>::exists(),
282				"Storage version not migrated correctly"
283			);
284
285			Ok(())
286		}
287	}
288}
289
290pub mod v12 {
291	use super::*;
292	use frame_support::{pallet_prelude::ValueQuery, storage_alias};
293
294	#[storage_alias]
295	type HistoryDepth<T: Config> = StorageValue<Pallet<T>, u32, ValueQuery>;
296
297	/// Clean up `T::HistoryDepth` from storage.
298	///
299	/// We will be depending on the configurable value of `T::HistoryDepth` post
300	/// this release.
301	pub struct MigrateToV12<T>(core::marker::PhantomData<T>);
302	impl<T: Config> OnRuntimeUpgrade for MigrateToV12<T> {
303		#[cfg(feature = "try-runtime")]
304		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
305			frame_support::ensure!(
306				StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0,
307				"Expected v11 before upgrading to v12"
308			);
309
310			if HistoryDepth::<T>::exists() {
311				frame_support::ensure!(
312					T::HistoryDepth::get() == HistoryDepth::<T>::get(),
313					"Provided value of HistoryDepth should be same as the existing storage value"
314				);
315			} else {
316				log::info!("No HistoryDepth in storage; nothing to remove");
317			}
318
319			Ok(Default::default())
320		}
321
322		fn on_runtime_upgrade() -> frame_support::weights::Weight {
323			if StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0 {
324				HistoryDepth::<T>::kill();
325				StorageVersion::<T>::put(ObsoleteReleases::V12_0_0);
326
327				log!(info, "v12 applied successfully");
328				T::DbWeight::get().reads_writes(1, 2)
329			} else {
330				log!(warn, "Skipping v12, should be removed");
331				T::DbWeight::get().reads(1)
332			}
333		}
334
335		#[cfg(feature = "try-runtime")]
336		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
337			frame_support::ensure!(
338				StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0,
339				"v12 not applied"
340			);
341			Ok(())
342		}
343	}
344}
345
346pub mod v11 {
347	use super::*;
348	use frame_support::{
349		storage::migration::move_pallet,
350		traits::{GetStorageVersion, PalletInfoAccess},
351	};
352	#[cfg(feature = "try-runtime")]
353	use sp_io::hashing::twox_128;
354
355	pub struct MigrateToV11<T, P, N>(core::marker::PhantomData<(T, P, N)>);
356	impl<T: Config, P: GetStorageVersion + PalletInfoAccess, N: Get<&'static str>> OnRuntimeUpgrade
357		for MigrateToV11<T, P, N>
358	{
359		#[cfg(feature = "try-runtime")]
360		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
361			frame_support::ensure!(
362				StorageVersion::<T>::get() == ObsoleteReleases::V10_0_0,
363				"must upgrade linearly"
364			);
365			let old_pallet_prefix = twox_128(N::get().as_bytes());
366
367			frame_support::ensure!(
368				sp_io::storage::next_key(&old_pallet_prefix).is_some(),
369				"no data for the old pallet name has been detected"
370			);
371
372			Ok(Default::default())
373		}
374
375		/// Migrate the entire storage of this pallet to a new prefix.
376		///
377		/// This new prefix must be the same as the one set in construct_runtime. For safety, use
378		/// `PalletInfo` to get it, as:
379		/// `<Runtime as frame_system::Config>::PalletInfo::name::<VoterBagsList>`.
380		///
381		/// The migration will look into the storage version in order to avoid triggering a
382		/// migration on an up to date storage.
383		fn on_runtime_upgrade() -> Weight {
384			let old_pallet_name = N::get();
385			let new_pallet_name = <P as PalletInfoAccess>::name();
386
387			if StorageVersion::<T>::get() == ObsoleteReleases::V10_0_0 {
388				// bump version anyway, even if we don't need to move the prefix
389				StorageVersion::<T>::put(ObsoleteReleases::V11_0_0);
390				if new_pallet_name == old_pallet_name {
391					log!(
392						warn,
393						"new bags-list name is equal to the old one, only bumping the version"
394					);
395					return T::DbWeight::get().reads(1).saturating_add(T::DbWeight::get().writes(1))
396				}
397
398				move_pallet(old_pallet_name.as_bytes(), new_pallet_name.as_bytes());
399				<T as frame_system::Config>::BlockWeights::get().max_block
400			} else {
401				log!(warn, "v11::migrate should be removed.");
402				T::DbWeight::get().reads(1)
403			}
404		}
405
406		#[cfg(feature = "try-runtime")]
407		fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
408			frame_support::ensure!(
409				StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0,
410				"wrong version after the upgrade"
411			);
412
413			let old_pallet_name = N::get();
414			let new_pallet_name = <P as PalletInfoAccess>::name();
415
416			// skip storage prefix checks for the same pallet names
417			if new_pallet_name == old_pallet_name {
418				return Ok(())
419			}
420
421			let old_pallet_prefix = twox_128(N::get().as_bytes());
422			frame_support::ensure!(
423				sp_io::storage::next_key(&old_pallet_prefix).is_none(),
424				"old pallet data hasn't been removed"
425			);
426
427			let new_pallet_name = <P as PalletInfoAccess>::name();
428			let new_pallet_prefix = twox_128(new_pallet_name.as_bytes());
429			frame_support::ensure!(
430				sp_io::storage::next_key(&new_pallet_prefix).is_some(),
431				"new pallet data hasn't been created"
432			);
433
434			Ok(())
435		}
436	}
437}