From 97131a900b954b1f1dd0a826d3889348f1f02b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 27 Jul 2021 23:21:27 +0200 Subject: [PATCH] Move `PalletVersion` away from the crate version (#9165) * Move `PalletVersion` away from the crate version Before this pr, `PalletVersion` was referring to the crate version that hosted the pallet. This pr introduces a custom `package.metadata.frame` section in the `Cargo.toml` that can contain a `pallet-version` key value pair. While the value is expected to be a valid u16. If this key/value pair isn't given, the version is set to 1. It also changes the `PalletVersion` declaration. We now only have one `u16` that represents the version. Not a major/minor/patch version. As the old `PalletVersion` was starting with the `u16` major, decoding the old values will work. * Overhaul the entire implementation - Drop PalletVersion - Introduce StorageVersion - StorageVersion needs to be set in the crate and set for the macros - Added migration * Fix migrations * Review feedback * Remove unneeded dep * remove pub consts * Brings back logging and implements `GetStorageVersion` * Return weight from migration * Fmt and remove unused import * Update frame/support/src/dispatch.rs Co-authored-by: Guillaume Thiolliere * Update frame/support/src/traits/metadata.rs Co-authored-by: Guillaume Thiolliere Co-authored-by: Guillaume Thiolliere --- frame/contracts/src/lib.rs | 6 +- frame/contracts/src/migration.rs | 17 +- frame/elections-phragmen/src/lib.rs | 6 +- frame/elections-phragmen/src/migrations/v3.rs | 45 +-- frame/elections-phragmen/src/migrations/v4.rs | 52 ++-- frame/grandpa/src/lib.rs | 6 +- frame/grandpa/src/migrations.rs | 4 +- .../grandpa/src/migrations/{v3_1.rs => v4.rs} | 49 ++-- frame/offences/src/migration.rs | 2 +- frame/support/procedural/src/lib.rs | 8 - .../procedural/src/pallet/expand/hooks.rs | 22 +- .../src/pallet/expand/pallet_struct.rs | 28 +- .../src/pallet/parse/pallet_struct.rs | 30 +- .../support/procedural/src/pallet_version.rs | 64 ---- frame/support/src/dispatch.rs | 232 +++++++++++---- frame/support/src/lib.rs | 33 +-- frame/support/src/migrations.rs | 73 +++++ frame/support/src/traits.rs | 4 +- frame/support/src/traits/hooks.rs | 15 - frame/support/src/traits/metadata.rs | 140 +++++---- frame/support/test/src/lib.rs | 3 - frame/support/test/tests/pallet.rs | 66 ++++- frame/support/test/tests/pallet_instance.rs | 24 +- frame/support/test/tests/pallet_version.rs | 274 ------------------ 24 files changed, 548 insertions(+), 655 deletions(-) rename frame/grandpa/src/migrations/{v3_1.rs => v4.rs} (73%) delete mode 100644 frame/support/procedural/src/pallet_version.rs create mode 100644 frame/support/src/migrations.rs delete mode 100644 frame/support/test/tests/pallet_version.rs diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index a3a3311fa9bee..74ab6578f797d 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -111,7 +111,7 @@ use crate::{ }; use frame_support::{ dispatch::Dispatchable, - traits::{Currency, Filter, Get, OnUnbalanced, Randomness, Time}, + traits::{Currency, Filter, Get, OnUnbalanced, Randomness, StorageVersion, Time}, weights::{GetDispatchInfo, PostDispatchInfo, Weight, WithPostDispatchInfo}, }; use frame_system::Pallet as System; @@ -134,6 +134,9 @@ type NegativeImbalanceOf = <::Currency as Currency< ::AccountId, >>::NegativeImbalance; +/// The current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); + #[frame_support::pallet] pub mod pallet { use super::*; @@ -273,6 +276,7 @@ pub mod pallet { } #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); #[pallet::hooks] diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index a28cb87bb60bd..fbf5b59e9e8aa 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -18,22 +18,17 @@ use crate::{Config, Pallet, Weight}; use frame_support::{ storage::migration, - traits::{Get, GetPalletVersion, PalletInfoAccess, PalletVersion}, + traits::{Get, PalletInfoAccess, StorageVersion}, }; pub fn migrate() -> Weight { let mut weight: Weight = 0; - match >::storage_version() { - Some(version) if version == PalletVersion::new(3, 0, 0) => { - weight = weight.saturating_add(T::DbWeight::get().writes(1)); - migration::remove_storage_prefix( - >::name().as_bytes(), - b"CurrentSchedule", - b"", - ); - }, - _ => (), + if StorageVersion::get::>() == 3 { + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + migration::remove_storage_prefix(>::name().as_bytes(), b"CurrentSchedule", b""); + + StorageVersion::new(4).put::>(); } weight diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 4b6dbc3f365d9..144997c60c2ee 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -104,7 +104,7 @@ use frame_support::{ traits::{ ChangeMembers, Contains, ContainsLengthBound, Currency, CurrencyToVote, Get, InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency, - SortedMembers, WithdrawReasons, + SortedMembers, StorageVersion, WithdrawReasons, }, weights::Weight, }; @@ -122,6 +122,9 @@ pub use weights::WeightInfo; /// All migrations. pub mod migrations; +/// The current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); + /// The maximum votes allowed per voter. pub const MAXIMUM_VOTE: usize = 16; @@ -239,6 +242,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); #[pallet::hooks] diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs index b19146a9e28e5..fae191373fa1b 100644 --- a/frame/elections-phragmen/src/migrations/v3.rs +++ b/frame/elections-phragmen/src/migrations/v3.rs @@ -19,7 +19,7 @@ use codec::{Decode, Encode, FullCodec}; use frame_support::{ - traits::{GetPalletVersion, PalletVersion}, + traits::{PalletInfoAccess, StorageVersion}, weights::Weight, RuntimeDebug, Twox64Concat, }; @@ -41,8 +41,8 @@ struct Voter { /// Trait to implement to give information about types used for migration pub trait V2ToV3 { - /// elections-phragmen module, used to check storage version. - type Module: GetPalletVersion; + /// The elections-phragmen pallet. + type Pallet: 'static + PalletInfoAccess; /// System config account id type AccountId: 'static + FullCodec; @@ -67,7 +67,7 @@ frame_support::generate_storage_alias!( > ); -/// Apply all of the migrations from 2_0_0 to 3_0_0. +/// Apply all of the migrations from 2 to 3. /// /// ### Warning /// @@ -77,28 +77,29 @@ frame_support::generate_storage_alias!( /// Be aware that this migration is intended to be used only for the mentioned versions. Use /// with care and run at your own risk. pub fn apply(old_voter_bond: T::Balance, old_candidacy_bond: T::Balance) -> Weight { - let maybe_storage_version = ::storage_version(); + let storage_version = StorageVersion::get::(); log::info!( target: "runtime::elections-phragmen", "Running migration for elections-phragmen with storage version {:?}", - maybe_storage_version, + storage_version, ); - match maybe_storage_version { - Some(storage_version) if storage_version <= PalletVersion::new(2, 0, 0) => { - migrate_voters_to_recorded_deposit::(old_voter_bond); - migrate_candidates_to_recorded_deposit::(old_candidacy_bond); - migrate_runners_up_to_recorded_deposit::(old_candidacy_bond); - migrate_members_to_recorded_deposit::(old_candidacy_bond); - Weight::max_value() - }, - _ => { - log::warn!( - target: "runtime::elections-phragmen", - "Attempted to apply migration to V3 but failed because storage version is {:?}", - maybe_storage_version, - ); - 0 - }, + + if storage_version <= 2 { + migrate_voters_to_recorded_deposit::(old_voter_bond); + migrate_candidates_to_recorded_deposit::(old_candidacy_bond); + migrate_runners_up_to_recorded_deposit::(old_candidacy_bond); + migrate_members_to_recorded_deposit::(old_candidacy_bond); + + StorageVersion::new(3).put::(); + + Weight::max_value() + } else { + log::warn!( + target: "runtime::elections-phragmen", + "Attempted to apply migration to V3 but failed because storage version is {:?}", + storage_version, + ); + 0 } } diff --git a/frame/elections-phragmen/src/migrations/v4.rs b/frame/elections-phragmen/src/migrations/v4.rs index fde9a768f335e..9acc1297294d9 100644 --- a/frame/elections-phragmen/src/migrations/v4.rs +++ b/frame/elections-phragmen/src/migrations/v4.rs @@ -18,7 +18,7 @@ //! Migrations to version [`4.0.0`], as denoted by the changelog. use frame_support::{ - traits::{Get, GetPalletVersion, PalletVersion}, + traits::{Get, StorageVersion}, weights::Weight, }; @@ -32,9 +32,7 @@ pub const OLD_PREFIX: &[u8] = b"PhragmenElection"; /// `::PalletInfo::name::`. /// /// The old storage prefix, `PhragmenElection` is hardcoded in the migration code. -pub fn migrate>( - new_pallet_name: N, -) -> Weight { +pub fn migrate>(new_pallet_name: N) -> Weight { if new_pallet_name.as_ref().as_bytes() == OLD_PREFIX { log::info!( target: "runtime::elections-phragmen", @@ -42,30 +40,30 @@ pub fn migrate>( ); return 0 } - let maybe_storage_version =

::storage_version(); + let storage_version = StorageVersion::get::>(); log::info!( target: "runtime::elections-phragmen", "Running migration to v4 for elections-phragmen with storage version {:?}", - maybe_storage_version, + storage_version, ); - match maybe_storage_version { - Some(storage_version) if storage_version <= PalletVersion::new(3, 0, 0) => { - log::info!("new prefix: {}", new_pallet_name.as_ref()); - frame_support::storage::migration::move_pallet( - OLD_PREFIX, - new_pallet_name.as_ref().as_bytes(), - ); - ::BlockWeights::get().max_block - }, - _ => { - log::warn!( - target: "runtime::elections-phragmen", - "Attempted to apply migration to v4 but failed because storage version is {:?}", - maybe_storage_version, - ); - 0 - }, + if storage_version <= 3 { + log::info!("new prefix: {}", new_pallet_name.as_ref()); + frame_support::storage::migration::move_pallet( + OLD_PREFIX, + new_pallet_name.as_ref().as_bytes(), + ); + + StorageVersion::new(4).put::>(); + + ::BlockWeights::get().max_block + } else { + log::warn!( + target: "runtime::elections-phragmen", + "Attempted to apply migration to v4 but failed because storage version is {:?}", + storage_version, + ); + 0 } } @@ -73,7 +71,7 @@ pub fn migrate>( /// [`frame_support::traits::OnRuntimeUpgrade::pre_upgrade`] for further testing. /// /// Panics if anything goes wrong. -pub fn pre_migration>(new: N) { +pub fn pre_migration>(new: N) { let new = new.as_ref(); log::info!("pre-migration elections-phragmen test with new = {}", new); @@ -94,15 +92,15 @@ pub fn pre_migration>(new: N) { sp_core::hexdisplay::HexDisplay::from(&sp_io::storage::next_key(new.as_bytes()).unwrap()) ); // ensure storage version is 3. - assert!(

::storage_version().unwrap().major == 3); + assert_eq!(StorageVersion::get::>(), 3); } /// Some checks for after migration. This can be linked to /// [`frame_support::traits::OnRuntimeUpgrade::post_upgrade`] for further testing. /// /// Panics if anything goes wrong. -pub fn post_migration() { +pub fn post_migration() { log::info!("post-migration elections-phragmen"); // ensure we've been updated to v4 by the automatic write of crate version -> storage version. - assert!(

::storage_version().unwrap().major == 4); + assert_eq!(StorageVersion::get::>(), 4); } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 184ab49608747..fe9973fcf9bad 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -42,7 +42,7 @@ use fg_primitives::{ use frame_support::{ dispatch::DispatchResultWithPostInfo, storage, - traits::{KeyOwnerProofSystem, OneSessionHandler}, + traits::{KeyOwnerProofSystem, OneSessionHandler, StorageVersion}, weights::{Pays, Weight}, }; use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult, KeyTypeId}; @@ -67,6 +67,9 @@ pub use equivocation::{ pub use pallet::*; +/// The current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); + #[frame_support::pallet] pub mod pallet { use super::*; @@ -75,6 +78,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] diff --git a/frame/grandpa/src/migrations.rs b/frame/grandpa/src/migrations.rs index b0c8578c33e07..05c24e11b3939 100644 --- a/frame/grandpa/src/migrations.rs +++ b/frame/grandpa/src/migrations.rs @@ -15,5 +15,5 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Version 3.1. -pub mod v3_1; +/// Version 4. +pub mod v4; diff --git a/frame/grandpa/src/migrations/v3_1.rs b/frame/grandpa/src/migrations/v4.rs similarity index 73% rename from frame/grandpa/src/migrations/v3_1.rs rename to frame/grandpa/src/migrations/v4.rs index c2ab9d3b7f665..094f276efef31 100644 --- a/frame/grandpa/src/migrations/v3_1.rs +++ b/frame/grandpa/src/migrations/v4.rs @@ -16,7 +16,7 @@ // limitations under the License. use frame_support::{ - traits::{Get, GetPalletVersion, PalletVersion}, + traits::{Get, StorageVersion}, weights::Weight, }; use sp_io::hashing::twox_128; @@ -31,9 +31,7 @@ pub const OLD_PREFIX: &[u8] = b"GrandpaFinality"; /// `::PalletInfo::name::`. /// /// The old storage prefix, `GrandpaFinality` is hardcoded in the migration code. -pub fn migrate>( - new_pallet_name: N, -) -> Weight { +pub fn migrate>(new_pallet_name: N) -> Weight { if new_pallet_name.as_ref().as_bytes() == OLD_PREFIX { log::info!( target: "runtime::afg", @@ -41,30 +39,25 @@ pub fn migrate>( ); return 0 } - let maybe_storage_version =

::storage_version(); + let storage_version = StorageVersion::get::>(); log::info!( target: "runtime::afg", "Running migration to v3.1 for grandpa with storage version {:?}", - maybe_storage_version, + storage_version, ); - match maybe_storage_version { - Some(storage_version) if storage_version <= PalletVersion::new(3, 0, 0) => { - log::info!("new prefix: {}", new_pallet_name.as_ref()); - frame_support::storage::migration::move_pallet( - OLD_PREFIX, - new_pallet_name.as_ref().as_bytes(), - ); - ::BlockWeights::get().max_block - }, - _ => { - log::warn!( - target: "runtime::afg", - "Attempted to apply migration to v3.1 but cancelled because storage version is {:?}", - maybe_storage_version, - ); - 0 - }, + if storage_version <= 3 { + log::info!("new prefix: {}", new_pallet_name.as_ref()); + frame_support::storage::migration::move_pallet( + OLD_PREFIX, + new_pallet_name.as_ref().as_bytes(), + ); + + StorageVersion::new(4).put::>(); + + ::BlockWeights::get().max_block + } else { + 0 } } @@ -72,9 +65,7 @@ pub fn migrate>( /// [`frame_support::traits::OnRuntimeUpgrade::pre_upgrade`] for further testing. /// /// Panics if anything goes wrong. -pub fn pre_migration>( - new: N, -) { +pub fn pre_migration>(new: N) { let new = new.as_ref(); log::info!("pre-migration grandpa test with new = {}", new); @@ -83,7 +74,7 @@ pub fn pre_migration().unwrap(); + let storage_key = StorageVersion::storage_key::>(); // ensure nothing is stored in the new prefix. assert!( @@ -103,14 +94,14 @@ pub fn pre_migration::storage_version().unwrap().major == 3); + assert_eq!(StorageVersion::get::>(), 3); } /// Some checks for after migration. This can be linked to /// [`frame_support::traits::OnRuntimeUpgrade::post_upgrade`] for further testing. /// /// Panics if anything goes wrong. -pub fn post_migration() { +pub fn post_migration() { log::info!("post-migration grandpa"); // Assert that nothing remains at the old prefix diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index cb5c520392c97..ee95d111a22b2 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -81,7 +81,7 @@ mod test { // when assert_eq!( Offences::on_runtime_upgrade(), - ::DbWeight::get().reads_writes(1, 2), + ::DbWeight::get().reads_writes(1, 1), ); // then diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index a59ae67851e66..483d7c31c0629 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -26,7 +26,6 @@ mod default_no_bound; mod dummy_part_checker; mod key_prefix; mod pallet; -mod pallet_version; mod partial_eq_no_bound; mod storage; mod transactional; @@ -461,13 +460,6 @@ pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStre .unwrap_or_else(|e| e.to_compile_error().into()) } -#[proc_macro] -pub fn crate_to_pallet_version(input: TokenStream) -> TokenStream { - pallet_version::crate_to_pallet_version(input) - .unwrap_or_else(|e| e.to_compile_error()) - .into() -} - /// The number of module instances supported by the runtime, starting at index 1, /// and up to `NUMBER_OF_INSTANCE`. pub(crate) const NUMBER_OF_INSTANCE: u8 = 16; diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index c279a83d3daaf..314f982c5aad9 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -40,9 +40,11 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { quote::quote! { #frame_support::log::info!( target: #frame_support::LOG_TARGET, - "⚠️ {} declares internal migrations (which *might* execute), setting storage version to {:?}", + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs current storage version `{:?}`", pallet_name, - new_storage_version, + ::on_chain_storage_version(), + ::current_storage_version(), ); } } else { @@ -50,9 +52,8 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { quote::quote! { #frame_support::log::info!( target: #frame_support::LOG_TARGET, - "✅ no migration for {}, setting storage version to {:?}", + "✅ no migration for {}", pallet_name, - new_storage_version, ); } }; @@ -131,7 +132,6 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { ); // log info about the upgrade. - let new_storage_version = #frame_support::crate_to_pallet_version!(); let pallet_name = < ::PalletInfo as @@ -139,19 +139,11 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { >::name::().unwrap_or(""); #log_runtime_upgrade - let result = < + < Self as #frame_support::traits::Hooks< ::BlockNumber > - >::on_runtime_upgrade(); - - new_storage_version.put_into_storage::<::PalletInfo, Self>(); - - let additional_write = < - ::DbWeight as #frame_support::traits::Get<_> - >::get().writes(1); - - result.saturating_add(additional_write) + >::on_runtime_upgrade() } #[cfg(feature = "try-runtime")] diff --git a/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/frame/support/procedural/src/pallet/expand/pallet_struct.rs index 8be933fc3cf9d..ccc6fee5c2ba6 100644 --- a/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -18,7 +18,7 @@ use crate::pallet::{expand::merge_where_clauses, parse::helper::get_doc_literals, Def}; /// * Add derive trait on Pallet -/// * Implement GetPalletVersion on Pallet +/// * Implement GetStorageVersion on Pallet /// * Implement OnGenesis on Pallet /// * Implement ModuleErrorMetadata on Pallet /// * declare Module type alias for construct_runtime @@ -151,6 +151,12 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } ); + let storage_version = if let Some(v) = def.pallet_struct.storage_version.as_ref() { + quote::quote! { #v } + } else { + quote::quote! { #frame_support::traits::StorageVersion::default() } + }; + quote::quote_spanned!(def.pallet_struct.attr_span => #module_error_metadata @@ -161,21 +167,17 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { #[allow(dead_code)] pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; - // Implement `GetPalletVersion` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::GetPalletVersion + // Implement `GetStorageVersion` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::GetStorageVersion for #pallet_ident<#type_use_gen> #config_where_clause { - fn current_version() -> #frame_support::traits::PalletVersion { - #frame_support::crate_to_pallet_version!() + fn current_storage_version() -> #frame_support::traits::StorageVersion { + #storage_version } - fn storage_version() -> Option<#frame_support::traits::PalletVersion> { - let key = #frame_support::traits::PalletVersion::storage_key::< - ::PalletInfo, Self - >().expect("Every active pallet has a name in the runtime; qed"); - - #frame_support::storage::unhashed::get(&key) + fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { + #frame_support::traits::StorageVersion::get::() } } @@ -185,8 +187,8 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { #config_where_clause { fn on_genesis() { - #frame_support::crate_to_pallet_version!() - .put_into_storage::<::PalletInfo, Self>(); + let storage_version = #storage_version; + storage_version.put::(); } } diff --git a/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/frame/support/procedural/src/pallet/parse/pallet_struct.rs index 088b647fad7de..278f46e13818e 100644 --- a/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -25,6 +25,7 @@ mod keyword { syn::custom_keyword!(Pallet); syn::custom_keyword!(generate_store); syn::custom_keyword!(generate_storage_info); + syn::custom_keyword!(storage_version); syn::custom_keyword!(Store); } @@ -43,14 +44,18 @@ pub struct PalletStructDef { /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. /// Contains the span of the attribute. pub generate_storage_info: Option, + /// The current storage version of the pallet. + pub storage_version: Option, } /// Parse for one variant of: /// * `#[pallet::generate_store($vis trait Store)]` /// * `#[pallet::generate_storage_info]` +/// * `#[pallet::storage_version(STORAGE_VERSION)]` pub enum PalletStructAttr { GenerateStore { span: proc_macro2::Span, vis: syn::Visibility, keyword: keyword::Store }, GenerateStorageInfoTrait(proc_macro2::Span), + StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, } impl PalletStructAttr { @@ -58,6 +63,7 @@ impl PalletStructAttr { match self { Self::GenerateStore { span, .. } => *span, Self::GenerateStorageInfoTrait(span) => *span, + Self::StorageVersion { span, .. } => *span, } } } @@ -83,6 +89,14 @@ impl syn::parse::Parse for PalletStructAttr { } else if lookahead.peek(keyword::generate_storage_info) { let span = content.parse::()?.span(); Ok(Self::GenerateStorageInfoTrait(span)) + } else if lookahead.peek(keyword::storage_version) { + let span = content.parse::()?.span(); + + let version_content; + syn::parenthesized!(version_content in content); + let storage_version = version_content.parse::()?; + + Ok(Self::StorageVersion { storage_version, span }) } else { Err(lookahead.error()) } @@ -104,6 +118,7 @@ impl PalletStructDef { let mut store = None; let mut generate_storage_info = None; + let mut storage_version_found = None; let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; for attr in struct_attrs { @@ -116,6 +131,11 @@ impl PalletStructDef { { generate_storage_info = Some(span); } + PalletStructAttr::StorageVersion { storage_version, .. } + if storage_version_found.is_none() => + { + storage_version_found = Some(storage_version); + } attr => { let msg = "Unexpected duplicated attribute"; return Err(syn::Error::new(attr.span(), msg)) @@ -138,6 +158,14 @@ impl PalletStructDef { let mut instances = vec![]; instances.push(helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?); - Ok(Self { index, instances, pallet, store, attr_span, generate_storage_info }) + Ok(Self { + index, + instances, + pallet, + store, + attr_span, + generate_storage_info, + storage_version: storage_version_found, + }) } } diff --git a/frame/support/procedural/src/pallet_version.rs b/frame/support/procedural/src/pallet_version.rs deleted file mode 100644 index f0821f343c035..0000000000000 --- a/frame/support/procedural/src/pallet_version.rs +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2021 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 -// limitations under the License. - -//! Implementation of macros related to pallet versioning. - -use frame_support_procedural_tools::generate_crate_access_2018; -use proc_macro2::{Span, TokenStream}; -use std::{env, str::FromStr}; -use syn::{Error, Result}; - -/// Get the version from the given version environment variable. -/// -/// The version is parsed into the requested destination type. -fn get_version(version_env: &str) -> std::result::Result { - let version = env::var(version_env) - .unwrap_or_else(|_| panic!("`{}` is always set by cargo; qed", version_env)); - - T::from_str(&version).map_err(drop) -} - -/// Create an error that will be shown by rustc at the call site of the macro. -fn create_error(message: &str) -> Error { - Error::new(Span::call_site(), message) -} - -/// Implementation of the `crate_to_pallet_version!` macro. -pub fn crate_to_pallet_version(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(create_error("No arguments expected!")) - } - - let major_version = get_version::("CARGO_PKG_VERSION_MAJOR") - .map_err(|_| create_error("Major version needs to fit into `u16`"))?; - - let minor_version = get_version::("CARGO_PKG_VERSION_MINOR") - .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; - - let patch_version = get_version::("CARGO_PKG_VERSION_PATCH") - .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; - - let crate_ = generate_crate_access_2018("frame-support")?; - - Ok(quote::quote! { - #crate_::traits::PalletVersion { - major: #major_version, - minor: #minor_version, - patch: #patch_version, - } - }) -} diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index d962f6e00d705..da9d6adff6f3f 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -26,7 +26,7 @@ pub use crate::{ result, }, traits::{ - CallMetadata, GetCallMetadata, GetCallName, GetPalletVersion, UnfilteredDispatchable, + CallMetadata, GetCallMetadata, GetCallName, GetStorageVersion, UnfilteredDispatchable, }, weights::{ ClassifyDispatch, DispatchInfo, GetDispatchInfo, PaysFee, PostDispatchInfo, @@ -352,6 +352,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -388,6 +389,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -408,6 +410,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event() = default; @@ -426,7 +429,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -445,6 +449,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event @@ -471,6 +476,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event() = default; @@ -493,6 +499,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -513,7 +520,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -533,6 +541,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -561,6 +570,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -585,6 +595,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_idle($param_name1:ident : $param1:ty, $param_name2:ident: $param2:ty $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -605,7 +616,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -626,6 +638,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -652,6 +665,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -678,6 +692,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -706,6 +721,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -726,7 +742,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -748,6 +765,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -772,6 +790,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } {} + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn integrity_test() { $( $impl:tt )* } @@ -794,6 +813,7 @@ macro_rules! decl_module { $(#[doc = $doc_attr])* fn integrity_test() { $( $impl)* } } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -815,6 +835,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )+ } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn integrity_test() { $( $impl:tt )* } @@ -839,6 +860,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -865,6 +887,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -893,6 +916,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -913,7 +937,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -935,6 +960,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -959,6 +985,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn offchain_worker( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -979,7 +1006,8 @@ macro_rules! decl_module { { fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -1001,6 +1029,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn offchain_worker( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -1026,6 +1055,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $( #[doc = $doc_attr:tt] )* const $name:ident: $ty:ty = $value:expr; @@ -1051,7 +1081,8 @@ macro_rules! decl_module { $name: $ty = $value; } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -1075,6 +1106,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* type Error = $error_type:ty; @@ -1095,7 +1127,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $error_type } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* ] $($rest)* ); @@ -1118,6 +1151,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $($t:tt)* ] $($rest:tt)* ) => { @@ -1136,12 +1170,59 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { &'static str } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $($t)* ] $($rest)* ); }; + // Parse storage version + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: + $trait_name:ident$(, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_idle:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + { } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + type StorageVersion = $storage_version:path; + $($rest:tt)* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type< + $trait_instance: $trait_name$(, $instance: $instantiable $(= $module_default_instance)?)? + > + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_runtime_upgrade )* } + { $( $on_idle )* } + { $( $on_finalize )* } + { $( $offchain )* } + { $( $constants )* } + { $( $error_type )* } + { $( $integrity_test)* } + { $storage_version } + [ $( $dispatchables )* ] + $($rest)* + ); + }; + // This puts the function statement into the [], decreasing `$rest` and moving toward finishing the parse. (@normalize $(#[$attr:meta])* @@ -1160,6 +1241,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $error_type:ty } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -1184,7 +1266,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $error_type } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } [ $( $dispatchables )* $(#[doc = $doc_attr])* @@ -1216,6 +1299,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[$fn_attr:meta])* @@ -1245,6 +1329,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -1274,6 +1359,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -1303,6 +1389,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -1333,6 +1420,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $( $error_type:tt )* } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } [ $( $dispatchables:tt )* ] ) => { $crate::decl_module!(@imp @@ -1350,7 +1438,8 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } - { $( $integrity_test)* } + { $( $integrity_test )* } + { $( $storage_version )* } ); }; @@ -1451,25 +1540,17 @@ macro_rules! decl_module { as $system::Config >::PalletInfo as $crate::traits::PalletInfo>::name::().unwrap_or(""); - let new_storage_version = $crate::crate_to_pallet_version!(); $crate::log::info!( target: $crate::LOG_TARGET, - "⚠️ {} declares internal migrations (which *might* execute), setting storage version to {:?}", + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs current storage version `{:?}`", pallet_name, - new_storage_version, + ::on_chain_storage_version(), + ::current_storage_version(), ); - let result: $return = (|| { $( $impl )* })(); - - new_storage_version - .put_into_storage::<<$trait_instance as $system::Config>::PalletInfo, Self>(); - - let additional_write = < - <$trait_instance as $system::Config>::DbWeight as $crate::traits::Get<_> - >::get().writes(1); - - result.saturating_add(additional_write) + (|| { $( $impl )* })() } #[cfg(feature = "try-runtime")] @@ -1500,19 +1581,14 @@ macro_rules! decl_module { as $system::Config >::PalletInfo as $crate::traits::PalletInfo>::name::().unwrap_or(""); - let new_storage_version = $crate::crate_to_pallet_version!(); $crate::log::info!( target: $crate::LOG_TARGET, - "✅ no migration for {}, setting storage version to {:?}", + "✅ no migration for {}", pallet_name, - new_storage_version, ); - new_storage_version - .put_into_storage::<<$trait_instance as $system::Config>::PalletInfo, Self>(); - - <<$trait_instance as $system::Config>::DbWeight as $crate::traits::Get<_>>::get().writes(1) + 0 } #[cfg(feature = "try-runtime")] @@ -1823,6 +1899,45 @@ macro_rules! decl_module { } }; + // Implementation for `GetStorageVersion`. + (@impl_get_storage_version + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + $( $storage_version:tt )+ + ) => { + // Implement `GetStorageVersion` for `Pallet` + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::GetStorageVersion + for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn current_storage_version() -> $crate::traits::StorageVersion { + $( $storage_version )* + } + + fn on_chain_storage_version() -> $crate::traits::StorageVersion { + $crate::traits::StorageVersion::get::() + } + } + }; + + // Implementation for `GetStorageVersion` when no storage version is passed. + (@impl_get_storage_version + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + ) => { + // Implement `GetStorageVersion` for `Pallet` + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::GetStorageVersion + for $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn current_storage_version() -> $crate::traits::StorageVersion { + Default::default() + } + + fn on_chain_storage_version() -> $crate::traits::StorageVersion { + $crate::traits::StorageVersion::get::() + } + } + }; + // The main macro expansion that actually renders the module code. (@imp @@ -1852,6 +1967,7 @@ macro_rules! decl_module { { $( $constants:tt )* } { $error_type:ty } { $( $integrity_test:tt )* } + { $( $storage_version:tt )* } ) => { $crate::__check_reserved_fn_name! { $( $fn_name )* } @@ -1908,6 +2024,7 @@ macro_rules! decl_module { { $( $other_where_bounds )* } $( $offchain )* } + $crate::decl_module! { @impl_deposit_event $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; @@ -1964,6 +2081,13 @@ macro_rules! decl_module { )* } + $crate::decl_module! { + @impl_get_storage_version + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } + $( $storage_version )* + } + // Implement weight calculation function for Call impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::GetDispatchInfo for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* @@ -1997,6 +2121,27 @@ macro_rules! decl_module { } } + // Implement PalletInfoAccess for the module. + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::PalletInfoAccess + for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn index() -> usize { + < + <$trait_instance as $system::Config>::PalletInfo as $crate::traits::PalletInfo + >::index::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name() -> &'static str { + < + <$trait_instance as $system::Config>::PalletInfo as $crate::traits::PalletInfo + >::name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + } + // Implement GetCallName for the Call. impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::GetCallName for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* @@ -2023,32 +2168,13 @@ macro_rules! decl_module { } } - // Bring `GetPalletVersion` into scope to make it easily usable. - pub use $crate::traits::GetPalletVersion as _; - // Implement `GetPalletVersion` for `Module` - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::GetPalletVersion - for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* - { - fn current_version() -> $crate::traits::PalletVersion { - $crate::crate_to_pallet_version!() - } - - fn storage_version() -> Option<$crate::traits::PalletVersion> { - let key = $crate::traits::PalletVersion::storage_key::< - <$trait_instance as $system::Config>::PalletInfo, Self - >().expect("Every active pallet has a name in the runtime; qed"); - - $crate::storage::unhashed::get(&key) - } - } - // Implement `OnGenesis` for `Module` impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::OnGenesis for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { fn on_genesis() { - $crate::crate_to_pallet_version!() - .put_into_storage::<<$trait_instance as $system::Config>::PalletInfo, Self>(); + let storage_version = ::current_storage_version(); + storage_version.put::(); } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 52b3907f64ced..a9cbe94d4fb72 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -59,6 +59,7 @@ pub mod inherent; #[macro_use] pub mod error; pub mod instances; +pub mod migrations; pub mod traits; pub mod weights; @@ -667,21 +668,6 @@ pub use frame_support_procedural::DefaultNoBound; /// ``` pub use frame_support_procedural::require_transactional; -/// Convert the current crate version into a [`PalletVersion`](crate::traits::PalletVersion). -/// -/// It uses the `CARGO_PKG_VERSION_MAJOR`, `CARGO_PKG_VERSION_MINOR` and -/// `CARGO_PKG_VERSION_PATCH` environment variables to fetch the crate version. -/// This means that the [`PalletVersion`](crate::traits::PalletVersion) -/// object will correspond to the version of the crate the macro is called in! -/// -/// # Example -/// -/// ``` -/// # use frame_support::{traits::PalletVersion, crate_to_pallet_version}; -/// const Version: PalletVersion = crate_to_pallet_version!(); -/// ``` -pub use frame_support_procedural::crate_to_pallet_version; - /// Return Err of the expression: `return Err($expression);`. /// /// Used as `fail!(expression)`. @@ -1301,7 +1287,7 @@ pub mod pallet_prelude { }, }, traits::{ - ConstU32, EnsureOrigin, Get, GetDefault, GetPalletVersion, Hooks, IsType, + ConstU32, EnsureOrigin, Get, GetDefault, GetStorageVersion, Hooks, IsType, PalletInfoAccess, StorageInfoTrait, }, weights::{DispatchClass, Pays, Weight}, @@ -1422,6 +1408,19 @@ pub mod pallet_prelude { /// This require all storage to implement the trait [`traits::StorageInfoTrait`], thus all keys /// and value types must bound [`pallet_prelude::MaxEncodedLen`]. /// +/// As the macro implements [`traits::GetStorageVersion`], the current storage version needs to be +/// communicated to the macro. This can be done by using the `storage_version` attribute: +/// +/// ```ignore +/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); +/// +/// #[pallet::pallet] +/// #[pallet::storage_version(STORAGE_VERSION)] +/// pub struct Pallet(_); +/// ``` +/// +/// If not present, the current storage version is set to the default value. +/// /// ### Macro expansion: /// /// The macro add this attribute to the struct definition: @@ -1436,7 +1435,7 @@ pub mod pallet_prelude { /// and replace the type `_` by `PhantomData`. /// /// It implements on pallet: -/// * [`traits::GetPalletVersion`] +/// * [`traits::GetStorageVersion`] /// * [`traits::OnGenesis`]: contains some logic to write pallet version into storage. /// * `ModuleErrorMetadata`: using error declared or no metadata. /// diff --git a/frame/support/src/migrations.rs b/frame/support/src/migrations.rs new file mode 100644 index 0000000000000..cf1ba81982424 --- /dev/null +++ b/frame/support/src/migrations.rs @@ -0,0 +1,73 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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 +// limitations under the License. + +use crate::{ + traits::{GetStorageVersion, PalletInfoAccess}, + weights::{RuntimeDbWeight, Weight}, +}; + +/// Trait used by [`migrate_from_pallet_version_to_storage_version`] to do the actual migration. +pub trait PalletVersionToStorageVersionHelper { + fn migrate(db_weight: &RuntimeDbWeight) -> Weight; +} + +impl PalletVersionToStorageVersionHelper for T { + fn migrate(db_weight: &RuntimeDbWeight) -> Weight { + const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; + + fn pallet_version_key(name: &str) -> [u8; 32] { + let pallet_name = sp_io::hashing::twox_128(name.as_bytes()); + let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); + + let mut final_key = [0u8; 32]; + final_key[..16].copy_from_slice(&pallet_name); + final_key[16..].copy_from_slice(&postfix); + + final_key + } + + sp_io::storage::clear(&pallet_version_key(::name())); + + let version = ::current_storage_version(); + version.put::(); + + db_weight.writes(2) + } +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl PalletVersionToStorageVersionHelper for T { + fn migrate(db_weight: &RuntimeDbWeight) -> Weight { + let mut weight: Weight = 0; + + for_tuples!( #( weight = weight.saturating_add(T::migrate(db_weight)); )* ); + + weight + } +} + +/// Migrate from the `PalletVersion` struct to the new +/// [`StorageVersion`](crate::traits::StorageVersion) struct. +/// +/// This will remove all `PalletVersion's` from the state and insert the current storage version. +pub fn migrate_from_pallet_version_to_storage_version< + AllPallets: PalletVersionToStorageVersionHelper, +>( + db_weight: &RuntimeDbWeight, +) -> Weight { + AllPallets::migrate(db_weight) +} diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 4c674e1f9662a..024e7e6c698e2 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -63,8 +63,8 @@ pub use randomness::Randomness; mod metadata; pub use metadata::{ - CallMetadata, GetCallMetadata, GetCallName, GetPalletVersion, PalletInfo, PalletInfoAccess, - PalletVersion, PALLET_VERSION_STORAGE_KEY_POSTFIX, + CallMetadata, GetCallMetadata, GetCallName, GetStorageVersion, PalletInfo, PalletInfoAccess, + StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX, }; mod hooks; diff --git a/frame/support/src/traits/hooks.rs b/frame/support/src/traits/hooks.rs index 37b07c3113018..45212c565039c 100644 --- a/frame/support/src/traits/hooks.rs +++ b/frame/support/src/traits/hooks.rs @@ -323,7 +323,6 @@ pub trait OnTimestampSet { #[cfg(test)] mod tests { use super::*; - use crate::traits::metadata::PalletVersion; #[test] fn on_initialize_and_on_runtime_upgrade_weight_merge_works() { @@ -342,18 +341,4 @@ mod tests { assert_eq!(<(Test, Test)>::on_initialize(0), 20); assert_eq!(<(Test, Test)>::on_runtime_upgrade(), 40); } - - #[test] - fn check_pallet_version_ordering() { - let version = PalletVersion::new(1, 0, 0); - assert!(version > PalletVersion::new(0, 1, 2)); - assert!(version == PalletVersion::new(1, 0, 0)); - assert!(version < PalletVersion::new(1, 0, 1)); - assert!(version < PalletVersion::new(1, 1, 0)); - - let version = PalletVersion::new(2, 50, 50); - assert!(version < PalletVersion::new(2, 50, 51)); - assert!(version > PalletVersion::new(2, 49, 51)); - assert!(version < PalletVersion::new(3, 49, 51)); - } } diff --git a/frame/support/src/traits/metadata.rs b/frame/support/src/traits/metadata.rs index ba26305638448..8b1707855f7b0 100644 --- a/frame/support/src/traits/metadata.rs +++ b/frame/support/src/traits/metadata.rs @@ -68,52 +68,42 @@ pub trait GetCallMetadata { fn get_call_metadata(&self) -> CallMetadata; } -/// The storage key postfix that is used to store the [`PalletVersion`] per pallet. +/// The storage key postfix that is used to store the [`StorageVersion`] per pallet. /// /// The full storage key is built by using: -/// Twox128([`PalletInfo::name`]) ++ Twox128([`PALLET_VERSION_STORAGE_KEY_POSTFIX`]) -pub const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; +/// Twox128([`PalletInfo::name`]) ++ Twox128([`STORAGE_VERSION_STORAGE_KEY_POSTFIX`]) +pub const STORAGE_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__STORAGE_VERSION__:"; -/// The version of a pallet. +/// The storage version of a pallet. /// -/// Each pallet version is stored in the state under a fixed key. See -/// [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] for how this key is built. -#[derive(RuntimeDebug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy)] -pub struct PalletVersion { - /// The major version of the pallet. - pub major: u16, - /// The minor version of the pallet. - pub minor: u8, - /// The patch version of the pallet. - pub patch: u8, -} +/// Each storage version of a pallet is stored in the state under a fixed key. See +/// [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] for how this key is built. +#[derive(RuntimeDebug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy, PartialOrd, Default)] +pub struct StorageVersion(u16); -impl PalletVersion { +impl StorageVersion { /// Creates a new instance of `Self`. - pub fn new(major: u16, minor: u8, patch: u8) -> Self { - Self { major, minor, patch } + pub const fn new(version: u16) -> Self { + Self(version) } - /// Returns the storage key for a pallet version. - /// - /// See [`PALLET_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built. + /// Returns the storage key for a storage version. /// - /// Returns `None` if the given `PI` returned a `None` as name for the given - /// `Pallet`. - pub fn storage_key() -> Option<[u8; 32]> { - let pallet_name = PI::name::()?; + /// See [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built. + pub fn storage_key() -> [u8; 32] { + let pallet_name = P::name(); let pallet_name = sp_io::hashing::twox_128(pallet_name.as_bytes()); - let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); + let postfix = sp_io::hashing::twox_128(STORAGE_VERSION_STORAGE_KEY_POSTFIX); let mut final_key = [0u8; 32]; final_key[..16].copy_from_slice(&pallet_name); final_key[16..].copy_from_slice(&postfix); - Some(final_key) + final_key } - /// Put this pallet version into the storage. + /// Put this storage version for the given pallet into the storage. /// /// It will use the storage key that is associated with the given `Pallet`. /// @@ -125,47 +115,75 @@ impl PalletVersion { /// /// It will also panic if this function isn't executed in an externalities /// provided environment. - pub fn put_into_storage(&self) { - let key = Self::storage_key::() - .expect("Every active pallet has a name in the runtime; qed"); + pub fn put(&self) { + let key = Self::storage_key::

(); crate::storage::unhashed::put(&key, self); } + + /// Get the storage version of the given pallet from the storage. + /// + /// It will use the storage key that is associated with the given `Pallet`. + /// + /// # Panics + /// + /// This function will panic iff `Pallet` can not be found by `PalletInfo`. + /// In a runtime that is put together using + /// [`construct_runtime!`](crate::construct_runtime) this should never happen. + /// + /// It will also panic if this function isn't executed in an externalities + /// provided environment. + pub fn get() -> Self { + let key = Self::storage_key::

(); + + crate::storage::unhashed::get_or_default(&key) + } } -impl sp_std::cmp::PartialOrd for PalletVersion { - fn partial_cmp(&self, other: &Self) -> Option { - let res = self - .major - .cmp(&other.major) - .then_with(|| self.minor.cmp(&other.minor).then_with(|| self.patch.cmp(&other.patch))); +impl PartialEq for StorageVersion { + fn eq(&self, other: &u16) -> bool { + self.0 == *other + } +} - Some(res) +impl PartialOrd for StorageVersion { + fn partial_cmp(&self, other: &u16) -> Option { + Some(self.0.cmp(other)) } } -/// Provides version information about a pallet. +/// Provides information about the storage version of a pallet. /// -/// This trait provides two functions for returning the version of a -/// pallet. There is a state where both functions can return distinct versions. -/// See [`GetPalletVersion::storage_version`] for more information about this. -pub trait GetPalletVersion { - /// Returns the current version of the pallet. - fn current_version() -> PalletVersion; - - /// Returns the version of the pallet that is stored in storage. - /// - /// Most of the time this will return the exact same version as - /// [`GetPalletVersion::current_version`]. Only when being in - /// a state after a runtime upgrade happened and the pallet did - /// not yet updated its version in storage, this will return a - /// different(the previous, seen from the time of calling) version. - /// - /// See [`PalletVersion`] for more information. - /// - /// # Note - /// - /// If there was no previous version of the pallet stored in the state, - /// this function returns `None`. - fn storage_version() -> Option; +/// It differentiates between current and on-chain storage version. Both should be only out of sync +/// when a new runtime upgrade was applied and the runtime migrations did not yet executed. +/// Otherwise it means that the pallet works with an unsupported storage version and unforeseen +/// stuff can happen. +/// +/// The current storage version is the version of the pallet as supported at runtime. The active +/// storage version is the version of the pallet in the storage. +/// +/// It is required to update the on-chain storage version manually when a migration was applied. +pub trait GetStorageVersion { + /// Returns the current storage version as supported by the pallet. + fn current_storage_version() -> StorageVersion; + /// Returns the on-chain storage version of the pallet as stored in the storage. + fn on_chain_storage_version() -> StorageVersion; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_storage_version_ordering() { + let version = StorageVersion::new(1); + assert!(version == StorageVersion::new(1)); + assert!(version < StorageVersion::new(2)); + assert!(version < StorageVersion::new(3)); + + let version = StorageVersion::new(2); + assert!(version < StorageVersion::new(3)); + assert!(version > StorageVersion::new(1)); + assert!(version < StorageVersion::new(5)); + } } diff --git a/frame/support/test/src/lib.rs b/frame/support/test/src/lib.rs index d40031c149d90..ffda500f96ad6 100644 --- a/frame/support/test/src/lib.rs +++ b/frame/support/test/src/lib.rs @@ -22,9 +22,6 @@ #![warn(missing_docs)] #![deny(warnings)] -#[cfg(test)] -mod pallet_version; - /// The configuration trait pub trait Config: 'static { /// The runtime origin type. diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 7385eeb6ad74f..c21808dfa8f24 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -19,9 +19,10 @@ use frame_support::{ dispatch::{Parameter, UnfilteredDispatchable}, storage::unhashed, traits::{ - GetCallName, GetPalletVersion, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade, + GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade, + PalletInfoAccess, StorageVersion, }, - weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, + weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays, RuntimeDbWeight}, }; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, @@ -96,13 +97,15 @@ impl SomeAssociation2 for u64 { pub mod pallet { use super::{ SomeAssociation1, SomeAssociation2, SomeType1, SomeType2, SomeType3, SomeType4, SomeType5, - SomeType6, SomeType7, + SomeType6, SomeType7, StorageVersion, }; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; type BalanceOf = ::Balance; + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(10); + #[pallet::config] pub trait Config: frame_system::Config where @@ -146,6 +149,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] #[pallet::generate_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::hooks] @@ -896,12 +900,7 @@ fn pallet_hooks_expand() { assert_eq!(AllPallets::on_initialize(1), 10); AllPallets::on_finalize(1); - assert_eq!(pallet::Pallet::::storage_version(), None); assert_eq!(AllPallets::on_runtime_upgrade(), 30); - assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), - ); assert_eq!( frame_system::Pallet::::events()[0].event, @@ -921,15 +920,60 @@ fn pallet_hooks_expand() { #[test] fn pallet_on_genesis() { TestExternalities::default().execute_with(|| { - assert_eq!(pallet::Pallet::::storage_version(), None); + assert_eq!(pallet::Pallet::::on_chain_storage_version(), StorageVersion::new(0)); pallet::Pallet::::on_genesis(); assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), + pallet::Pallet::::current_storage_version(), + pallet::Pallet::::on_chain_storage_version(), ); }) } +#[test] +fn migrate_from_pallet_version_to_storage_version() { + const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; + + fn pallet_version_key(name: &str) -> [u8; 32] { + let pallet_name = sp_io::hashing::twox_128(name.as_bytes()); + let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); + + let mut final_key = [0u8; 32]; + final_key[..16].copy_from_slice(&pallet_name); + final_key[16..].copy_from_slice(&postfix); + + final_key + } + + TestExternalities::default().execute_with(|| { + // Insert some fake pallet versions + sp_io::storage::set(&pallet_version_key(Example::name()), &[1, 2, 3]); + sp_io::storage::set(&pallet_version_key(Example2::name()), &[1, 2, 3]); + sp_io::storage::set(&pallet_version_key(System::name()), &[1, 2, 3]); + + // Check that everyone currently is at version 0 + assert_eq!(Example::on_chain_storage_version(), StorageVersion::new(0)); + assert_eq!(Example2::on_chain_storage_version(), StorageVersion::new(0)); + assert_eq!(System::on_chain_storage_version(), StorageVersion::new(0)); + + let db_weight = RuntimeDbWeight { read: 0, write: 5 }; + let weight = frame_support::migrations::migrate_from_pallet_version_to_storage_version::< + AllPalletsWithSystem, + >(&db_weight); + + // 3 pallets, 2 writes and every write costs 5 weight. + assert_eq!(3 * 2 * 5, weight); + + // All pallet versions should be removed + assert!(sp_io::storage::get(&pallet_version_key(Example::name())).is_none()); + assert!(sp_io::storage::get(&pallet_version_key(Example2::name())).is_none()); + assert!(sp_io::storage::get(&pallet_version_key(System::name())).is_none()); + + assert_eq!(Example::on_chain_storage_version(), pallet::STORAGE_VERSION); + assert_eq!(Example2::on_chain_storage_version(), StorageVersion::new(0)); + assert_eq!(System::on_chain_storage_version(), StorageVersion::new(0)); + }); +} + #[test] fn metadata() { use codec::{Decode, Encode}; diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index e3146f698e691..2c6c2a7a66468 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -18,9 +18,7 @@ use frame_support::{ dispatch::UnfilteredDispatchable, storage::unhashed, - traits::{ - GetCallName, GetPalletVersion, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade, - }, + traits::{GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade}, weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, }; use sp_io::{ @@ -505,17 +503,7 @@ fn pallet_hooks_expand() { assert_eq!(AllPallets::on_initialize(1), 21); AllPallets::on_finalize(1); - assert_eq!(pallet::Pallet::::storage_version(), None); - assert_eq!(pallet::Pallet::::storage_version(), None); assert_eq!(AllPallets::on_runtime_upgrade(), 61); - assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), - ); - assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), - ); // The order is indeed reversed due to https://github.com/paritytech/substrate/issues/6280 assert_eq!( @@ -548,19 +536,9 @@ fn pallet_hooks_expand() { #[test] fn pallet_on_genesis() { TestExternalities::default().execute_with(|| { - assert_eq!(pallet::Pallet::::storage_version(), None); pallet::Pallet::::on_genesis(); - assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), - ); - assert_eq!(pallet::Pallet::::storage_version(), None); pallet::Pallet::::on_genesis(); - assert_eq!( - pallet::Pallet::::storage_version(), - Some(pallet::Pallet::::current_version()), - ); }) } diff --git a/frame/support/test/tests/pallet_version.rs b/frame/support/test/tests/pallet_version.rs deleted file mode 100644 index 5048f47f67522..0000000000000 --- a/frame/support/test/tests/pallet_version.rs +++ /dev/null @@ -1,274 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2021 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 -// limitations under the License. - -//! Tests related to the pallet version. - -#![recursion_limit = "128"] - -use codec::{Decode, Encode}; -use frame_support::{ - crate_to_pallet_version, - traits::{ - GetPalletVersion, OnRuntimeUpgrade, PalletVersion, PALLET_VERSION_STORAGE_KEY_POSTFIX, - }, - weights::Weight, -}; -use sp_core::{sr25519, H256}; -use sp_runtime::{ - generic, - traits::{BlakeTwo256, Verify}, - BuildStorage, -}; - -/// A version that we will check for in the tests -const SOME_TEST_VERSION: PalletVersion = PalletVersion { major: 3000, minor: 30, patch: 13 }; - -/// Checks that `on_runtime_upgrade` sets the latest pallet version when being called without -/// being provided by the user. -mod module1 { - pub trait Config: frame_system::Config {} - - frame_support::decl_module! { - pub struct Module for enum Call where - origin: ::Origin, - {} - } -} - -/// Checks that `on_runtime_upgrade` sets the latest pallet version when being called and also -/// being provided by the user. -mod module2 { - use super::*; - - pub trait Config: frame_system::Config {} - - frame_support::decl_module! { - pub struct Module, I: Instance=DefaultInstance> for enum Call where - origin: ::Origin, - { - fn on_runtime_upgrade() -> Weight { - assert_eq!(crate_to_pallet_version!(), Self::current_version()); - - let version_key = PalletVersion::storage_key::().unwrap(); - let version_value = sp_io::storage::get(&version_key); - - if version_value.is_some() { - assert_eq!(SOME_TEST_VERSION, Self::storage_version().unwrap()); - } else { - // As the storage version does not exist yet, it should be `None`. - assert!(Self::storage_version().is_none()); - } - - 0 - } - } - } - - frame_support::decl_storage! { - trait Store for Module, I: Instance=DefaultInstance> as Module2 {} - } -} - -#[frame_support::pallet] -mod pallet3 { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_runtime_upgrade() -> Weight { - return 3 - } - } - - #[pallet::call] - impl Pallet {} -} - -#[frame_support::pallet] -mod pallet4 { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_runtime_upgrade() -> Weight { - return 3 - } - } - - #[pallet::call] - impl, I: 'static> Pallet {} -} - -impl module1::Config for Runtime {} -impl module2::Config for Runtime {} -impl module2::Config for Runtime {} -impl module2::Config for Runtime {} - -impl pallet3::Config for Runtime {} -impl pallet4::Config for Runtime {} -impl pallet4::Config for Runtime {} -impl pallet4::Config for Runtime {} - -pub type Signature = sr25519::Signature; -pub type AccountId = ::Signer; -pub type BlockNumber = u64; -pub type Index = u64; - -frame_support::parameter_types!( - pub const BlockHashCount: u32 = 250; -); - -impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::AllowAll; - type Origin = Origin; - type Index = u64; - type BlockNumber = BlockNumber; - type Call = Call; - type Hash = H256; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = sp_runtime::traits::IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); -} - -frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: frame_system::{Pallet, Call, Event}, - Module1: module1::{Pallet, Call}, - Module2: module2::{Pallet, Call}, - Module2_1: module2::::{Pallet, Call}, - Module2_2: module2::::{Pallet, Call}, - Pallet3: pallet3::{Pallet, Call}, - Pallet4: pallet4::{Pallet, Call}, - Pallet4_1: pallet4::::{Pallet, Call}, - Pallet4_2: pallet4::::{Pallet, Call}, - } -); - -pub type Header = generic::Header; -pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - -/// Returns the storage key for `PalletVersion` for the given `pallet`. -fn get_pallet_version_storage_key_for_pallet(pallet: &str) -> [u8; 32] { - let pallet_name = sp_io::hashing::twox_128(pallet.as_bytes()); - let postfix = sp_io::hashing::twox_128(PALLET_VERSION_STORAGE_KEY_POSTFIX); - - let mut final_key = [0u8; 32]; - final_key[..16].copy_from_slice(&pallet_name); - final_key[16..].copy_from_slice(&postfix); - - final_key -} - -/// Checks the version of the given `pallet`. -/// -/// It is expected that the pallet version can be found in the storage and equals the -/// current crate version. -fn check_pallet_version(pallet: &str) { - let key = get_pallet_version_storage_key_for_pallet(pallet); - let value = sp_io::storage::get(&key).expect("Pallet version exists"); - let version = - PalletVersion::decode(&mut &value[..]).expect("Pallet version is encoded correctly"); - - assert_eq!(crate_to_pallet_version!(), version); -} - -#[test] -fn on_runtime_upgrade_sets_the_pallet_versions_in_storage() { - sp_io::TestExternalities::new_empty().execute_with(|| { - AllPallets::on_runtime_upgrade(); - - check_pallet_version("Module1"); - check_pallet_version("Module2"); - check_pallet_version("Module2_1"); - check_pallet_version("Module2_2"); - check_pallet_version("Pallet3"); - check_pallet_version("Pallet4"); - check_pallet_version("Pallet4_1"); - check_pallet_version("Pallet4_2"); - }); -} - -#[test] -fn on_runtime_upgrade_overwrites_old_version() { - sp_io::TestExternalities::new_empty().execute_with(|| { - let key = get_pallet_version_storage_key_for_pallet("Module2"); - sp_io::storage::set(&key, &SOME_TEST_VERSION.encode()); - - AllPallets::on_runtime_upgrade(); - - check_pallet_version("Module1"); - check_pallet_version("Module2"); - check_pallet_version("Module2_1"); - check_pallet_version("Module2_2"); - check_pallet_version("Pallet3"); - check_pallet_version("Pallet4"); - check_pallet_version("Pallet4_1"); - check_pallet_version("Pallet4_2"); - }); -} - -#[test] -fn genesis_init_puts_pallet_version_into_storage() { - let storage = GenesisConfig::default().build_storage().expect("Builds genesis storage"); - - sp_io::TestExternalities::new(storage).execute_with(|| { - check_pallet_version("Module1"); - check_pallet_version("Module2"); - check_pallet_version("Module2_1"); - check_pallet_version("Module2_2"); - check_pallet_version("Pallet3"); - check_pallet_version("Pallet4"); - check_pallet_version("Pallet4_1"); - check_pallet_version("Pallet4_2"); - - let system_version = System::storage_version().expect("System version should be set"); - assert_eq!(System::current_version(), system_version); - }); -}