From 24333b18c65d2eb268d0d44cddbb0efefc748ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 18 May 2022 00:45:56 +0200 Subject: [PATCH] generate_storage_alias: Rewrite as proc macro attribute (#11387) * generate_storage_alias: Rewrite as proc macro attribute This rewrites the `generate_storage_alias!` declarative macro as proc-macro attribute. While doing this the name is changed to `storage_alias`. The prefix can now also be the name of a pallet. This makes storage aliases work in migrations for all kind of chains and not just for the ones that use predefined prefixes. * Fix compilation and FMT * Moare fixes * :facepalm: * ...... * Rework the syntax and support instancing * FMT * Prefix variants with `Storage` * Make it compile * Fix where clause on rust stable --- frame/bags-list/src/list/tests.rs | 10 +- frame/bags-list/src/migrations.rs | 5 +- frame/contracts/src/migration.rs | 64 +- frame/elections-phragmen/src/migrations/v3.rs | 98 +-- frame/offences/src/migration.rs | 11 +- frame/staking/src/migrations.rs | 63 +- frame/support/procedural/src/lib.rs | 8 + frame/support/procedural/src/storage_alias.rs | 566 ++++++++++++++++++ frame/support/src/lib.rs | 232 +++---- .../support/src/storage/bounded_btree_map.rs | 15 +- .../support/src/storage/bounded_btree_set.rs | 15 +- frame/support/src/storage/bounded_vec.rs | 15 +- .../src/storage/generator/double_map.rs | 6 +- frame/support/src/storage/generator/map.rs | 3 +- frame/support/src/storage/generator/nmap.rs | 22 +- frame/support/src/storage/mod.rs | 19 +- frame/support/src/storage/types/nmap.rs | 8 +- frame/support/src/storage/weak_bounded_vec.rs | 13 +- frame/support/test/tests/pallet.rs | 15 + frame/support/test/tests/pallet_instance.rs | 13 + frame/system/src/migrations/mod.rs | 66 +- 21 files changed, 886 insertions(+), 381 deletions(-) create mode 100644 frame/support/procedural/src/storage_alias.rs diff --git a/frame/bags-list/src/list/tests.rs b/frame/bags-list/src/list/tests.rs index ff7dd2871c237..40a174b35a5d3 100644 --- a/frame/bags-list/src/list/tests.rs +++ b/frame/bags-list/src/list/tests.rs @@ -368,12 +368,10 @@ mod list { assert_eq!(crate::ListNodes::::count(), 4); // we do some wacky stuff here to get access to the counter, since it is (reasonably) // not exposed as mutable in any sense. - frame_support::generate_storage_alias!( - BagsList, - CounterForListNodes - => Value - ); - CounterForListNodes::mutate(|counter| *counter += 1); + #[frame_support::storage_alias] + type CounterForListNodes = + StorageValue, u32, frame_support::pallet_prelude::ValueQuery>; + CounterForListNodes::::mutate(|counter| *counter += 1); assert_eq!(crate::ListNodes::::count(), 5); assert_eq!(List::::sanity_check(), Err("iter_count != stored_count")); diff --git a/frame/bags-list/src/migrations.rs b/frame/bags-list/src/migrations.rs index 696733e8c7ba5..f8041327f10be 100644 --- a/frame/bags-list/src/migrations.rs +++ b/frame/bags-list/src/migrations.rs @@ -30,11 +30,12 @@ impl OnRuntimeUpgrade for CheckCounterPrefix { fn pre_upgrade() -> Result<(), &'static str> { use frame_support::ensure; // The old explicit storage item. - frame_support::generate_storage_alias!(BagsList, CounterForListNodes => Value); + #[frame_support::storage_alias] + type CounterForListNodes = StorageValue, u32>; // ensure that a value exists in the counter struct. ensure!( - crate::ListNodes::::count() == CounterForListNodes::get().unwrap(), + crate::ListNodes::::count() == CounterForListNodes::::get().unwrap(), "wrong list node counter" ); diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index fb99078451ef9..0832ebadac9c6 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -18,11 +18,8 @@ use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight}; use codec::{Decode, Encode}; use frame_support::{ - codec, generate_storage_alias, - pallet_prelude::*, - storage::migration, - traits::{Get, PalletInfoAccess}, - Identity, Twox64Concat, + codec, pallet_prelude::*, storage::migration, storage_alias, traits::Get, Identity, + Twox64Concat, }; use sp_std::{marker::PhantomData, prelude::*}; @@ -117,15 +114,16 @@ mod v5 { trie_id: TrieId, } - generate_storage_alias!( - Contracts, - ContractInfoOf => Map<(Twox64Concat, T::AccountId), ContractInfo> - ); + #[storage_alias] + type ContractInfoOf = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + ContractInfo, + >; - generate_storage_alias!( - Contracts, - DeletionQueue => Value> - ); + #[storage_alias] + type DeletionQueue = StorageValue, Vec>; pub fn migrate() -> Weight { let mut weight: Weight = 0; @@ -142,7 +140,7 @@ mod v5 { } }); - DeletionQueue::translate(|old: Option>| { + DeletionQueue::::translate(|old: Option>| { weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); old.map(|old| old.into_iter().map(|o| DeletedContract { trie_id: o.trie_id }).collect()) }) @@ -202,20 +200,19 @@ mod v6 { type ContractInfo = RawContractInfo, BalanceOf>; - generate_storage_alias!( - Contracts, - ContractInfoOf => Map<(Twox64Concat, T::AccountId), ContractInfo> - ); + #[storage_alias] + type ContractInfoOf = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + ContractInfo, + >; - generate_storage_alias!( - Contracts, - CodeStorage => Map<(Identity, CodeHash), PrefabWasmModule> - ); + #[storage_alias] + type CodeStorage = StorageMap, Identity, CodeHash, PrefabWasmModule>; - generate_storage_alias!( - Contracts, - OwnerInfoOf => Map<(Identity, CodeHash), OwnerInfo> - ); + #[storage_alias] + type OwnerInfoOf = StorageMap, Identity, CodeHash, OwnerInfo>; pub fn migrate() -> Weight { let mut weight: Weight = 0; @@ -259,15 +256,12 @@ mod v7 { use super::*; pub fn migrate() -> Weight { - generate_storage_alias!( - Contracts, - AccountCounter => Value - ); - generate_storage_alias!( - Contracts, - Nonce => Value - ); - Nonce::set(AccountCounter::take()); + #[storage_alias] + type AccountCounter = StorageValue, u64, ValueQuery>; + #[storage_alias] + type Nonce = StorageValue, u64, ValueQuery>; + + Nonce::::set(AccountCounter::::take()); T::DbWeight::get().reads_writes(1, 2) } } diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs index c6a7ce7e7ca1b..b1cdd4be98541 100644 --- a/frame/elections-phragmen/src/migrations/v3.rs +++ b/frame/elections-phragmen/src/migrations/v3.rs @@ -17,12 +17,10 @@ //! Migrations to version [`3.0.0`], as denoted by the changelog. +use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ - pallet_prelude::ValueQuery, - traits::{PalletInfoAccess, StorageVersion}, - weights::Weight, - RuntimeDebug, Twox64Concat, + pallet_prelude::ValueQuery, traits::StorageVersion, weights::Weight, RuntimeDebug, Twox64Concat, }; use sp_std::prelude::*; @@ -42,9 +40,6 @@ struct Voter { /// Trait to implement to give information about types used for migration pub trait V2ToV3 { - /// The elections-phragmen pallet. - type Pallet: 'static + PalletInfoAccess; - /// System config account id type AccountId: 'static + FullCodec; @@ -52,30 +47,31 @@ pub trait V2ToV3 { type Balance: 'static + FullCodec + Copy; } -frame_support::generate_storage_alias!( - PhragmenElection, Candidates => Value< - Vec<(T::AccountId, T::Balance)>, - ValueQuery - > -); -frame_support::generate_storage_alias!( - PhragmenElection, Members => Value< - Vec>, - ValueQuery - > -); -frame_support::generate_storage_alias!( - PhragmenElection, RunnersUp => Value< - Vec>, - ValueQuery - > -); -frame_support::generate_storage_alias!( - PhragmenElection, Voting => Map< - (Twox64Concat, T::AccountId), - Voter - > -); +#[frame_support::storage_alias] +type Candidates = + StorageValue, Vec<(::AccountId, ::Balance)>, ValueQuery>; + +#[frame_support::storage_alias] +type Members = StorageValue< + Pallet, + Vec::AccountId, ::Balance>>, + ValueQuery, +>; + +#[frame_support::storage_alias] +type RunnersUp = StorageValue< + Pallet, + Vec::AccountId, ::Balance>>, + ValueQuery, +>; + +#[frame_support::storage_alias] +type Voting = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + Voter<::AccountId, ::Balance>, +>; /// Apply all of the migrations from 2 to 3. /// @@ -86,8 +82,11 @@ 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 storage_version = StorageVersion::get::(); +pub fn apply( + old_voter_bond: V::Balance, + old_candidacy_bond: V::Balance, +) -> Weight { + let storage_version = StorageVersion::get::>(); log::info!( target: "runtime::elections-phragmen", "Running migration for elections-phragmen with storage version {:?}", @@ -95,12 +94,12 @@ pub fn apply(old_voter_bond: T::Balance, old_candidacy_bond: T::Balan ); 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); + 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::(); + StorageVersion::new(3).put::>(); Weight::max_value() } else { @@ -114,21 +113,21 @@ pub fn apply(old_voter_bond: T::Balance, old_candidacy_bond: T::Balan } /// Migrate from the old legacy voting bond (fixed) to the new one (per-vote dynamic). -pub fn migrate_voters_to_recorded_deposit(old_deposit: T::Balance) { - >::translate::<(T::Balance, Vec), _>(|_who, (stake, votes)| { +pub fn migrate_voters_to_recorded_deposit(old_deposit: V::Balance) { + >::translate::<(V::Balance, Vec), _>(|_who, (stake, votes)| { Some(Voter { votes, stake, deposit: old_deposit }) }); log::info!( target: "runtime::elections-phragmen", "migrated {} voter accounts.", - >::iter().count(), + >::iter().count(), ); } /// Migrate all candidates to recorded deposit. -pub fn migrate_candidates_to_recorded_deposit(old_deposit: T::Balance) { - let _ = >::translate::, _>(|maybe_old_candidates| { +pub fn migrate_candidates_to_recorded_deposit(old_deposit: V::Balance) { + let _ = >::translate::, _>(|maybe_old_candidates| { maybe_old_candidates.map(|old_candidates| { log::info!( target: "runtime::elections-phragmen", @@ -141,8 +140,8 @@ pub fn migrate_candidates_to_recorded_deposit(old_deposit: T::Balance } /// Migrate all members to recorded deposit. -pub fn migrate_members_to_recorded_deposit(old_deposit: T::Balance) { - let _ = >::translate::, _>(|maybe_old_members| { +pub fn migrate_members_to_recorded_deposit(old_deposit: V::Balance) { + let _ = >::translate::, _>(|maybe_old_members| { maybe_old_members.map(|old_members| { log::info!( target: "runtime::elections-phragmen", @@ -158,9 +157,9 @@ pub fn migrate_members_to_recorded_deposit(old_deposit: T::Balance) { } /// Migrate all runners-up to recorded deposit. -pub fn migrate_runners_up_to_recorded_deposit(old_deposit: T::Balance) { - let _ = - >::translate::, _>(|maybe_old_runners_up| { +pub fn migrate_runners_up_to_recorded_deposit(old_deposit: V::Balance) { + let _ = >::translate::, _>( + |maybe_old_runners_up| { maybe_old_runners_up.map(|old_runners_up| { log::info!( target: "runtime::elections-phragmen", @@ -172,5 +171,6 @@ pub fn migrate_runners_up_to_recorded_deposit(old_deposit: T::Balance .map(|(who, stake)| SeatHolder { who, stake, deposit: old_deposit }) .collect::>() }) - }); + }, + ); } diff --git a/frame/offences/src/migration.rs b/frame/offences/src/migration.rs index 0d6c98b564cb1..e8fab1c0babc7 100644 --- a/frame/offences/src/migration.rs +++ b/frame/offences/src/migration.rs @@ -16,9 +16,7 @@ // limitations under the License. use super::{Config, OffenceDetails, Perbill, SessionIndex}; -use frame_support::{ - generate_storage_alias, pallet_prelude::ValueQuery, traits::Get, weights::Weight, -}; +use frame_support::{pallet_prelude::ValueQuery, storage_alias, traits::Get, weights::Weight}; use sp_staking::offence::{DisableStrategy, OnOffenceHandler}; use sp_std::vec::Vec; @@ -31,10 +29,9 @@ type DeferredOffenceOf = ( // Deferred reports that have been rejected by the offence handler and need to be submitted // at a later time. -generate_storage_alias!( - Offences, - DeferredOffences => Value>, ValueQuery> -); +#[storage_alias] +type DeferredOffences = + StorageValue, Vec>, ValueQuery>; pub fn remove_deferred_storage() -> Weight { let mut weight = T::DbWeight::get().reads_writes(1, 1); diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 96c905f4e5942..14846da8a5d54 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -139,18 +139,20 @@ pub mod v8 { pub mod v7 { use super::*; - use frame_support::generate_storage_alias; + use frame_support::storage_alias; - generate_storage_alias!(Staking, CounterForValidators => Value); - generate_storage_alias!(Staking, CounterForNominators => Value); + #[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::::get().unwrap().is_zero(), "CounterForValidators already set." ); assert!( - CounterForNominators::get().unwrap().is_zero(), + CounterForNominators::::get().unwrap().is_zero(), "CounterForNominators already set." ); assert!(Validators::::count().is_zero(), "Validators already set."); @@ -164,8 +166,8 @@ pub mod v7 { let validator_count = Validators::::iter().count() as u32; let nominator_count = Nominators::::iter().count() as u32; - CounterForValidators::put(validator_count); - CounterForNominators::put(nominator_count); + CounterForValidators::::put(validator_count); + CounterForNominators::::put(nominator_count); StorageVersion::::put(Releases::V7_0_0); log!(info, "Completed staking migration to Releases::V7_0_0"); @@ -176,26 +178,35 @@ pub mod v7 { pub mod v6 { use super::*; - use frame_support::{generate_storage_alias, traits::Get, weights::Weight}; + use frame_support::{storage_alias, traits::Get, weights::Weight}; // NOTE: value type doesn't matter, we just set it to () here. - generate_storage_alias!(Staking, SnapshotValidators => Value<()>); - generate_storage_alias!(Staking, SnapshotNominators => Value<()>); - generate_storage_alias!(Staking, QueuedElected => Value<()>); - generate_storage_alias!(Staking, QueuedScore => Value<()>); - generate_storage_alias!(Staking, EraElectionStatus => Value<()>); - generate_storage_alias!(Staking, IsCurrentSessionFinal => Value<()>); + #[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()); + 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!"); + assert!( + IsCurrentSessionFinal::::exists(), + "IsCurrentSessionFinal storage item not found!" + ); + assert!(EraElectionStatus::::exists(), "EraElectionStatus storage item not found!"); Ok(()) } @@ -203,12 +214,12 @@ pub mod v6 { pub fn migrate() -> Weight { log!(info, "Migrating staking to Releases::V6_0_0"); - SnapshotValidators::kill(); - SnapshotNominators::kill(); - QueuedElected::kill(); - QueuedScore::kill(); - EraElectionStatus::kill(); - IsCurrentSessionFinal::kill(); + SnapshotValidators::::kill(); + SnapshotNominators::::kill(); + QueuedElected::::kill(); + QueuedScore::::kill(); + EraElectionStatus::::kill(); + IsCurrentSessionFinal::::kill(); StorageVersion::::put(Releases::V6_0_0); log!(info, "Done."); diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 92564e94493c1..f8aaa5fe37749 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -31,6 +31,7 @@ mod pallet; mod pallet_error; mod partial_eq_no_bound; mod storage; +mod storage_alias; mod transactional; mod tt_macro; @@ -575,3 +576,10 @@ pub fn derive_pallet_error(input: TokenStream) -> TokenStream { pub fn __create_tt_macro(input: TokenStream) -> TokenStream { tt_macro::create_tt_return_macro(input) } + +#[proc_macro_attribute] +pub fn storage_alias(_: TokenStream, input: TokenStream) -> TokenStream { + storage_alias::storage_alias(input.into()) + .unwrap_or_else(|r| r.into_compile_error()) + .into() +} diff --git a/frame/support/procedural/src/storage_alias.rs b/frame/support/procedural/src/storage_alias.rs new file mode 100644 index 0000000000000..adec995016653 --- /dev/null +++ b/frame/support/procedural/src/storage_alias.rs @@ -0,0 +1,566 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 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 the `storage_alias` attribute macro. + +use frame_support_procedural_tools::generate_crate_access_2018; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, ToTokens}; +use syn::{ + ext::IdentExt, + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + token, Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, +}; + +/// Represents a path that only consists of [`Ident`] separated by `::`. +struct SimplePath { + leading_colon: Option, + segments: Punctuated, +} + +impl SimplePath { + /// Returns the [`Ident`] of this path. + /// + /// It only returns `Some(_)` if there is exactly one element and no leading colon. + fn get_ident(&self) -> Option<&Ident> { + if self.segments.len() != 1 || self.leading_colon.is_some() { + None + } else { + self.segments.first() + } + } +} + +impl Parse for SimplePath { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + leading_colon: if input.peek(Token![::]) { Some(input.parse()?) } else { None }, + segments: Punctuated::parse_separated_nonempty_with(input, |p| Ident::parse_any(p))?, + }) + } +} + +impl ToTokens for SimplePath { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.leading_colon.to_tokens(tokens); + self.segments.to_tokens(tokens); + } +} + +/// Represents generics which only support [`Ident`] separated by commas as you would pass it to a +/// type. +struct TypeGenerics { + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], +} + +impl TypeGenerics { + /// Returns the generics for types declarations etc. + fn iter(&self) -> impl Iterator { + self.params.iter() + } +} + +impl Parse for TypeGenerics { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } +} + +impl ToTokens for TypeGenerics { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } +} + +/// Represents generics which only support [`TypeParam`] separated by commas. +struct SimpleGenerics { + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], +} + +impl SimpleGenerics { + /// Returns the generics for types declarations etc. + fn type_generics(&self) -> impl Iterator { + self.params.iter().map(|p| &p.ident) + } + + /// Returns the generics for the `impl` block. + fn impl_generics(&self) -> impl Iterator { + self.params.iter() + } +} + +impl Parse for SimpleGenerics { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } +} + +impl ToTokens for SimpleGenerics { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } +} + +mod storage_types { + syn::custom_keyword!(StorageValue); + syn::custom_keyword!(StorageMap); + syn::custom_keyword!(StorageDoubleMap); + syn::custom_keyword!(StorageNMap); +} + +/// The supported storage types +enum StorageType { + Value { + _kw: storage_types::StorageValue, + _lt_token: Token![<], + prefix: SimplePath, + prefix_generics: Option, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + Map { + _kw: storage_types::StorageMap, + _lt_token: Token![<], + prefix: SimplePath, + prefix_generics: Option, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + DoubleMap { + _kw: storage_types::StorageDoubleMap, + _lt_token: Token![<], + prefix: SimplePath, + prefix_generics: Option, + _hasher1_comma: Token![,], + hasher1_ty: Type, + _key1_comma: Token![,], + key1_ty: Type, + _hasher2_comma: Token![,], + hasher2_ty: Type, + _key2_comma: Token![,], + key2_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + NMap { + _kw: storage_types::StorageNMap, + _lt_token: Token![<], + prefix: SimplePath, + prefix_generics: Option, + _paren_comma: Token![,], + _paren_token: token::Paren, + key_types: Punctuated, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, +} + +impl StorageType { + /// Generate the actual type declaration. + fn generate_type_declaration( + &self, + crate_: &Ident, + storage_instance: &StorageInstance, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + visibility: &Visibility, + attributes: &[Attribute], + ) -> TokenStream { + let storage_instance = &storage_instance.name; + let attributes = attributes.iter(); + let storage_generics = storage_generics.map(|g| { + let generics = g.type_generics(); + + quote!( < #( #generics ),* > ) + }); + + match self { + Self::Value { value_ty, query_type, prefix_generics, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< + #storage_instance #prefix_generics, + #value_ty + #query_type + >; + } + }, + Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageMap< + #storage_instance #prefix_generics, + #hasher_ty, + #key_ty, + #value_ty + #query_type + >; + } + }, + Self::DoubleMap { + value_ty, + query_type, + hasher1_ty, + key1_ty, + hasher2_ty, + key2_ty, + prefix_generics, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< + #storage_instance #prefix_generics, + #hasher1_ty, + #key1_ty, + #hasher2_ty, + #key2_ty, + #value_ty + #query_type + >; + } + }, + Self::NMap { value_ty, query_type, key_types, prefix_generics, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let key_types = key_types.iter(); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< + #storage_instance #prefix_generics, + ( #( #key_types ),* ), + #value_ty + #query_type + >; + } + }, + } + } + + /// The prefix for this storage type. + fn prefix(&self) -> &SimplePath { + match self { + Self::Value { prefix, .. } | + Self::Map { prefix, .. } | + Self::NMap { prefix, .. } | + Self::DoubleMap { prefix, .. } => prefix, + } + } + + /// The prefix generics for this storage type. + fn prefix_generics(&self) -> Option<&TypeGenerics> { + match self { + Self::Value { prefix_generics, .. } | + Self::Map { prefix_generics, .. } | + Self::NMap { prefix_generics, .. } | + Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(), + } + } +} + +impl Parse for StorageType { + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + + let parse_query_type = |input: ParseStream<'_>| -> Result> { + if input.peek(Token![,]) && !input.peek2(Token![>]) { + Ok(Some((input.parse()?, input.parse()?))) + } else { + Ok(None) + } + }; + + let parse_pallet_generics = |input: ParseStream<'_>| -> Result> { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![<]) { + Ok(Some(input.parse()?)) + } else if lookahead.peek(Token![,]) { + Ok(None) + } else { + Err(lookahead.error()) + } + }; + + if lookahead.peek(storage_types::StorageValue) { + Ok(Self::Value { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + prefix_generics: parse_pallet_generics(input)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageMap) { + Ok(Self::Map { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + prefix_generics: parse_pallet_generics(input)?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageDoubleMap) { + Ok(Self::DoubleMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + prefix_generics: parse_pallet_generics(input)?, + _hasher1_comma: input.parse()?, + hasher1_ty: input.parse()?, + _key1_comma: input.parse()?, + key1_ty: input.parse()?, + _hasher2_comma: input.parse()?, + hasher2_ty: input.parse()?, + _key2_comma: input.parse()?, + key2_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageNMap) { + let content; + Ok(Self::NMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + prefix_generics: parse_pallet_generics(input)?, + _paren_comma: input.parse()?, + _paren_token: parenthesized!(content in input), + key_types: Punctuated::parse_terminated(&content)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else { + Err(lookahead.error()) + } + } +} + +/// The input expected by this macro. +struct Input { + attributes: Vec, + visibility: Visibility, + _type: Token![type], + storage_name: Ident, + storage_generics: Option, + where_clause: Option, + _equal: Token![=], + storage_type: StorageType, + _semicolon: Token![;], +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result { + let attributes = input.call(Attribute::parse_outer)?; + let visibility = input.parse()?; + let _type = input.parse()?; + let storage_name = input.parse()?; + + let lookahead = input.lookahead1(); + let storage_generics = if lookahead.peek(Token![<]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let lookahead = input.lookahead1(); + let where_clause = if lookahead.peek(Token![where]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let _equal = input.parse()?; + + let storage_type = input.parse()?; + + let _semicolon = input.parse()?; + + Ok(Self { + attributes, + visibility, + _type, + storage_name, + storage_generics, + _equal, + storage_type, + where_clause, + _semicolon, + }) + } +} + +/// Implementation of the `storage_alias` attribute macro. +pub fn storage_alias(input: TokenStream) -> Result { + let input = syn::parse2::(input)?; + let crate_ = generate_crate_access_2018("frame-support")?; + + let storage_instance = generate_storage_instance( + &crate_, + &input.storage_name, + input.storage_generics.as_ref(), + input.where_clause.as_ref(), + input.storage_type.prefix(), + input.storage_type.prefix_generics(), + &input.visibility, + )?; + + let definition = input.storage_type.generate_type_declaration( + &crate_, + &storage_instance, + &input.storage_name, + input.storage_generics.as_ref(), + &input.visibility, + &input.attributes, + ); + + let storage_instance_code = storage_instance.code; + + Ok(quote! { + #storage_instance_code + + #definition + }) +} + +/// The storage instance to use for the storage alias. +struct StorageInstance { + name: Ident, + code: TokenStream, +} + +/// Generate the [`StorageInstance`] for the storage alias. +fn generate_storage_instance( + crate_: &Ident, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + storage_where_clause: Option<&WhereClause>, + prefix: &SimplePath, + prefix_generics: Option<&TypeGenerics>, + visibility: &Visibility, +) -> Result { + let (pallet_prefix, impl_generics, type_generics) = + if let Some((prefix_generics, storage_generics)) = + prefix_generics.and_then(|p| storage_generics.map(|s| (p, s))) + { + let type_generics = prefix_generics.iter(); + let type_generics2 = prefix_generics.iter(); + let impl_generics = storage_generics + .impl_generics() + .filter(|g| prefix_generics.params.iter().any(|pg| *pg == g.ident)); + + ( + quote! { + <#prefix < #( #type_generics2 ),* > as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } else if let Some(prefix) = prefix.get_ident() { + let prefix_str = prefix.to_string(); + + (quote!(#prefix_str), quote!(), quote!()) + } else { + return Err(Error::new_spanned( + prefix, + "If there are no generics, the prefix is only allowed to be an identifier.", + )) + }; + + let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); + + let name = Ident::new(&format!("{}_Storage_Instance", storage_name), Span::call_site()); + let storage_name_str = storage_name.to_string(); + + // Implement `StorageInstance` trait. + let code = quote! { + #visibility struct #name< #impl_generics >( + #crate_::sp_std::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #storage_name_str; + } + }; + + Ok(StorageInstance { name, code }) +} diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 33830257cdd82..d73a01187bfcc 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -166,178 +166,74 @@ macro_rules! bounded_btree_map { /// Useful for creating a *storage-like* struct for test and migrations. /// /// ``` -/// # use frame_support::generate_storage_alias; +/// # use frame_support::storage_alias; /// use frame_support::codec; /// use frame_support::Twox64Concat; /// // generate a storage value with type u32. -/// generate_storage_alias!(Prefix, StorageName => Value); +/// #[storage_alias] +/// type StorageName = StorageValue; /// /// // generate a double map from `(u32, u32)` (with hashers `Twox64Concat` for each key) /// // to `Vec` -/// generate_storage_alias!( -/// OtherPrefix, OtherStorageName => DoubleMap< -/// (Twox64Concat, u32), -/// (Twox64Concat, u32), -/// Vec -/// > -/// ); +/// #[storage_alias] +/// type OtherStorageName = StorageDoubleMap< +/// OtherPrefix, +/// Twox64Concat, +/// u32, +/// Twox64Concat, +/// u32, +/// Vec, +/// >; /// /// // optionally specify the query type /// use frame_support::pallet_prelude::{ValueQuery, OptionQuery}; -/// generate_storage_alias!(Prefix, ValueName => Value); -/// generate_storage_alias!( -/// Prefix, SomeStorageName => DoubleMap< -/// (Twox64Concat, u32), -/// (Twox64Concat, u32), -/// Vec, -/// ValueQuery -/// > -/// ); +/// #[storage_alias] +/// type ValueName = StorageValue; +/// #[storage_alias] +/// type SomeStorageName = StorageMap< +/// Prefix, +/// Twox64Concat, +/// u32, +/// Vec, +/// ValueQuery, +/// >; /// /// // generate a map from `Config::AccountId` (with hasher `Twox64Concat`) to `Vec` /// trait Config { type AccountId: codec::FullCodec; } -/// generate_storage_alias!( -/// Prefix, GenericStorage => Map<(Twox64Concat, T::AccountId), Vec> -/// ); +/// #[storage_alias] +/// type GenericStorage = StorageMap::AccountId, Vec>; +/// +/// // It also supports NMap +/// use frame_support::storage::types::Key as NMapKey; +/// +/// #[storage_alias] +/// type SomeNMap = StorageNMap, NMapKey), Vec>; +/// +/// // Using pallet name as prefix. +/// // +/// // When the first generic argument is taking generic arguments it is expected to be a pallet. +/// // The prefix will then be the pallet name as configured in the runtime through +/// // `construct_runtime!`. +/// +/// # struct Pallet(std::marker::PhantomData<(T, I)>); +/// # impl frame_support::traits::PalletInfoAccess for Pallet { +/// # fn index() -> usize { 0 } +/// # fn name() -> &'static str { "pallet" } +/// # fn module_name() -> &'static str { "module" } +/// # fn crate_version() -> frame_support::traits::CrateVersion { unimplemented!() } +/// # } +/// +/// #[storage_alias] +/// type SomeValue = StorageValue, u64>; +/// +/// // Pallet with instance +/// +/// #[storage_alias] +/// type SomeValue2 = StorageValue, u64>; +/// /// # fn main() {} /// ``` -#[macro_export] -macro_rules! generate_storage_alias { - // without generic for $name. - ($pallet:ident, $name:ident => Map<($hasher:ty, $key:ty), $value:ty $(, $querytype:ty)?>) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - type $name = $crate::storage::types::StorageMap< - [<$name Instance>], - $hasher, - $key, - $value, - $( $querytype )? - >; - } - }; - ( - $pallet:ident, - $name:ident - => DoubleMap<($hasher1:ty, $key1:ty), ($hasher2:ty, $key2:ty), $value:ty $(, $querytype:ty)?> - ) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - type $name = $crate::storage::types::StorageDoubleMap< - [<$name Instance>], - $hasher1, - $key1, - $hasher2, - $key2, - $value, - $( $querytype )? - >; - } - }; - ( - $pallet:ident, - $name:ident - => NMap, $value:ty $(, $querytype:ty)?> - ) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - type $name = $crate::storage::types::StorageNMap< - [<$name Instance>], - ( - $( $crate::storage::types::Key<$hasher, $key>, )+ - ), - $value, - $( $querytype )? - >; - } - }; - ($pallet:ident, $name:ident => Value<$value:ty $(, $querytype:ty)?>) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - type $name = $crate::storage::types::StorageValue< - [<$name Instance>], - $value, - $( $querytype )? - >; - } - }; - // with generic for $name. - ( - $pallet:ident, - $name:ident<$t:ident : $bounds:tt> - => Map<($hasher:ty, $key:ty), $value:ty $(, $querytype:ty)?> - ) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - #[allow(type_alias_bounds)] - type $name<$t : $bounds> = $crate::storage::types::StorageMap< - [<$name Instance>], - $hasher, - $key, - $value, - $( $querytype )? - >; - } - }; - ( - $pallet:ident, - $name:ident<$t:ident : $bounds:tt> - => DoubleMap<($hasher1:ty, $key1:ty), ($hasher2:ty, $key2:ty), $value:ty $(, $querytype:ty)?> - ) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - #[allow(type_alias_bounds)] - type $name<$t : $bounds> = $crate::storage::types::StorageDoubleMap< - [<$name Instance>], - $hasher1, - $key1, - $hasher2, - $key2, - $value, - $( $querytype )? - >; - } - }; - ( - $pallet:ident, - $name:ident<$t:ident : $bounds:tt> - => NMap<$(($hasher:ty, $key:ty),)+ $value:ty $(, $querytype:ty)?> - ) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - #[allow(type_alias_bounds)] - type $name<$t : $bounds> = $crate::storage::types::StorageNMap< - [<$name Instance>], - ( - $( $crate::storage::types::Key<$hasher, $key>, )+ - ), - $value, - $( $querytype )? - >; - } - }; - ($pallet:ident, $name:ident<$t:ident : $bounds:tt> => Value<$value:ty $(, $querytype:ty)?>) => { - $crate::paste::paste! { - $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); - #[allow(type_alias_bounds)] - type $name<$t : $bounds> = $crate::storage::types::StorageValue< - [<$name Instance>], - $value, - $( $querytype )? - >; - } - }; - // helper used in all arms. - (@GENERATE_INSTANCE_STRUCT $pallet:ident, $name:ident) => { - $crate::paste::paste! { - struct [<$name Instance>]; - impl $crate::traits::StorageInstance for [<$name Instance>] { - fn pallet_prefix() -> &'static str { stringify!($pallet) } - const STORAGE_PREFIX: &'static str = stringify!($name); - } - } - }; -} +pub use frame_support_procedural::storage_alias; /// Create new implementations of the [`Get`](crate::traits::Get) trait. /// @@ -995,16 +891,28 @@ pub mod tests { } #[test] - fn generate_storage_alias_works() { + fn storage_alias_works() { new_test_ext().execute_with(|| { - generate_storage_alias!( + #[crate::storage_alias] + type GenericData2 = StorageMap< Test, - GenericData2 => Map<(Blake2_128Concat, T::BlockNumber), T::BlockNumber> - ); + Blake2_128Concat, + ::BlockNumber, + ::BlockNumber, + >; assert_eq!(Module::::generic_data2(5), None); GenericData2::::insert(5, 5); assert_eq!(Module::::generic_data2(5), Some(5)); + + /// Some random docs that ensure that docs are accepted + #[crate::storage_alias] + pub type GenericData = StorageMap< + Test2, + Blake2_128Concat, + ::BlockNumber, + ::BlockNumber, + >; }); } diff --git a/frame/support/src/storage/bounded_btree_map.rs b/frame/support/src/storage/bounded_btree_map.rs index 0d589994bcc2c..390330457b9b9 100644 --- a/frame/support/src/storage/bounded_btree_map.rs +++ b/frame/support/src/storage/bounded_btree_map.rs @@ -348,12 +348,15 @@ pub mod test { use frame_support::traits::ConstU32; use sp_io::TestExternalities; - crate::generate_storage_alias! { Prefix, Foo => Value>> } - crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedBTreeMap>> } - crate::generate_storage_alias! { - Prefix, - FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedBTreeMap>> - } + #[crate::storage_alias] + type Foo = StorageValue>>; + + #[crate::storage_alias] + type FooMap = StorageMap>>; + + #[crate::storage_alias] + type FooDoubleMap = + StorageDoubleMap>>; fn map_from_keys(keys: &[K]) -> BTreeMap where diff --git a/frame/support/src/storage/bounded_btree_set.rs b/frame/support/src/storage/bounded_btree_set.rs index f38952bf545d9..275bb07c6275c 100644 --- a/frame/support/src/storage/bounded_btree_set.rs +++ b/frame/support/src/storage/bounded_btree_set.rs @@ -322,12 +322,15 @@ pub mod test { use frame_support::traits::ConstU32; use sp_io::TestExternalities; - crate::generate_storage_alias! { Prefix, Foo => Value>> } - crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedBTreeSet>> } - crate::generate_storage_alias! { - Prefix, - FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedBTreeSet>> - } + #[crate::storage_alias] + type Foo = StorageValue>>; + + #[crate::storage_alias] + type FooMap = StorageMap>>; + + #[crate::storage_alias] + type FooDoubleMap = + StorageDoubleMap>>; fn set_from_keys(keys: &[T]) -> BTreeSet where diff --git a/frame/support/src/storage/bounded_vec.rs b/frame/support/src/storage/bounded_vec.rs index e99ec6850b9f4..d3f9bfdd21d3b 100644 --- a/frame/support/src/storage/bounded_vec.rs +++ b/frame/support/src/storage/bounded_vec.rs @@ -672,12 +672,15 @@ pub mod test { use crate::{bounded_vec, traits::ConstU32, Twox128}; use sp_io::TestExternalities; - crate::generate_storage_alias! { Prefix, Foo => Value>> } - crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedVec>> } - crate::generate_storage_alias! { - Prefix, - FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedVec>> - } + #[crate::storage_alias] + type Foo = StorageValue>>; + + #[crate::storage_alias] + type FooMap = StorageMap>>; + + #[crate::storage_alias] + type FooDoubleMap = + StorageDoubleMap>>; #[test] fn slide_works() { diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index 16fcaf940c62a..1c308bc351902 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -525,10 +525,8 @@ mod test_iterators { fn double_map_iter_from() { sp_io::TestExternalities::default().execute_with(|| { use crate::hash::Identity; - crate::generate_storage_alias!( - MyModule, - MyDoubleMap => DoubleMap<(Identity, u64), (Identity, u64), u64> - ); + #[crate::storage_alias] + type MyDoubleMap = StorageDoubleMap; MyDoubleMap::insert(1, 10, 100); MyDoubleMap::insert(1, 21, 201); diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index da48952bcba87..bd24e14e9e0d8 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -384,7 +384,8 @@ mod test_iterators { fn map_iter_from() { sp_io::TestExternalities::default().execute_with(|| { use crate::hash::Identity; - crate::generate_storage_alias!(MyModule, MyMap => Map<(Identity, u64), u64>); + #[crate::storage_alias] + type MyMap = StorageMap; MyMap::insert(1, 10); MyMap::insert(2, 20); diff --git a/frame/support/src/storage/generator/nmap.rs b/frame/support/src/storage/generator/nmap.rs index be085ca2d9db6..18c912cf294ef 100755 --- a/frame/support/src/storage/generator/nmap.rs +++ b/frame/support/src/storage/generator/nmap.rs @@ -475,10 +475,12 @@ mod test_iterators { fn n_map_iter_from() { sp_io::TestExternalities::default().execute_with(|| { use crate::{hash::Identity, storage::Key as NMapKey}; - crate::generate_storage_alias!( + #[crate::storage_alias] + type MyNMap = StorageNMap< MyModule, - MyNMap => NMap, u64> - ); + (NMapKey, NMapKey, NMapKey), + u64, + >; MyNMap::insert((1, 1, 1), 11); MyNMap::insert((1, 1, 2), 21); @@ -518,11 +520,15 @@ mod test_iterators { let key_hash = NMap::hashed_key_for((1, 2)); { - crate::generate_storage_alias!(Test, NMap => DoubleMap< - (crate::Blake2_128Concat, u16), - (crate::Twox64Concat, u32), - u64 - >); + #[crate::storage_alias] + type NMap = StorageDoubleMap< + Test, + crate::Blake2_128Concat, + u16, + crate::Twox64Concat, + u32, + u64, + >; let value = NMap::get(1, 2).unwrap(); assert_eq!(value, 50); diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 115f179d803a7..7135ead850b6d 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -1545,10 +1545,8 @@ mod test { fn prefix_iterator_pagination_works() { TestExternalities::default().execute_with(|| { use crate::{hash::Identity, storage::generator::map::StorageMap}; - crate::generate_storage_alias! { - MyModule, - MyStorageMap => Map<(Identity, u64), u64> - } + #[crate::storage_alias] + type MyStorageMap = StorageMap; MyStorageMap::insert(1, 10); MyStorageMap::insert(2, 20); @@ -1663,12 +1661,13 @@ mod test { }); } - crate::generate_storage_alias! { Prefix, Foo => Value>> } - crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedVec>> } - crate::generate_storage_alias! { - Prefix, - FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedVec>> - } + #[crate::storage_alias] + type Foo = StorageValue>>; + #[crate::storage_alias] + type FooMap = StorageMap>>; + #[crate::storage_alias] + type FooDoubleMap = + StorageDoubleMap>>; #[test] fn try_append_works() { diff --git a/frame/support/src/storage/types/nmap.rs b/frame/support/src/storage/types/nmap.rs index 16dc30ea03903..5faeb5d8cac28 100755 --- a/frame/support/src/storage/types/nmap.rs +++ b/frame/support/src/storage/types/nmap.rs @@ -544,7 +544,7 @@ mod test { use crate::{ hash::{StorageHasher as _, *}, metadata::{StorageEntryModifier, StorageHasher}, - storage::types::{Key, ValueQuery}, + storage::types::{Key, Key as NMapKey, ValueQuery}, }; use sp_io::{hashing::twox_128, TestExternalities}; @@ -589,10 +589,8 @@ mod test { assert_eq!(AValueQueryWithAnOnEmpty::get((3,)), 10); { - crate::generate_storage_alias!(test, Foo => NMap< - Key<(Blake2_128Concat, u16)>, - u32 - >); + #[crate::storage_alias] + type Foo = StorageNMap), u32>; assert_eq!(Foo::contains_key((3,)), true); assert_eq!(Foo::get((3,)), Some(10)); diff --git a/frame/support/src/storage/weak_bounded_vec.rs b/frame/support/src/storage/weak_bounded_vec.rs index 21cc487b49082..8d1d38374d8a7 100644 --- a/frame/support/src/storage/weak_bounded_vec.rs +++ b/frame/support/src/storage/weak_bounded_vec.rs @@ -321,12 +321,13 @@ pub mod test { use frame_support::traits::ConstU32; use sp_io::TestExternalities; - crate::generate_storage_alias! { Prefix, Foo => Value>> } - crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), WeakBoundedVec>> } - crate::generate_storage_alias! { - Prefix, - FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), WeakBoundedVec>> - } + #[crate::storage_alias] + type Foo = StorageValue>>; + #[crate::storage_alias] + type FooMap = StorageMap>>; + #[crate::storage_alias] + type FooDoubleMap = + StorageDoubleMap>>; #[test] fn bound_returns_correct_value() { diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index ec97c1794c27b..792fd93299705 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -17,6 +17,7 @@ use frame_support::{ dispatch::{Parameter, UnfilteredDispatchable}, + pallet_prelude::ValueQuery, storage::unhashed, traits::{ ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, @@ -1631,3 +1632,17 @@ fn assert_type_all_pallets_without_system_reversed_is_correct() { _a(t) } } + +#[test] +fn test_storage_alias() { + #[frame_support::storage_alias] + type Value + where + ::AccountId: From + SomeAssociation1, + = StorageValue, u32, ValueQuery>; + + TestExternalities::default().execute_with(|| { + pallet::Value::::put(10); + assert_eq!(10, Value::::get()); + }) +} diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index 118794e2fa20a..29074843f4bbb 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -17,6 +17,7 @@ use frame_support::{ dispatch::UnfilteredDispatchable, + pallet_prelude::ValueQuery, storage::unhashed, traits::{ConstU32, GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade}, weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, @@ -821,3 +822,15 @@ fn test_pallet_info_access() { assert_eq!(::index(), 3); assert_eq!(::index(), 4); } + +#[test] +fn test_storage_alias() { + #[frame_support::storage_alias] + type Value, I: 'static> = + StorageValue, u32, ValueQuery>; + + TestExternalities::default().execute_with(|| { + pallet::Value::::put(10); + assert_eq!(10, Value::::get()); + }) +} diff --git a/frame/system/src/migrations/mod.rs b/frame/system/src/migrations/mod.rs index 872cf389d246c..f02af7a316fe1 100644 --- a/frame/system/src/migrations/mod.rs +++ b/frame/system/src/migrations/mod.rs @@ -17,6 +17,7 @@ //! Migrate the reference counting state. +use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ pallet_prelude::ValueQuery, traits::PalletInfoAccess, weights::Weight, Blake2_128Concat, @@ -52,43 +53,24 @@ pub trait V2ToV3 { type AccountData: 'static + FullCodec; } -// ### Warning -// -// The call below is only valid because the name System is enforced -// at runtime construction level for the system pallet. -frame_support::generate_storage_alias!( - System, UpgradedToU32RefCount => Value< - bool, - ValueQuery - > -); - -// ### Warning -// -// The call below is only valid because the name System is enforced -// at runtime construction level for the system pallet. -frame_support::generate_storage_alias!( - System, UpgradedToTripleRefCount => Value< - bool, - ValueQuery - > -); - -// ### Warning -// -// The call below is only valid because the name System is enforced -// at runtime construction level for the system pallet. -frame_support::generate_storage_alias!( - System, Account => Map< - (Blake2_128Concat, T::AccountId), - AccountInfo - > -); +#[frame_support::storage_alias] +type UpgradedToU32RefCount = StorageValue, bool, ValueQuery>; + +#[frame_support::storage_alias] +type UpgradedToTripleRefCount = StorageValue, bool, ValueQuery>; + +#[frame_support::storage_alias] +type Account = StorageMap< + Pallet, + Blake2_128Concat, + ::AccountId, + AccountInfo<::Index, ::AccountData>, +>; /// Migrate from unique `u8` reference counting to triple `u32` reference counting. -pub fn migrate_from_single_u8_to_triple_ref_count() -> Weight { +pub fn migrate_from_single_u8_to_triple_ref_count() -> Weight { let mut translated: usize = 0; - >::translate::<(T::Index, u8, T::AccountData), _>(|_key, (nonce, rc, data)| { + >::translate::<(V::Index, u8, V::AccountData), _>(|_key, (nonce, rc, data)| { translated += 1; Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data }) }); @@ -97,15 +79,15 @@ pub fn migrate_from_single_u8_to_triple_ref_count() -> Weight { "Applied migration from single u8 to triple reference counting to {:?} elements.", translated ); - ::put(true); - ::put(true); + >::put(true); + >::put(true); Weight::max_value() } /// Migrate from unique `u32` reference counting to triple `u32` reference counting. -pub fn migrate_from_single_to_triple_ref_count() -> Weight { +pub fn migrate_from_single_to_triple_ref_count() -> Weight { let mut translated: usize = 0; - >::translate::<(T::Index, RefCount, T::AccountData), _>( + >::translate::<(V::Index, RefCount, V::AccountData), _>( |_key, (nonce, consumers, data)| { translated += 1; Some(AccountInfo { nonce, consumers, providers: 1, sufficients: 0, data }) @@ -116,14 +98,14 @@ pub fn migrate_from_single_to_triple_ref_count() -> Weight { "Applied migration from single to triple reference counting to {:?} elements.", translated ); - ::put(true); + >::put(true); Weight::max_value() } /// Migrate from dual `u32` reference counting to triple `u32` reference counting. -pub fn migrate_from_dual_to_triple_ref_count() -> Weight { +pub fn migrate_from_dual_to_triple_ref_count() -> Weight { let mut translated: usize = 0; - >::translate::<(T::Index, RefCount, RefCount, T::AccountData), _>( + >::translate::<(V::Index, RefCount, RefCount, V::AccountData), _>( |_key, (nonce, consumers, providers, data)| { translated += 1; Some(AccountInfo { nonce, consumers, providers, sufficients: 0, data }) @@ -134,6 +116,6 @@ pub fn migrate_from_dual_to_triple_ref_count() -> Weight { "Applied migration from dual to triple reference counting to {:?} elements.", translated ); - ::put(true); + >::put(true); Weight::max_value() }