Implement a CountedStorageMap (#9125)

* initial impl

* expose in pallet_prelude

* temp test

* Apply suggestions from code review

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
Co-authored-by: Xiliang Chen <xlchen1291@gmail.com>

* implement with macro help.

* test for macro generation

* add iterable functions, some test and fixes

* fix merge

* doc

* Update frame/support/src/storage/types/counted_map.rs

Co-authored-by: Zeke Mostov <32168567+emostov@users.noreply.github.com>

* fix merge

* fmt

* fix spelling

* improve on removal

* fix partial storage info

* fmt

* add license

* suggested renames

* fix typo

* fix test

* fmt

* fix ui tests

* clearer doc

* better doc

* add metadata test

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
Co-authored-by: Xiliang Chen <xlchen1291@gmail.com>
Co-authored-by: Zeke Mostov <32168567+emostov@users.noreply.github.com>
This commit is contained in:
Guillaume Thiolliere
2021-09-16 15:20:29 +02:00
committed by GitHub
parent 49b6dfd2e5
commit 65e5fa3aa3
22 changed files with 1721 additions and 204 deletions
+3
View File
@@ -605,6 +605,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> {
+9
View File
@@ -180,6 +180,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.
@@ -19,29 +19,70 @@ use crate::pallet::{
parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
Def,
};
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>) -> syn::Result<()> {
fn check_prefix_duplicates(
storage_def: &StorageDef,
// A hashmap of all already used prefix and their associated error if duplication
used_prefixes: &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 !set.insert(prefix.clone()) {
let err = syn::Error::new(
storage_def.prefix_span(),
format!("Duplicate storage prefixes found for `{}`", prefix),
);
if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) {
let mut err = dup_err;
err.combine(other_dup_err);
return Err(err)
}
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 `{}`, used for counter associated to \
counted storage map",
counter_prefix,
),
);
if let Some(other_dup_err) =
used_prefixes.insert(counter_prefix.clone(), counter_dup_err.clone())
{
let mut err = counter_dup_err;
err.combine(other_dup_err);
return Err(err)
}
}
Ok(())
}
@@ -51,11 +92,8 @@ fn check_prefix_duplicates(storage_def: &StorageDef, set: &mut HashSet<String>)
/// * 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 {
@@ -109,6 +147,24 @@ 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,
@@ -162,11 +218,22 @@ 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 entries_builder = def.storages.iter().map(|storage| {
let docs = &storage.docs;
let ident = &storage.ident;
@@ -176,14 +243,14 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
let cfg_attrs = &storage.cfg_attrs;
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)* #frame_support::metadata::StorageEntryMetadata {
name: <#full_ident as #frame_support::storage::StorageEntryMetadata>::NAME,
modifier: <#full_ident as #frame_support::storage::StorageEntryMetadata>::MODIFIER,
ty: <#full_ident as #frame_support::storage::StorageEntryMetadata>::ty(),
default: <#full_ident as #frame_support::storage::StorageEntryMetadata>::default(),
docs: #frame_support::sp_std::vec![
#( #docs, )*
],
#(#cfg_attrs)*
{
<#full_ident as #frame_support::storage::StorageEntryMetadataBuilder>::build_metadata(
#frame_support::sp_std::vec![
#( #docs, )*
],
&mut entries,
);
}
)
});
@@ -246,6 +313,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 =>
@@ -311,7 +399,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,)>
@@ -351,9 +476,12 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
#frame_support::traits::PalletInfo
>::name::<#pallet_ident<#type_use_gen>>()
.expect("Every active pallet has a name in the runtime; qed"),
entries: #frame_support::sp_std::vec![
#( #entries, )*
],
entries: {
#[allow(unused_mut)]
let mut entries = #frame_support::sp_std::vec![];
#( #entries_builder )*
entries
},
}
}
}
@@ -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, key2: syn::Type },
NMap { keys: Vec<syn::Type>, keygen: syn::Type, value: syn::Type },
}
@@ -153,6 +154,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>,
@@ -173,6 +182,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)?, keygen, value },
@@ -186,6 +196,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(),
}
@@ -195,6 +206,7 @@ impl StorageGenerics {
enum StorageKind {
Value,
Map,
CountedMap,
DoubleMap,
NMap,
}
@@ -324,6 +336,33 @@ 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,
@@ -425,6 +464,11 @@ fn process_unnamed_generics(
Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
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 {
@@ -451,6 +495,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 => {
+16 -5
View File
@@ -1293,8 +1293,8 @@ pub mod pallet_prelude {
storage::{
bounded_vec::BoundedVec,
types::{
Key as NMapKey, OptionQuery, StorageDoubleMap, StorageMap, StorageNMap,
StorageValue, ValueQuery,
CountedStorageMap, Key as NMapKey, OptionQuery, StorageDoubleMap, StorageMap,
StorageNMap, StorageValue, ValueQuery,
},
},
traits::{
@@ -1673,6 +1673,8 @@ pub mod pallet_prelude {
/// * [`pallet_prelude::StorageValue`] expect `Value` and optionally `QueryKind` and `OnEmpty`,
/// * [`pallet_prelude::StorageMap`] expect `Hasher`, `Key`, `Value` and optionally `QueryKind`
/// and `OnEmpty`,
/// * [`pallet_prelude::CountedStorageMap`] expect `Hasher`, `Key`, `Value` and optionally
/// `QueryKind` and `OnEmpty`,
/// * [`pallet_prelude::StorageDoubleMap`] expect `Hasher1`, `Key1`, `Hasher2`, `Key2`, `Value`
/// and optionally `QueryKind` and `OnEmpty`.
///
@@ -1684,13 +1686,16 @@ pub mod pallet_prelude {
/// E.g. if runtime names the pallet "MyExample" then the storage `type Foo<T> = ...` use the
/// prefix: `Twox128(b"MyExample") ++ Twox128(b"Foo")`.
///
/// The optional attribute `#[pallet::storage_prefix = "$custom_name"]` allows to define a
/// specific name to use for the prefix.
/// For the `CountedStorageMap` variant, the Prefix also implements
/// `CountedStorageMapInstance`. It associate a `CounterPrefix`, which is implemented same as
/// above, but the storage prefix is prepend with `"CounterFor"`.
/// E.g. if runtime names the pallet "MyExample" then the storage
/// `type Foo<T> = CountedStorageaMap<...>` will store its counter at the prefix:
/// `Twox128(b"MyExample") ++ Twox128(b"CounterForFoo")`.
///
/// E.g:
/// ```ignore
/// #[pallet::storage]
/// #[pallet::storage_prefix = "OtherName"]
/// pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
/// ```
/// In this case the final prefix used by the map is
@@ -1699,9 +1704,13 @@ pub mod pallet_prelude {
/// The optional attribute `#[pallet::getter(fn $my_getter_fn_name)]` allows to define a
/// getter function on `Pallet`.
///
/// The optional attribute `#[pallet::storage_prefix = "SomeName"]` allow to define the storage
/// prefix to use, see how `Prefix` generic is implemented above.
///
/// E.g:
/// ```ignore
/// #[pallet::storage]
/// #[pallet::storage_prefix = "foo"]
/// #[pallet::getter(fn my_storage)]
/// pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
/// ```
@@ -1738,6 +1747,8 @@ pub mod pallet_prelude {
/// `_GeneratedPrefixForStorage$NameOfStorage`, and implements
/// [`StorageInstance`](traits::StorageInstance) on it using the pallet and storage name. It
/// then uses it as the first generic of the aliased type.
/// For `CountedStorageMap`, `CountedStorageMapInstance` is implemented, and another similar
/// struct is generated.
///
/// For named generic, the macro will reorder the generics, and remove the names.
///
@@ -219,6 +219,7 @@ where
previous_key: prefix,
drain: false,
closure: |_raw_key, mut raw_value| V::decode(&mut raw_value),
phantom: Default::default(),
}
}
@@ -345,6 +346,7 @@ where
let mut key_material = G::Hasher2::reverse(raw_key_without_prefix);
Ok((K2::decode(&mut key_material)?, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
@@ -398,6 +400,7 @@ where
let k2 = K2::decode(&mut k2_material)?;
Ok((k1, k2, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
@@ -138,6 +138,7 @@ where
let mut key_material = G::Hasher::reverse(raw_key_without_prefix);
Ok((K::decode(&mut key_material)?, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
@@ -196,6 +196,7 @@ where
previous_key: prefix,
drain: false,
closure: |_raw_key, mut raw_value| V::decode(&mut raw_value),
phantom: Default::default(),
}
}
@@ -305,6 +306,7 @@ impl<K: ReversibleKeyGenerator, V: FullCodec, G: StorageNMap<K, V>>
let partial_key = K::decode_partial_key(raw_key_without_prefix)?;
Ok((partial_key, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
@@ -368,6 +370,7 @@ impl<K: ReversibleKeyGenerator, V: FullCodec, G: StorageNMap<K, V>>
let (final_key, _) = K::decode_final_key(raw_key_without_prefix)?;
Ok((final_key, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
@@ -186,7 +186,7 @@ pub fn storage_iter_with_suffix<T: Decode + Sized>(
Ok((raw_key_without_prefix.to_vec(), value))
};
PrefixIterator { prefix, previous_key, drain: false, closure }
PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
@@ -219,7 +219,7 @@ pub fn storage_key_iter_with_suffix<
let value = T::decode(&mut &raw_value[..])?;
Ok((key, value))
};
PrefixIterator { prefix, previous_key, drain: false, closure }
PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
}
/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
@@ -344,11 +344,12 @@ pub fn move_prefix(from_prefix: &[u8], to_prefix: &[u8]) {
return
}
let iter = PrefixIterator {
let iter = PrefixIterator::<_> {
prefix: from_prefix.to_vec(),
previous_key: from_prefix.to_vec(),
drain: true,
closure: |key, value| Ok((key.to_vec(), value.to_vec())),
phantom: Default::default(),
};
for (key, value) in iter {
+31 -9
View File
@@ -17,7 +17,7 @@
//! Stuff to do with the runtime's storage.
pub use self::types::StorageEntryMetadata;
pub use self::types::StorageEntryMetadataBuilder;
use crate::{
hash::{ReversibleStorageHasher, StorageHasher},
storage::types::{
@@ -786,10 +786,12 @@ pub trait StorageNMap<K: KeyGenerator, V: FullCodec> {
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter;
}
/// Iterate over a prefix and decode raw_key and raw_value into `T`.
/// Iterate or drain over a prefix and decode raw_key and raw_value into `T`.
///
/// If any decoding fails it skips it and continues to the next key.
pub struct PrefixIterator<T> {
///
/// If draining, then the hook `OnRemoval::on_removal` is called after each removal.
pub struct PrefixIterator<T, OnRemoval = ()> {
prefix: Vec<u8>,
previous_key: Vec<u8>,
/// If true then value are removed while iterating
@@ -797,9 +799,21 @@ pub struct PrefixIterator<T> {
/// Function that take `(raw_key_without_prefix, raw_value)` and decode `T`.
/// `raw_key_without_prefix` is the raw storage key without the prefix iterated on.
closure: fn(&[u8], &[u8]) -> Result<T, codec::Error>,
phantom: core::marker::PhantomData<OnRemoval>,
}
impl<T> PrefixIterator<T> {
/// Trait for specialising on removal logic of [`PrefixIterator`].
pub trait PrefixIteratorOnRemoval {
/// This function is called whenever a key/value is removed.
fn on_removal(key: &[u8], value: &[u8]);
}
/// No-op implementation.
impl PrefixIteratorOnRemoval for () {
fn on_removal(_key: &[u8], _value: &[u8]) {}
}
impl<T, OnRemoval> PrefixIterator<T, OnRemoval> {
/// Creates a new `PrefixIterator`, iterating after `previous_key` and filtering out keys that
/// are not prefixed with `prefix`.
///
@@ -813,7 +827,13 @@ impl<T> PrefixIterator<T> {
previous_key: Vec<u8>,
decode_fn: fn(&[u8], &[u8]) -> Result<T, codec::Error>,
) -> Self {
PrefixIterator { prefix, previous_key, drain: false, closure: decode_fn }
PrefixIterator {
prefix,
previous_key,
drain: false,
closure: decode_fn,
phantom: Default::default(),
}
}
/// Get the last key that has been iterated upon and return it.
@@ -838,7 +858,7 @@ impl<T> PrefixIterator<T> {
}
}
impl<T> Iterator for PrefixIterator<T> {
impl<T, OnRemoval: PrefixIteratorOnRemoval> Iterator for PrefixIterator<T, OnRemoval> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
@@ -859,7 +879,8 @@ impl<T> Iterator for PrefixIterator<T> {
},
};
if self.drain {
unhashed::kill(&self.previous_key)
unhashed::kill(&self.previous_key);
OnRemoval::on_removal(&self.previous_key, &raw_value);
}
let raw_key_without_prefix = &self.previous_key[self.prefix.len()..];
let item = match (self.closure)(raw_key_without_prefix, &raw_value[..]) {
@@ -1119,7 +1140,7 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
/// Iter over all value of the storage.
///
/// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped.
/// NOTE: If a value failed to decode because storage is corrupted then it is skipped.
fn iter_values() -> PrefixIterator<Value> {
let prefix = Self::final_prefix();
PrefixIterator {
@@ -1127,6 +1148,7 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
previous_key: prefix.to_vec(),
drain: false,
closure: |_raw_key, mut raw_value| Value::decode(&mut raw_value),
phantom: Default::default(),
}
}
@@ -1613,7 +1635,7 @@ mod test {
assert_eq!(final_vec, vec![1, 2, 3, 4, 5]);
let mut iter = PrefixIterator::new(
let mut iter = PrefixIterator::<_>::new(
iter.prefix().to_vec(),
stored_key,
|mut raw_key_without_prefix, mut raw_value| {
File diff suppressed because it is too large Load Diff
@@ -19,9 +19,9 @@
//! StoragePrefixedDoubleMap traits and their methods directly.
use crate::{
metadata::{StorageEntryModifier, StorageEntryType},
metadata::{StorageEntryMetadata, StorageEntryType},
storage::{
types::{OptionQuery, QueryKindTrait, StorageEntryMetadata},
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
@@ -342,7 +342,7 @@ where
/// Iter over all value of the storage.
///
/// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped.
/// NOTE: If a value failed to decode because storage is corrupted then it is skipped.
pub fn iter_values() -> crate::storage::PrefixIterator<Value> {
<Self as crate::storage::StoragePrefixedMap<Value>>::iter_values()
}
@@ -512,7 +512,7 @@ where
}
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageEntryMetadata
StorageEntryMetadataBuilder
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
@@ -525,19 +525,20 @@ where
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const NAME: &'static str = Prefix::STORAGE_PREFIX;
fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadata>) {
let entry = StorageEntryMetadata {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryType::Map {
hashers: vec![Hasher1::METADATA, Hasher2::METADATA],
key: scale_info::meta_type::<(Key1, Key2)>(),
value: scale_info::meta_type::<Value>(),
},
default: OnEmpty::get().encode(),
docs,
};
fn ty() -> StorageEntryType {
StorageEntryType::Map {
hashers: vec![Hasher1::METADATA, Hasher2::METADATA],
key: scale_info::meta_type::<(Key1, Key2)>(),
value: scale_info::meta_type::<Value>(),
}
}
fn default() -> Vec<u8> {
OnEmpty::get().encode()
entries.push(entry);
}
}
@@ -605,7 +606,6 @@ mod test {
metadata::{StorageEntryModifier, StorageEntryType, StorageHasher},
storage::types::ValueQuery,
};
use assert_matches::assert_matches;
use sp_io::{hashing::twox_128, TestExternalities};
struct Prefix;
@@ -767,29 +767,42 @@ mod test {
A::translate::<u8, _>(|k1, k2, v| Some((k1 * k2 as u16 * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 1600), (3, 30, 900)]);
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
let assert_map_hashers = |ty, expected_hashers| {
if let StorageEntryType::Map { hashers, .. } = ty {
assert_eq!(hashers, expected_hashers)
} else {
assert_matches!(ty, StorageEntryType::Map { .. })
}
};
assert_map_hashers(
A::ty(),
vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat],
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: 97u32.encode(),
docs: vec![],
}
]
);
assert_map_hashers(
AValueQueryWithAnOnEmpty::ty(),
vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat],
);
assert_eq!(A::NAME, "foo");
assert_eq!(AValueQueryWithAnOnEmpty::default(), 97u32.encode());
assert_eq!(A::default(), Option::<u32>::None.encode());
WithLen::remove_all(None);
assert_eq!(WithLen::decode_len(3, 30), None);
@@ -19,9 +19,9 @@
//! methods directly.
use crate::{
metadata::{StorageEntryModifier, StorageEntryType},
metadata::{StorageEntryMetadata, StorageEntryType},
storage::{
types::{OptionQuery, QueryKindTrait, StorageEntryMetadata},
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
@@ -241,7 +241,7 @@ where
/// Iter over all value of the storage.
///
/// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped.
/// NOTE: If a value failed to decode because storage is corrupted then it is skipped.
pub fn iter_values() -> crate::storage::PrefixIterator<Value> {
<Self as crate::storage::StoragePrefixedMap<Value>>::iter_values()
}
@@ -336,7 +336,7 @@ where
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadata
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadataBuilder
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
@@ -347,19 +347,20 @@ where
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const NAME: &'static str = Prefix::STORAGE_PREFIX;
fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadata>) {
let entry = StorageEntryMetadata {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryType::Map {
hashers: vec![Hasher::METADATA],
key: scale_info::meta_type::<Key>(),
value: scale_info::meta_type::<Value>(),
},
default: OnEmpty::get().encode(),
docs,
};
fn ty() -> StorageEntryType {
StorageEntryType::Map {
hashers: vec![Hasher::METADATA],
key: scale_info::meta_type::<Key>(),
value: scale_info::meta_type::<Value>(),
}
}
fn default() -> Vec<u8> {
OnEmpty::get().encode()
entries.push(entry);
}
}
@@ -421,7 +422,6 @@ mod test {
metadata::{StorageEntryModifier, StorageEntryType, StorageHasher},
storage::types::ValueQuery,
};
use assert_matches::assert_matches;
use sp_io::{hashing::twox_128, TestExternalities};
struct Prefix;
@@ -573,25 +573,36 @@ mod test {
A::translate::<u8, _>(|k, v| Some((k * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]);
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
let assert_map_hashers = |ty, expected_hashers| {
if let StorageEntryType::Map { hashers, .. } = ty {
assert_eq!(hashers, expected_hashers)
} else {
assert_matches!(ty, StorageEntryType::Map { .. })
}
};
assert_map_hashers(A::ty(), vec![StorageHasher::Blake2_128Concat]);
assert_map_hashers(
AValueQueryWithAnOnEmpty::ty(),
vec![StorageHasher::Blake2_128Concat],
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: 97u32.encode(),
docs: vec![],
}
]
);
assert_eq!(A::NAME, "foo");
assert_eq!(AValueQueryWithAnOnEmpty::default(), 97u32.encode());
assert_eq!(A::default(), Option::<u32>::None.encode());
WithLen::remove_all(None);
assert_eq!(WithLen::decode_len(3), None);
@@ -18,16 +18,18 @@
//! Storage types to build abstraction on storage, they implements storage traits such as
//! StorageMap and others.
use crate::metadata::{StorageEntryModifier, StorageEntryType};
use crate::metadata::{StorageEntryMetadata, StorageEntryModifier};
use codec::FullCodec;
use sp_std::prelude::*;
mod counted_map;
mod double_map;
mod key;
mod map;
mod nmap;
mod value;
pub use counted_map::{CountedStorageMap, CountedStorageMapInstance};
pub use double_map::StorageDoubleMap;
pub use key::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, Key, KeyGenerator,
@@ -103,13 +105,10 @@ where
}
}
/// Provide metadata for a storage entry.
/// Build the metadata of a storage.
///
/// Implemented by each of the storage entry kinds: value, map, doublemap and nmap.
pub trait StorageEntryMetadata {
const MODIFIER: StorageEntryModifier;
const NAME: &'static str;
fn ty() -> StorageEntryType;
fn default() -> Vec<u8>;
/// Implemented by each of the storage types: value, map, countedmap, doublemap and nmap.
pub trait StorageEntryMetadataBuilder {
/// Build into `entries` the storage metadata entries of a storage given some `docs`.
fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadata>);
}
+122 -32
View File
@@ -19,11 +19,11 @@
//! StoragePrefixedDoubleMap traits and their methods directly.
use crate::{
metadata::{StorageEntryModifier, StorageEntryType},
metadata::{StorageEntryMetadata, StorageEntryType},
storage::{
types::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, OptionQuery, QueryKindTrait,
StorageEntryMetadata, TupleToEncodedIter,
StorageEntryMetadataBuilder, TupleToEncodedIter,
},
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap,
},
@@ -440,7 +440,7 @@ where
}
}
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadata
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadataBuilder
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
@@ -450,19 +450,20 @@ where
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const NAME: &'static str = Prefix::STORAGE_PREFIX;
fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadata>) {
let entry = StorageEntryMetadata {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryType::Map {
key: scale_info::meta_type::<Key::Key>(),
hashers: Key::HASHER_METADATA.iter().cloned().collect(),
value: scale_info::meta_type::<Value>(),
},
default: OnEmpty::get().encode(),
docs,
};
fn ty() -> StorageEntryType {
StorageEntryType::Map {
key: scale_info::meta_type::<Key::Key>(),
hashers: Key::HASHER_METADATA.iter().cloned().collect(),
value: scale_info::meta_type::<Value>(),
}
}
fn default() -> Vec<u8> {
OnEmpty::get().encode()
entries.push(entry);
}
}
@@ -516,8 +517,8 @@ where
mod test {
use super::*;
use crate::{
hash::*,
metadata::StorageEntryModifier,
hash::{StorageHasher as _, *},
metadata::{StorageEntryModifier, StorageHasher},
storage::types::{Key, ValueQuery},
};
use sp_io::{hashing::twox_128, TestExternalities};
@@ -684,11 +685,36 @@ mod test {
A::translate::<u8, _>(|k1, v| Some((k1 as u16 * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]);
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
assert_eq!(A::NAME, "Foo");
assert_eq!(AValueQueryWithAnOnEmpty::default(), 98u32.encode());
assert_eq!(A::default(), Option::<u32>::None.encode());
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: 98u32.encode(),
docs: vec![],
}
]
);
WithLen::remove_all(None);
assert_eq!(WithLen::decode_len((3,)), None);
@@ -852,11 +878,42 @@ mod test {
A::translate::<u8, _>(|(k1, k2), v| Some((k1 * k2 as u16 * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![((4, 40), 1600), ((3, 30), 900)]);
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
assert_eq!(A::NAME, "Foo");
assert_eq!(AValueQueryWithAnOnEmpty::default(), 98u32.encode());
assert_eq!(A::default(), Option::<u32>::None.encode());
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: 98u32.encode(),
docs: vec![],
}
]
);
WithLen::remove_all(None);
assert_eq!(WithLen::decode_len((3, 30)), None);
@@ -1042,11 +1099,44 @@ mod test {
});
assert_eq!(A::iter().collect::<Vec<_>>(), vec![((4, 40, 400), 4), ((3, 30, 300), 3)]);
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
assert_eq!(A::NAME, "Foo");
assert_eq!(AValueQueryWithAnOnEmpty::default(), 98u32.encode());
assert_eq!(A::default(), Option::<u32>::None.encode());
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u16, u16)>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "Foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Map {
hashers: vec![
StorageHasher::Blake2_128Concat,
StorageHasher::Blake2_128Concat,
StorageHasher::Twox64Concat
],
key: scale_info::meta_type::<(u16, u16, u16)>(),
value: scale_info::meta_type::<u32>(),
},
default: 98u32.encode(),
docs: vec![],
}
]
);
WithLen::remove_all(None);
assert_eq!(WithLen::decode_len((3, 30, 300)), None);
@@ -18,10 +18,10 @@
//! Storage value type. Implements StorageValue trait and its method directly.
use crate::{
metadata::{StorageEntryModifier, StorageEntryType},
metadata::{StorageEntryMetadata, StorageEntryType},
storage::{
generator::StorageValue as StorageValueT,
types::{OptionQuery, QueryKindTrait, StorageEntryMetadata},
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
StorageAppend, StorageDecodeLength, StorageTryAppend,
},
traits::{GetDefault, StorageInfo, StorageInstance},
@@ -201,7 +201,7 @@ where
}
}
impl<Prefix, Value, QueryKind, OnEmpty> StorageEntryMetadata
impl<Prefix, Value, QueryKind, OnEmpty> StorageEntryMetadataBuilder
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
@@ -209,15 +209,16 @@ where
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const NAME: &'static str = Prefix::STORAGE_PREFIX;
fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec<StorageEntryMetadata>) {
let entry = StorageEntryMetadata {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryType::Plain(scale_info::meta_type::<Value>()),
default: OnEmpty::get().encode(),
docs,
};
fn ty() -> StorageEntryType {
StorageEntryType::Plain(scale_info::meta_type::<Value>())
}
fn default() -> Vec<u8> {
OnEmpty::get().encode()
entries.push(entry);
}
}
@@ -342,11 +343,28 @@ mod test {
A::kill();
assert_eq!(A::try_get(), Err(()));
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
assert_eq!(A::NAME, "foo");
assert_eq!(A::default(), Option::<u32>::None.encode());
assert_eq!(AValueQueryWithAnOnEmpty::default(), 97u32.encode());
let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Plain(scale_info::meta_type::<u32>()),
default: Option::<u32>::None.encode(),
docs: vec![],
},
StorageEntryMetadata {
name: "foo",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Plain(scale_info::meta_type::<u32>()),
default: 97u32.encode(),
docs: vec![],
}
]
);
WithLen::kill();
assert_eq!(WithLen::decode_len(), None);
+91 -7
View File
@@ -323,6 +323,12 @@ pub mod pallet {
pub type ConditionalNMap<T> =
StorageNMap<_, (storage::Key<Blake2_128Concat, u8>, storage::Key<Twox64Concat, u16>), u32>;
#[pallet::storage]
#[pallet::storage_prefix = "RenamedCountedMap"]
#[pallet::getter(fn counted_storage_map)]
pub type SomeCountedStorageMap<T> =
CountedStorageMap<Hasher = Twox64Concat, Key = u8, Value = u32>;
#[pallet::genesis_config]
#[derive(Default)]
pub struct GenesisConfig {
@@ -416,6 +422,7 @@ pub mod pallet {
}
// Test that a pallet with non generic event and generic genesis_config is correctly handled
// and that a pallet without the attribute generate_storage_info is correctly handled.
#[frame_support::pallet]
pub mod pallet2 {
use super::{SomeAssociation1, SomeType1};
@@ -446,6 +453,10 @@ pub mod pallet2 {
#[pallet::storage]
pub type SomeValue<T: Config> = StorageValue<_, Vec<u32>>;
#[pallet::storage]
pub type SomeCountedStorageMap<T> =
CountedStorageMap<Hasher = Twox64Concat, Key = u8, Value = u32>;
#[pallet::event]
pub enum Event {
/// Something
@@ -899,6 +910,13 @@ fn storage_expand() {
pallet::ConditionalDoubleMap::<Runtime>::insert(1, 2, 3);
pallet::ConditionalNMap::<Runtime>::insert((1, 2), 3);
}
pallet::SomeCountedStorageMap::<Runtime>::insert(1, 2);
let mut k = [twox_128(b"Example"), twox_128(b"RenamedCountedMap")].concat();
k.extend(1u8.using_encoded(twox_64_concat));
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
let k = [twox_128(b"Example"), twox_128(b"CounterForRenamedCountedMap")].concat();
assert_eq!(unhashed::get::<u32>(&k), Some(1u32));
})
}
@@ -1180,6 +1198,24 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "RenamedCountedMap",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Twox64Concat],
key: meta_type::<u8>(),
value: meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "CounterForRenamedCountedMap",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Plain(meta_type::<u32>()),
default: vec![0, 0, 0, 0],
docs: vec!["Counter for the related counted storage map"],
},
],
}),
calls: Some(meta_type::<pallet::Call<Runtime>>().into()),
@@ -1370,6 +1406,24 @@ fn metadata() {
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "RenamedCountedMap",
modifier: StorageEntryModifier::Optional,
ty: StorageEntryType::Map {
hashers: vec![StorageHasher::Twox64Concat],
key: meta_type::<u8>(),
value: meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadata {
name: "CounterForRenamedCountedMap",
modifier: StorageEntryModifier::Default,
ty: StorageEntryType::Plain(meta_type::<u32>()),
default: vec![0, 0, 0, 0],
docs: vec!["Counter for the related counted storage map"],
},
],
}),
calls: Some(meta_type::<pallet::Call<Runtime>>().into()),
@@ -1577,17 +1631,47 @@ fn test_storage_info() {
max_size: Some(7 + 16 + 8),
}
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"RenamedCountedMap".to_vec(),
prefix: prefix(b"Example", b"RenamedCountedMap").to_vec(),
max_values: None,
max_size: Some(1 + 4 + 8),
},
StorageInfo {
pallet_name: b"Example".to_vec(),
storage_name: b"CounterForRenamedCountedMap".to_vec(),
prefix: prefix(b"Example", b"CounterForRenamedCountedMap").to_vec(),
max_values: Some(1),
max_size: Some(4),
},
],
);
assert_eq!(
Example2::storage_info(),
vec![StorageInfo {
pallet_name: b"Example2".to_vec(),
storage_name: b"SomeValue".to_vec(),
prefix: prefix(b"Example2", b"SomeValue").to_vec(),
max_values: Some(1),
max_size: None,
},],
vec![
StorageInfo {
pallet_name: b"Example2".to_vec(),
storage_name: b"SomeValue".to_vec(),
prefix: prefix(b"Example2", b"SomeValue").to_vec(),
max_values: Some(1),
max_size: None,
},
StorageInfo {
pallet_name: b"Example2".to_vec(),
storage_name: b"SomeCountedStorageMap".to_vec(),
prefix: prefix(b"Example2", b"SomeCountedStorageMap").to_vec(),
max_values: None,
max_size: None,
},
StorageInfo {
pallet_name: b"Example2".to_vec(),
storage_name: b"CounterForSomeCountedStorageMap".to_vec(),
prefix: prefix(b"Example2", b"CounterForSomeCountedStorageMap").to_vec(),
max_values: Some(1),
max_size: Some(4),
},
],
);
}
@@ -1,6 +1,6 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::StorageValue;
use frame_support::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
@@ -12,9 +12,15 @@ mod pallet {
#[pallet::storage]
type Foo<T> = StorageValue<_, u8>;
#[pallet::storage]
#[pallet::storage_prefix = "Foo"]
type NotFoo<T> = StorageValue<_, u16>;
#[pallet::storage]
#[pallet::storage_prefix = "Foo"]
type NotFoo<T> = StorageValue<_, u16>;
#[pallet::storage]
type CounterForBar<T> = StorageValue<_, u16>;
#[pallet::storage]
type Bar<T> = CountedStorageMap<_, Twox64Concat, u16, u16>;
}
fn main() {
@@ -1,17 +1,47 @@
error: Duplicate storage prefixes found for `Foo`
--> $DIR/duplicate_storage_prefix.rs:16:32
--> $DIR/duplicate_storage_prefix.rs:16:29
|
16 | #[pallet::storage_prefix = "Foo"]
| ^^^^^
error: Duplicate storage prefixes found for `Foo`
--> $DIR/duplicate_storage_prefix.rs:13:7
|
13 | type Foo<T> = StorageValue<_, u8>;
| ^^^
error: Duplicate storage prefixes found for `CounterForBar`, used for counter associated to counted storage map
--> $DIR/duplicate_storage_prefix.rs:23:7
|
23 | type Bar<T> = CountedStorageMap<_, Twox64Concat, u16, u16>;
| ^^^
error: Duplicate storage prefixes found for `CounterForBar`
--> $DIR/duplicate_storage_prefix.rs:20:7
|
20 | type CounterForBar<T> = StorageValue<_, u16>;
| ^^^^^^^^^^^^^
error[E0412]: cannot find type `_GeneratedPrefixForStorageFoo` in this scope
--> $DIR/duplicate_storage_prefix.rs:13:7
|
13 | type Foo<T> = StorageValue<_, u8>;
| ^^^ not found in this scope
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
--> $DIR/duplicate_storage_prefix.rs:17:35
error[E0412]: cannot find type `_GeneratedPrefixForStorageNotFoo` in this scope
--> $DIR/duplicate_storage_prefix.rs:17:7
|
17 | type NotFoo<T> = StorageValue<_, u16>;
| ^ not allowed in type signatures
| ^^^^^^ not found in this scope
error[E0412]: cannot find type `_GeneratedPrefixForStorageCounterForBar` in this scope
--> $DIR/duplicate_storage_prefix.rs:20:7
|
20 | type CounterForBar<T> = StorageValue<_, u16>;
| ^^^^^^^^^^^^^ not found in this scope
error[E0412]: cannot find type `_GeneratedPrefixForStorageBar` in this scope
--> $DIR/duplicate_storage_prefix.rs:23:7
|
23 | type Bar<T> = CountedStorageMap<_, Twox64Concat, u16, u16>;
| ^^^ not found in this scope
@@ -5,8 +5,8 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
| ^^^^^^^ the trait `TypeInfo` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `StaticTypeInfo` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12
@@ -16,8 +16,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
|
= note: required because of the requirements on the impl of `Decode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12
@@ -27,8 +27,8 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
= note: required because of the requirements on the impl of `FullEncode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12
@@ -39,8 +39,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied
= note: required because of the requirements on the impl of `Encode` for `Bar`
= note: required because of the requirements on the impl of `FullEncode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:9:12
@@ -5,8 +5,8 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
| ^^^^^^^ the trait `TypeInfo` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `StaticTypeInfo` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12
@@ -16,8 +16,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
|
= note: required because of the requirements on the impl of `Decode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12
@@ -27,8 +27,8 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
= note: required because of the requirements on the impl of `FullEncode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12
@@ -39,8 +39,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied
= note: required because of the requirements on the impl of `Encode` for `Bar`
= note: required because of the requirements on the impl of `FullEncode` for `Bar`
= note: required because of the requirements on the impl of `FullCodec` for `Bar`
= note: required because of the requirements on the impl of `frame_support::storage::StorageEntryMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `NAME`
= note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>`
= note: required by `build_metadata`
error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied
--> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:9:12
@@ -4,6 +4,6 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied
10 | #[pallet::generate_storage_info]
| ^^^^^^^^^^^^^^^^^^^^^ the trait `MaxEncodedLen` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `KeyGeneratorMaxEncodedLen` for `NMapKey<frame_support::Twox64Concat, Bar>`
= note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo<T>, NMapKey<frame_support::Twox64Concat, Bar>, u32>`
= note: required because of the requirements on the impl of `KeyGeneratorMaxEncodedLen` for `Key<frame_support::Twox64Concat, Bar>`
= note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo<T>, Key<frame_support::Twox64Concat, Bar>, u32>`
= note: required by `storage_info`