// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and //! Storage migrations for the Staking pallet. The changelog for this is maintained at //! [CHANGELOG.md](https://github.com/paritytech/substrate/blob/master/frame/staking/CHANGELOG.md). use super::*; use frame_election_provider_support::SortedListProvider; use frame_support::{ pallet_prelude::ValueQuery, storage_alias, traits::{GetStorageVersion, OnRuntimeUpgrade}, }; #[cfg(feature = "try-runtime")] use frame_support::ensure; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; /// Used for release versioning upto v12. /// /// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] enum ObsoleteReleases { V1_0_0Ancient, V2_0_0, V3_0_0, V4_0_0, V5_0_0, // blockable validators. V6_0_0, // removal of all storage associated with offchain phragmen. V7_0_0, // keep track of number of nominators / validators in map V8_0_0, // populate `VoterList`. V9_0_0, // inject validators into `VoterList` as well. V10_0_0, // remove `EarliestUnappliedSlash`. V11_0_0, // Move pallet storage prefix, e.g. BagsList -> VoterBagsList V12_0_0, // remove `HistoryDepth`. } impl Default for ObsoleteReleases { fn default() -> Self { ObsoleteReleases::V12_0_0 } } /// Alias to the old storage item used for release versioning. Obsolete since v13. #[storage_alias] type StorageVersion = StorageValue, ObsoleteReleases, ValueQuery>; /// Migration of era exposure storage items to paged exposures. /// Changelog: [v14.](https://github.com/paritytech/substrate/blob/ankan/paged-rewards-rebased2/frame/staking/CHANGELOG.md#14) pub mod v14 { use super::*; pub struct MigrateToV14(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV14 { fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let on_chain = Pallet::::on_chain_storage_version(); if current == 14 && on_chain == 13 { current.put::>(); log!(info, "v14 applied successfully."); T::DbWeight::get().reads_writes(1, 1) } else { log!(warn, "v14 not applied."); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( Pallet::::on_chain_storage_version() >= 14, "v14 not applied" ); Ok(()) } } } pub mod v13 { use super::*; pub struct MigrateToV13(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV13 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V12_0_0, "Required v12 before upgrading to v13" ); Ok(Default::default()) } fn on_runtime_upgrade() -> Weight { let current = Pallet::::current_storage_version(); let onchain = StorageVersion::::get(); if current == 13 && onchain == ObsoleteReleases::V12_0_0 { StorageVersion::::kill(); current.put::>(); log!(info, "v13 applied successfully"); T::DbWeight::get().reads_writes(1, 2) } else { log!(warn, "Skipping v13, should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( Pallet::::on_chain_storage_version() == 13, "v13 not applied" ); frame_support::ensure!( !StorageVersion::::exists(), "Storage version not migrated correctly" ); Ok(()) } } } pub mod v12 { use super::*; use frame_support::{pallet_prelude::ValueQuery, storage_alias}; #[storage_alias] type HistoryDepth = StorageValue, u32, ValueQuery>; /// Clean up `T::HistoryDepth` from storage. /// /// We will be depending on the configurable value of `T::HistoryDepth` post /// this release. pub struct MigrateToV12(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV12 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V11_0_0, "Expected v11 before upgrading to v12" ); if HistoryDepth::::exists() { frame_support::ensure!( T::HistoryDepth::get() == HistoryDepth::::get(), "Provided value of HistoryDepth should be same as the existing storage value" ); } else { log::info!("No HistoryDepth in storage; nothing to remove"); } Ok(Default::default()) } fn on_runtime_upgrade() -> frame_support::weights::Weight { if StorageVersion::::get() == ObsoleteReleases::V11_0_0 { HistoryDepth::::kill(); StorageVersion::::put(ObsoleteReleases::V12_0_0); log!(info, "v12 applied successfully"); T::DbWeight::get().reads_writes(1, 2) } else { log!(warn, "Skipping v12, should be removed"); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V12_0_0, "v12 not applied" ); Ok(()) } } } pub mod v11 { use super::*; use frame_support::{ storage::migration::move_pallet, traits::{GetStorageVersion, PalletInfoAccess}, }; #[cfg(feature = "try-runtime")] use sp_io::hashing::twox_128; pub struct MigrateToV11(sp_std::marker::PhantomData<(T, P, N)>); impl> OnRuntimeUpgrade for MigrateToV11 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V10_0_0, "must upgrade linearly" ); let old_pallet_prefix = twox_128(N::get().as_bytes()); frame_support::ensure!( sp_io::storage::next_key(&old_pallet_prefix).is_some(), "no data for the old pallet name has been detected" ); Ok(Default::default()) } /// Migrate the entire storage of this pallet to a new prefix. /// /// This new prefix must be the same as the one set in construct_runtime. For safety, use /// `PalletInfo` to get it, as: /// `::PalletInfo::name::`. /// /// The migration will look into the storage version in order to avoid triggering a /// migration on an up to date storage. fn on_runtime_upgrade() -> Weight { let old_pallet_name = N::get(); let new_pallet_name =

::name(); if StorageVersion::::get() == ObsoleteReleases::V10_0_0 { // bump version anyway, even if we don't need to move the prefix StorageVersion::::put(ObsoleteReleases::V11_0_0); if new_pallet_name == old_pallet_name { log!( warn, "new bags-list name is equal to the old one, only bumping the version" ); return T::DbWeight::get().reads(1).saturating_add(T::DbWeight::get().writes(1)) } move_pallet(old_pallet_name.as_bytes(), new_pallet_name.as_bytes()); ::BlockWeights::get().max_block } else { log!(warn, "v11::migrate should be removed."); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V11_0_0, "wrong version after the upgrade" ); let old_pallet_name = N::get(); let new_pallet_name =

::name(); // skip storage prefix checks for the same pallet names if new_pallet_name == old_pallet_name { return Ok(()) } let old_pallet_prefix = twox_128(N::get().as_bytes()); frame_support::ensure!( sp_io::storage::next_key(&old_pallet_prefix).is_none(), "old pallet data hasn't been removed" ); let new_pallet_name =

::name(); let new_pallet_prefix = twox_128(new_pallet_name.as_bytes()); frame_support::ensure!( sp_io::storage::next_key(&new_pallet_prefix).is_some(), "new pallet data hasn't been created" ); Ok(()) } } } pub mod v10 { use super::*; use frame_support::storage_alias; #[storage_alias] type EarliestUnappliedSlash = StorageValue, EraIndex>; /// Apply any pending slashes that where queued. /// /// That means we might slash someone a bit too early, but we will definitely /// won't forget to slash them. The cap of 512 is somewhat randomly taken to /// prevent us from iterating over an arbitrary large number of keys `on_runtime_upgrade`. pub struct MigrateToV10(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV10 { fn on_runtime_upgrade() -> frame_support::weights::Weight { if StorageVersion::::get() == ObsoleteReleases::V9_0_0 { let pending_slashes = UnappliedSlashes::::iter().take(512); for (era, slashes) in pending_slashes { for slash in slashes { // in the old slashing scheme, the slash era was the key at which we read // from `UnappliedSlashes`. log!(warn, "prematurely applying a slash ({:?}) for era {:?}", slash, era); slashing::apply_slash::(slash, era); } } EarliestUnappliedSlash::::kill(); StorageVersion::::put(ObsoleteReleases::V10_0_0); log!(info, "MigrateToV10 executed successfully"); T::DbWeight::get().reads_writes(1, 1) } else { log!(warn, "MigrateToV10 should be removed."); T::DbWeight::get().reads(1) } } } } pub mod v9 { use super::*; #[cfg(feature = "try-runtime")] use codec::{Decode, Encode}; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; /// Migration implementation that injects all validators into sorted list. /// /// This is only useful for chains that started their `VoterList` just based on nominators. pub struct InjectValidatorsIntoVoterList(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for InjectValidatorsIntoVoterList { fn on_runtime_upgrade() -> Weight { if StorageVersion::::get() == ObsoleteReleases::V8_0_0 { let prev_count = T::VoterList::count(); let weight_of_cached = Pallet::::weight_of_fn(); for (v, _) in Validators::::iter() { let weight = weight_of_cached(&v); let _ = T::VoterList::on_insert(v.clone(), weight).map_err(|err| { log!(warn, "failed to insert {:?} into VoterList: {:?}", v, err) }); } log!( info, "injected a total of {} new voters, prev count: {} next count: {}, updating to version 9", Validators::::count(), prev_count, T::VoterList::count(), ); StorageVersion::::put(ObsoleteReleases::V9_0_0); T::BlockWeights::get().max_block } else { log!( warn, "InjectValidatorsIntoVoterList being executed on the wrong storage \ version, expected ObsoleteReleases::V8_0_0" ); T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V8_0_0, "must upgrade linearly" ); let prev_count = T::VoterList::count(); Ok(prev_count.encode()) } #[cfg(feature = "try-runtime")] fn post_upgrade(prev_count: Vec) -> Result<(), TryRuntimeError> { let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( "the state parameter should be something that was generated by pre_upgrade", ); let post_count = T::VoterList::count(); let validators = Validators::::count(); ensure!( post_count == prev_count + validators, "`VoterList` count after the migration must equal to the sum of \ previous count and the current number of validators" ); frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V9_0_0, "must upgrade" ); Ok(()) } } } pub mod v8 { use super::*; use crate::{Config, Nominators, Pallet, Weight}; use frame_election_provider_support::SortedListProvider; use frame_support::traits::Get; #[cfg(feature = "try-runtime")] pub fn pre_migrate() -> Result<(), &'static str> { frame_support::ensure!( StorageVersion::::get() == ObsoleteReleases::V7_0_0, "must upgrade linearly" ); crate::log!(info, "👜 staking bags-list migration passes PRE migrate checks ✅",); Ok(()) } /// Migration to sorted `VoterList`. pub fn migrate() -> Weight { if StorageVersion::::get() == ObsoleteReleases::V7_0_0 { crate::log!(info, "migrating staking to ObsoleteReleases::V8_0_0"); let migrated = T::VoterList::unsafe_regenerate( Nominators::::iter().map(|(id, _)| id), Pallet::::weight_of_fn(), ); StorageVersion::::put(ObsoleteReleases::V8_0_0); crate::log!( info, "👜 completed staking migration to ObsoleteReleases::V8_0_0 with {} voters migrated", migrated, ); T::BlockWeights::get().max_block } else { T::DbWeight::get().reads(1) } } #[cfg(feature = "try-runtime")] pub fn post_migrate() -> Result<(), &'static str> { T::VoterList::try_state().map_err(|_| "VoterList is not in a sane state.")?; crate::log!(info, "👜 staking bags-list migration passes POST migrate checks ✅",); Ok(()) } } pub mod v7 { use super::*; use frame_support::storage_alias; #[storage_alias] type CounterForValidators = StorageValue, u32>; #[storage_alias] type CounterForNominators = StorageValue, u32>; pub fn pre_migrate() -> Result<(), &'static str> { assert!( CounterForValidators::::get().unwrap().is_zero(), "CounterForValidators already set." ); assert!( CounterForNominators::::get().unwrap().is_zero(), "CounterForNominators already set." ); assert!(Validators::::count().is_zero(), "Validators already set."); assert!(Nominators::::count().is_zero(), "Nominators already set."); assert!(StorageVersion::::get() == ObsoleteReleases::V6_0_0); Ok(()) } pub fn migrate() -> Weight { log!(info, "Migrating staking to ObsoleteReleases::V7_0_0"); let validator_count = Validators::::iter().count() as u32; let nominator_count = Nominators::::iter().count() as u32; CounterForValidators::::put(validator_count); CounterForNominators::::put(nominator_count); StorageVersion::::put(ObsoleteReleases::V7_0_0); log!(info, "Completed staking migration to ObsoleteReleases::V7_0_0"); T::DbWeight::get().reads_writes(validator_count.saturating_add(nominator_count).into(), 2) } } pub mod v6 { use super::*; use frame_support::{storage_alias, traits::Get, weights::Weight}; // NOTE: value type doesn't matter, we just set it to () here. #[storage_alias] type SnapshotValidators = StorageValue, ()>; #[storage_alias] type SnapshotNominators = StorageValue, ()>; #[storage_alias] type QueuedElected = StorageValue, ()>; #[storage_alias] type QueuedScore = StorageValue, ()>; #[storage_alias] type EraElectionStatus = StorageValue, ()>; #[storage_alias] type IsCurrentSessionFinal = StorageValue, ()>; /// check to execute prior to migration. pub fn pre_migrate() -> Result<(), &'static str> { // these may or may not exist. log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::::exists()); log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::::exists()); log!(info, "QueuedElected.exits()? {:?}", QueuedElected::::exists()); log!(info, "QueuedScore.exits()? {:?}", QueuedScore::::exists()); // these must exist. assert!( IsCurrentSessionFinal::::exists(), "IsCurrentSessionFinal storage item not found!" ); assert!(EraElectionStatus::::exists(), "EraElectionStatus storage item not found!"); Ok(()) } /// Migrate storage to v6. pub fn migrate() -> Weight { log!(info, "Migrating staking to ObsoleteReleases::V6_0_0"); SnapshotValidators::::kill(); SnapshotNominators::::kill(); QueuedElected::::kill(); QueuedScore::::kill(); EraElectionStatus::::kill(); IsCurrentSessionFinal::::kill(); StorageVersion::::put(ObsoleteReleases::V6_0_0); log!(info, "Done."); T::DbWeight::get().writes(6 + 1) } }