Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frame/example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ pub mod pallet {
#[pallet::getter(fn foo)]
pub(super) type Foo<T: Config> = StorageValue<_, T::Balance, ValueQuery>;

#[pallet::storage]
pub type CountedMap<T> = CountedStorageMap<_, Blake2_128Concat, u8, u16>;

// The genesis config type.
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
Expand Down
9 changes: 9 additions & 0 deletions frame/example/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ fn signed_ext_watch_dummy_works() {
})
}

#[test]
fn counted_map_works() {
new_test_ext().execute_with(|| {
assert_eq!(CountedMap::<Test>::count(), 0);
CountedMap::<Test>::insert(3, 3);
assert_eq!(CountedMap::<Test>::count(), 1);
})
}

#[test]
fn weights_work() {
// must have a defined weight.
Expand Down
178 changes: 165 additions & 13 deletions frame/support/procedural/src/pallet/expand/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,66 @@
use crate::pallet::{Def, parse::storage::StorageDef};
use crate::pallet::parse::storage::{Metadata, QueryKind, StorageGenerics};
use frame_support_procedural_tools::clean_type_string;
use std::collections::HashSet;
use std::collections::HashMap;

/// Generate the prefix_ident related the the storage.
/// Generate the prefix_ident related to the storage.
/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
fn prefix_ident(storage: &StorageDef) -> syn::Ident {
let storage_ident = &storage.ident;
syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
}

/// Generate the counter_prefix_ident related to the storage.
/// counter_prefix_ident is used for the prefix struct to be given to counted storage map.
fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
syn::Ident::new(
&format!("_GeneratedCounterPrefixForStorage{}", storage_ident),
storage_ident.span(),
)
}

/// Generate the counter_prefix related to the storage.
/// counter_prefix is used by counted storage map.
fn counter_prefix(prefix: &str) -> String {
format!("CounterFor{}", prefix)
}

/// Check for duplicated storage prefixes. This step is necessary since users can specify an
/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure
/// that the prefix specified by the user is not a duplicate of an existing one.
fn check_prefix_duplicates(
storage_def: &StorageDef,
set: &mut HashSet<String>,
// A hashmap of all already used prefix and their associated error if duplication
set: &mut HashMap<String, syn::Error>,
) -> syn::Result<()> {
let prefix = storage_def.prefix();
let dup_err = syn::Error::new(
storage_def.prefix_span(),
format!("Duplicate storage prefixes found for `{}`", prefix),
);

if let Some(other_dup_err) = set.insert(prefix.clone(), dup_err.clone()) {
let mut err = dup_err;
err.combine(other_dup_err);
return Err(err);
}

if !set.insert(prefix.clone()) {
let err = syn::Error::new(
if let Metadata::CountedMap { .. } = storage_def.metadata {
let counter_prefix = counter_prefix(&prefix);
let counter_dup_err = syn::Error::new(
storage_def.prefix_span(),
format!("Duplicate storage prefixes found for `{}`", prefix),
format!(
"Duplicate storage prefixes found for `{}`, used for counter associated to \
counted storage map",
counter_prefix,
),
);
return Err(err);

if let Some(other_dup_err) = set.insert(counter_prefix.clone(), counter_dup_err.clone()) {
let mut err = counter_dup_err;
err.combine(other_dup_err);
return Err(err);
}
}

Ok(())
Expand All @@ -52,11 +88,8 @@ fn check_prefix_duplicates(
/// * Add `#[allow(type_alias_bounds)]`
pub fn process_generics(def: &mut Def) -> syn::Result<()> {
let frame_support = &def.frame_support;
let mut prefix_set = HashSet::new();

for storage_def in def.storages.iter_mut() {
check_prefix_duplicates(storage_def, &mut prefix_set)?;

let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];

let typ_item = match item {
Expand Down Expand Up @@ -112,6 +145,19 @@ pub fn process_generics(def: &mut Def) -> syn::Result<()> {
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::CountedMap {
hasher, key, value, query_kind, on_empty, max_values
} => {
args.args.push(syn::GenericArgument::Type(hasher));
args.args.push(syn::GenericArgument::Type(key));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::DoubleMap {
hasher1, key1, hasher2, key2, value, query_kind, on_empty, max_values,
} => {
Expand Down Expand Up @@ -157,11 +203,19 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
return e.into_compile_error().into();
}

// Check for duplicate prefixes
let mut prefix_set = HashMap::new();
let mut errors = def.storages.iter()
.filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err());
if let Some(mut final_error) = errors.next() {
errors.for_each(|error| final_error.combine(error));
return final_error.into_compile_error()
}

let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let pallet_ident = &def.pallet_struct.pallet;


let entries = def.storages.iter()
.map(|storage| {
let docs = &storage.docs;
Expand All @@ -179,6 +233,9 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
Metadata::Map { .. } => quote::quote_spanned!(storage.attr_span =>
#frame_support::storage::types::StorageMapMetadata
),
Metadata::CountedMap { .. } => quote::quote_spanned!(storage.attr_span =>
#frame_support::storage::types::CountedStorageMapMetadata
),
Metadata::DoubleMap { .. } => quote::quote_spanned!(storage.attr_span =>
#frame_support::storage::types::StorageDoubleMapMetadata
),
Expand Down Expand Up @@ -208,6 +265,18 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
}
)
},
Metadata::CountedMap { key, value } => {
let value = clean_type_string(&quote::quote!(#value).to_string());
let key = clean_type_string(&quote::quote!(#key).to_string());
quote::quote_spanned!(storage.attr_span =>
#frame_support::metadata::StorageEntryType::Map {
hasher: <#full_ident as #metadata_trait>::HASHER,
key: #frame_support::metadata::DecodeDifferent::Encode(#key),
value: #frame_support::metadata::DecodeDifferent::Encode(#value),
unused: false,
}
)
},
Metadata::DoubleMap { key1, key2, value } => {
let value = clean_type_string(&quote::quote!(#value).to_string());
let key1 = clean_type_string(&quote::quote!(#key1).to_string());
Expand Down Expand Up @@ -242,7 +311,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
}
};

quote::quote_spanned!(storage.attr_span =>
let mut metadata = quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)* #frame_support::metadata::StorageEntryMetadata {
name: #frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::NAME
Expand All @@ -256,7 +325,32 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
#( #docs, )*
]),
}
)
);

// Additional metadata for some storages:
if let Metadata::CountedMap { .. } = storage.metadata {
metadata.extend(quote::quote_spanned!(storage.attr_span =>
, #(#cfg_attrs)* #frame_support::metadata::StorageEntryMetadata {
name: #frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::COUNTER_NAME
),
modifier: <#full_ident as #metadata_trait>::COUNTER_MODIFIER,
ty: #frame_support::metadata::StorageEntryType::Plain(
#frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::COUNTER_TY
)
),
default: #frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::COUNTER_DEFAULT
),
documentation: #frame_support::metadata::DecodeDifferent::Encode(&[
<#full_ident as #metadata_trait>::COUNTER_DOC
]),
}
));
}

metadata
});

let getters = def.storages.iter()
Expand Down Expand Up @@ -317,6 +411,27 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
}
)
},
Metadata::CountedMap { key, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
#( #docs )*
pub fn #getter<KArg>(k: KArg) -> #query where
KArg: #frame_support::codec::EncodeLike<#key>,
{
// NOTE: we can't use any trait here because CountedStorageMap
// doesn't implement any.
<#full_ident>::get(k)
}
}
)
},
Metadata::DoubleMap { key1, key2, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Expand Down Expand Up @@ -381,7 +496,44 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {

let cfg_attrs = &storage_def.cfg_attrs;

let maybe_counter = if let Metadata::CountedMap { .. } = storage_def.metadata {
let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);

quote::quote_spanned!(storage_def.attr_span =>
#(#cfg_attrs)*
#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
);
#(#cfg_attrs)*
impl<#type_impl_gen> #frame_support::traits::StorageInstance
for #counter_prefix_struct_ident<#type_use_gen>
#config_where_clause
{
fn pallet_prefix() -> &'static str {
<
<T as #frame_system::Config>::PalletInfo
as #frame_support::traits::PalletInfo
>::name::<Pallet<#type_use_gen>>()
.expect("Every active pallet has a name in the runtime; qed")
}
const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
}
#(#cfg_attrs)*
impl<#type_impl_gen> #frame_support::storage::types::CountedStorageMapInstance
for #prefix_struct_ident<#type_use_gen>
#config_where_clause
{
type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
}
)
} else {
proc_macro2::TokenStream::default()
};

quote::quote_spanned!(storage_def.attr_span =>
#maybe_counter

#(#cfg_attrs)*
#prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
Expand Down
45 changes: 45 additions & 0 deletions frame/support/procedural/src/pallet/parse/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl syn::parse::Parse for PalletStorageAttr {
pub enum Metadata {
Value { value: syn::Type },
Map { value: syn::Type, key: syn::Type },
CountedMap { value: syn::Type, key: syn::Type },
DoubleMap {
value: syn::Type,
key1: syn::Type,
Expand Down Expand Up @@ -160,6 +161,14 @@ pub enum StorageGenerics {
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
CountedMap {
hasher: syn::Type,
key: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
Value {
value: syn::Type,
query_kind: Option<syn::Type>,
Expand All @@ -180,6 +189,7 @@ impl StorageGenerics {
let res = match self.clone() {
Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 },
Self::Map { value, key, .. } => Metadata::Map { value, key },
Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key },
Self::Value { value, .. } => Metadata::Value { value },
Self::NMap { keygen, value, .. } => Metadata::NMap {
keys: collect_keys(&keygen)?,
Expand All @@ -196,6 +206,7 @@ impl StorageGenerics {
match &self {
Self::DoubleMap { query_kind, .. }
| Self::Map { query_kind, .. }
| Self::CountedMap { query_kind, .. }
| Self::Value { query_kind, .. }
| Self::NMap { query_kind, .. }
=> query_kind.clone(),
Expand All @@ -206,6 +217,7 @@ impl StorageGenerics {
enum StorageKind {
Value,
Map,
CountedMap,
DoubleMap,
NMap,
}
Expand Down Expand Up @@ -333,6 +345,30 @@ fn process_named_generics(
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
}
StorageKind::CountedMap => {
check_generics(
&parsed,
&["Hasher", "Key", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"CountedStorageMap",
args_span,
)?;

StorageGenerics::CountedMap {
hasher: parsed.remove("Hasher")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
key: parsed.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
}
StorageKind::DoubleMap => {
check_generics(
&parsed,
Expand Down Expand Up @@ -436,6 +472,14 @@ fn process_unnamed_generics(
},
retrieve_arg(4).ok(),
),
StorageKind::CountedMap => (
None,
Metadata::CountedMap {
key: retrieve_arg(2)?,
value: retrieve_arg(3)?,
},
retrieve_arg(4).ok(),
),
StorageKind::DoubleMap => (
None,
Metadata::DoubleMap {
Expand Down Expand Up @@ -470,6 +514,7 @@ fn process_generics(
let storage_kind = match &*segment.ident.to_string() {
"StorageValue" => StorageKind::Value,
"StorageMap" => StorageKind::Map,
"CountedStorageMap" => StorageKind::CountedMap,
"StorageDoubleMap" => StorageKind::DoubleMap,
"StorageNMap" => StorageKind::NMap,
found => {
Expand Down
Loading