Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
implement key/value max len info
Browse files Browse the repository at this point in the history
  • Loading branch information
gui1117 committed May 5, 2021
1 parent 72e4dcc commit 4d469eb
Show file tree
Hide file tree
Showing 19 changed files with 309 additions and 78 deletions.
31 changes: 30 additions & 1 deletion frame/support/procedural/src/pallet/expand/pallet_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,35 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {

let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::<Vec<_>>();
let storage_cfg_attrs = &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::<Vec<_>>();
let storage_max_values= &def.storages.iter().map(|storage| &storage.max_values).collect::<Vec<_>>();
let storage_max_values= &def.storages.iter()
.map(|storage| if let Some(max_values) = &storage.max_values {
quote::quote_spanned!(def.pallet_struct.attr_span => {
let max_values = (|| {
let max_values: u32 = #max_values;
max_values
})();
Some(max_values)
})
} else {
quote::quote!(None)
})
.collect::<Vec<_>>();

let storage_max_size = &def.storages.iter()
.map(|storage| if let Some(attr_span) = def.pallet_struct.set_storage_max_len {
let storage_name = &storage.ident;
quote::quote_spanned!(attr_span =>
Some(
<
#storage_name<#type_use_gen>
as #frame_support::traits::StorageMaxEncodedLen
>::storage_max_encoded_len()
)
)
} else {
quote::quote!(None)
})
.collect::<Vec<_>>();

quote::quote_spanned!(def.pallet_struct.attr_span =>
#module_error_metadata
Expand Down Expand Up @@ -181,6 +209,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
#frame_support::traits::StorageInfo {
prefix: <#storage_names<#type_use_gen>>::final_prefix(),
max_values: #storage_max_values,
max_size: #storage_max_size,
}
},
)*
Expand Down
76 changes: 58 additions & 18 deletions frame/support/procedural/src/pallet/parse/pallet_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod keyword {
syn::custom_keyword!(pallet);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(generate_store);
syn::custom_keyword!(set_storage_max_encoded_len);
syn::custom_keyword!(Store);
}

Expand All @@ -39,12 +40,30 @@ pub struct PalletStructDef {
pub store: Option<(syn::Visibility, keyword::Store)>,
/// The span of the pallet::pallet attribute.
pub attr_span: proc_macro2::Span,
/// Whether to specify the storages max encoded len when implementing `StoragesInfo`.
/// Contains the span of the attribute.
pub set_storage_max_len: Option<proc_macro2::Span>,
}

/// Parse for `#[pallet::generate_store($vis trait Store)]`
pub struct PalletStructAttr {
vis: syn::Visibility,
keyword: keyword::Store,
/// Parse for one variant of:
/// * `#[pallet::generate_store($vis trait Store)]`
/// * `#[pallet::set_storage_max_encoded_len]`
pub enum PalletStructAttr {
GenerateStore {
span: proc_macro2::Span,
vis: syn::Visibility,
keyword: keyword::Store,
},
SetStorageMaxLen(proc_macro2::Span),
}

impl PalletStructAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::GenerateStore { span, .. } => *span,
Self::SetStorageMaxLen(span) => *span,
}
}
}

impl syn::parse::Parse for PalletStructAttr {
Expand All @@ -54,14 +73,23 @@ impl syn::parse::Parse for PalletStructAttr {
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::generate_store>()?;

let generate_content;
syn::parenthesized!(generate_content in content);
let vis = generate_content.parse::<syn::Visibility>()?;
generate_content.parse::<syn::Token![trait]>()?;
let keyword = generate_content.parse::<keyword::Store>()?;
Ok(Self { vis, keyword })

let lookahead = content.lookahead1();
if lookahead.peek(keyword::generate_store) {
let span = content.parse::<keyword::generate_store>()?.span();

let generate_content;
syn::parenthesized!(generate_content in content);
let vis = generate_content.parse::<syn::Visibility>()?;
generate_content.parse::<syn::Token![trait]>()?;
let keyword = generate_content.parse::<keyword::Store>()?;
Ok(Self::GenerateStore { vis, keyword, span })
} else if lookahead.peek(keyword::set_storage_max_encoded_len) {
let span = content.parse::<keyword::set_storage_max_encoded_len>()?.span();
Ok(Self::SetStorageMaxLen(span))
} else {
Err(lookahead.error())
}
}
}

Expand All @@ -78,12 +106,24 @@ impl PalletStructDef {
return Err(syn::Error::new(item.span(), msg));
};

let mut event_attrs: Vec<PalletStructAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
if event_attrs.len() > 1 {
let msg = "Invalid pallet::pallet, multiple argument pallet::generate_store found";
return Err(syn::Error::new(event_attrs[1].keyword.span(), msg));
let mut store = None;
let mut set_storage_max_len = None;

let struct_attrs: Vec<PalletStructAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
for attr in struct_attrs {
match attr {
PalletStructAttr::GenerateStore { vis, keyword, .. } if store.is_none() => {
store = Some((vis, keyword));
},
PalletStructAttr::SetStorageMaxLen(span) if set_storage_max_len.is_none() => {
set_storage_max_len = Some(span);
},
attr => {
let msg = "Unexpected duplicated attribute";
return Err(syn::Error::new(attr.span(), msg));
},
}
}
let store = event_attrs.pop().map(|attr| (attr.vis, attr.keyword));

let pallet = syn::parse2::<keyword::Pallet>(item.ident.to_token_stream())?;

Expand All @@ -100,6 +140,6 @@ 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 })
Ok(Self { index, instances, pallet, store, attr_span, set_storage_max_len })
}
}
10 changes: 5 additions & 5 deletions frame/support/procedural/src/pallet/parse/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub struct StorageDef {
pub cfg_attrs: Vec<syn::Attribute>,
/// The `max_values` expression if given the attribute `#[pallet::storage(max_values = $expr)]`
/// or a default.
pub max_values: syn::Expr,
pub max_values: Option<syn::Expr>,
}

/// In `Foo<A, B, C>` retrieve the argument at given position, i.e. A is argument at position 0.
Expand Down Expand Up @@ -171,18 +171,18 @@ impl StorageDef {
};
if attr_max_values.is_some() {
let msg = "Invalid pallet::storage, expect no max_values attributes for
`StorageValue`, i.e. only `#[pallet::storage]`";
`StorageValue` as it is always `1`, expect `#[pallet::storage]`";
return Err(syn::Error::new(attr_span, msg));
}
max_values = syn::parse_quote!(1u32);
max_values = Some(syn::parse_quote!(1));
}
"StorageMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 4);
metadata = Metadata::Map {
key: retrieve_arg(&typ.path.segments[0], 2)?,
value: retrieve_arg(&typ.path.segments[0], 3)?,
};
max_values = attr_max_values.unwrap_or_else(|| syn::parse_quote!(u32::max_value()));
max_values = attr_max_values;
}
"StorageDoubleMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 6);
Expand All @@ -191,7 +191,7 @@ impl StorageDef {
key2: retrieve_arg(&typ.path.segments[0], 4)?,
value: retrieve_arg(&typ.path.segments[0], 5)?,
};
max_values = attr_max_values.unwrap_or_else(|| syn::parse_quote!(u32::max_value()));
max_values = attr_max_values;
}
found => {
let msg = format!(
Expand Down
8 changes: 4 additions & 4 deletions frame/support/procedural/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ pub struct StorageLineDef {
getter: Option<syn::Ident>,
/// The name of the field to be used in genesis config if any.
config: Option<syn::Ident>,
/// The given max values with `max_values` attribute, or a sensible default expression.
max_values: syn::Expr,
/// The given max values with `max_values` attribute, or a none if not specified.
max_values: Option<syn::Expr>,
/// The build function of the storage if any.
build: Option<syn::Expr>,
/// Default value of genesis config field and also for storage when no value available.
Expand All @@ -204,8 +204,8 @@ pub struct StorageLineDefExt {
getter: Option<syn::Ident>,
/// The name of the field to be used in genesis config if any.
config: Option<syn::Ident>,
/// The given max values with `max_values` attribute, or a sensible default expression.
max_values: syn::Expr,
/// The given max values with `max_values` attribute, or a none if not specified.
max_values: Option<syn::Expr>,
/// The build function of the storage if any.
build: Option<syn::Expr>,
/// Default value of genesis config field and also for storage when no value available.
Expand Down
3 changes: 1 addition & 2 deletions frame/support/procedural/src/storage/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,15 +485,14 @@ fn parse_storage_line_defs(
let max_values = match &line.storage_type {
DeclStorageType::Map(_) | DeclStorageType::DoubleMap(_) => {
line.max_values.inner.map(|i| i.expr.content)
.unwrap_or(syn::parse_quote!(u32::max_value()))
},
DeclStorageType::Simple(_) => {
if let Some(max_values) = line.max_values.inner {
let msg = "unexpected max_values attribute for storage value.";
let span = max_values.max_values_keyword.span();
return Err(syn::Error::new(span, msg));
} else {
syn::parse_quote!(1u32)
Some(syn::parse_quote!(1u32))
}
},
};
Expand Down
13 changes: 12 additions & 1 deletion frame/support/procedural/src/storage/storages_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,25 @@ pub fn impl_storages_info(scrate: &TokenStream, def: &DeclStorageDefExt) -> Toke
for line in def.storage_lines.iter() {
let storage_struct = &line.storage_struct;
let value_type = &line.value_type;
let max_values = &line.max_values;
let storage_generator_trait = &line.storage_generator_trait;

let max_values = if let Some(max_values) = &line.max_values {
quote::quote!({
let max_values: u32 = (|| #max_values)();
Some(max_values)
})
} else {
quote::quote!(None)
};

let entry = match &line.storage_type {
StorageLineTypeDef::Simple(_) => quote!(
#scrate::traits::StorageInfo {
prefix: <
#storage_struct as #scrate::#storage_generator_trait
>::storage_value_final_key(),
max_values: #max_values,
max_size: None,
},
),
StorageLineTypeDef::Map(_)| StorageLineTypeDef::DoubleMap(_) => quote!(
Expand All @@ -44,6 +54,7 @@ pub fn impl_storages_info(scrate: &TokenStream, def: &DeclStorageDefExt) -> Toke
#storage_struct as #scrate::StoragePrefixedMap<#value_type>
>::final_prefix(),
max_values: #max_values,
max_size: None,
},
),
};
Expand Down
19 changes: 18 additions & 1 deletion frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,17 @@ pub mod pallet_prelude {
/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using
/// `<Pallet as Store>::Foo`.
///
/// To generate the full storage info (used for PoV calculation) use the attribute
/// `#[pallet::set_storage_max_encoded_len]`, e.g.:
/// ```ignore
/// #[pallet::pallet]
/// #[pallet::set_storage_max_encoded_len]
/// pub struct Pallet<T>(_);
/// ```
///
/// This require all storage to implement the trait [`traits::StorageMaxEncodedLen`], thus all keys
/// and value types must bound [`traits::MaxEncodedLen`].
///
/// ### Macro expansion:
///
/// The macro add this attribute to the struct definition:
Expand All @@ -1369,7 +1380,13 @@ pub mod pallet_prelude {
/// given by [`frame_support::traits::PalletInfo`].
/// (The implementation use the associated type `frame_system::Config::PalletInfo`).
///
/// If attribute generate_store then macro create the trait `Store` and implement it on `Pallet`.
/// It implements [`traits::StoragesInfo`] on `Pallet` which give information about all storages.
///
/// If the attribute generate_store is set then the macro creates the trait `Store` and implements
/// it on `Pallet`.
///
/// If the attribute set_storage_max_encoded_len is set then the macro call
/// [`traits::StorageMaxEncodedLen`] in the implementation of [`StoragesInfo`].
///
/// # Hooks: `#[pallet::hooks]` mandatory
///
Expand Down
22 changes: 21 additions & 1 deletion frame/support/src/storage/types/double_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ use crate::{
bounded_vec::{BoundedVec, BoundedVecValue},
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
},
traits::{GetDefault, StorageInstance, Get},
traits::{GetDefault, StorageInstance, Get, MaxEncodedLen, StorageMaxEncodedLen},
};
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
use sp_arithmetic::traits::SaturatedConversion;
use sp_std::vec::Vec;

/// A type that allow to store values for `(key1, key2)` couple. Similar to `StorageMap` but allow
Expand Down Expand Up @@ -466,6 +467,25 @@ impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty> StorageDou
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
}

impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty> StorageMaxEncodedLen
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty> where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec + MaxEncodedLen,
Key2: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
{
fn storage_max_encoded_len() -> u32 {
Key1::max_encoded_len()
.saturating_add(Key2::max_encoded_len())
.saturating_add(Value::max_encoded_len())
.saturated_into()
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
19 changes: 18 additions & 1 deletion frame/support/src/storage/types/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ use crate::{
bounded_vec::{BoundedVec, BoundedVecValue},
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
},
traits::{GetDefault, StorageInstance, Get},
traits::{GetDefault, StorageInstance, Get, MaxEncodedLen, StorageMaxEncodedLen},
};
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
use sp_arithmetic::traits::SaturatedConversion;
use sp_std::prelude::*;

/// A type that allow to store value for given key. Allowing to insert/remove/iterate on values.
Expand Down Expand Up @@ -350,6 +351,22 @@ impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> StorageMapMetadata
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
}

impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> StorageMaxEncodedLen
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
{
fn storage_max_encoded_len() -> u32 {
Key::max_encoded_len()
.saturating_add(Value::max_encoded_len())
.saturated_into()
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
Loading

0 comments on commit 4d469eb

Please sign in to comment.