Allow to specify some max number of values for storages in pallet macro. (#8735)

* implement max_values + storages info

* some formatting + doc

* rename StoragesInfo -> PalletStorageInfo

* merge both StorageInfoTrait and PalletStorageInfo

I think it is more future proof. In the future some storage could make
use of multiple prefix. Like one to store how much value has been
inserted, etc...

* Update frame/support/procedural/src/storage/parse.rs

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

* Update frame/support/procedural/src/storage/storage_struct.rs

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

* Fix max_size using hasher information

hasher now expose `max_len` which allows to computes their maximum len.
For hasher without concatenation, it is the size of the hash part,
for hasher with concatenation, it is the size of the hash part + max
encoded len of the key.

* fix tests

* fix ui tests

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
This commit is contained in:
Guillaume Thiolliere
2021-05-17 15:44:24 +02:00
committed by GitHub
parent 59f34ab8bc
commit 9bf62ef65d
26 changed files with 1161 additions and 149 deletions
+38
View File
@@ -20,6 +20,7 @@
use codec::Codec;
use sp_std::prelude::Vec;
use sp_io::hashing::{blake2_128, blake2_256, twox_64, twox_128, twox_256};
use crate::traits::MaxEncodedLen;
// This trait must be kept coherent with frame-support-procedural HasherKind usage
pub trait Hashable: Sized {
@@ -59,6 +60,9 @@ pub trait StorageHasher: 'static {
const METADATA: frame_metadata::StorageHasher;
type Output: AsRef<[u8]>;
fn hash(x: &[u8]) -> Self::Output;
/// The max length of the final hash, for the given key type.
fn max_len<K: MaxEncodedLen>() -> usize;
}
/// Hasher to use to hash keys to insert to storage.
@@ -79,6 +83,9 @@ impl StorageHasher for Identity {
fn hash(x: &[u8]) -> Vec<u8> {
x.to_vec()
}
fn max_len<K: MaxEncodedLen>() -> usize {
K::max_encoded_len()
}
}
impl ReversibleStorageHasher for Identity {
fn reverse(x: &[u8]) -> &[u8] {
@@ -98,6 +105,9 @@ impl StorageHasher for Twox64Concat {
.cloned()
.collect::<Vec<_>>()
}
fn max_len<K: MaxEncodedLen>() -> usize {
K::max_encoded_len().saturating_add(8)
}
}
impl ReversibleStorageHasher for Twox64Concat {
fn reverse(x: &[u8]) -> &[u8] {
@@ -121,6 +131,9 @@ impl StorageHasher for Blake2_128Concat {
.cloned()
.collect::<Vec<_>>()
}
fn max_len<K: MaxEncodedLen>() -> usize {
K::max_encoded_len().saturating_add(16)
}
}
impl ReversibleStorageHasher for Blake2_128Concat {
fn reverse(x: &[u8]) -> &[u8] {
@@ -140,6 +153,9 @@ impl StorageHasher for Blake2_128 {
fn hash(x: &[u8]) -> [u8; 16] {
blake2_128(x)
}
fn max_len<K: MaxEncodedLen>() -> usize {
16
}
}
/// Hash storage keys with blake2 256
@@ -150,6 +166,9 @@ impl StorageHasher for Blake2_256 {
fn hash(x: &[u8]) -> [u8; 32] {
blake2_256(x)
}
fn max_len<K: MaxEncodedLen>() -> usize {
32
}
}
/// Hash storage keys with twox 128
@@ -160,6 +179,9 @@ impl StorageHasher for Twox128 {
fn hash(x: &[u8]) -> [u8; 16] {
twox_128(x)
}
fn max_len<K: MaxEncodedLen>() -> usize {
16
}
}
/// Hash storage keys with twox 256
@@ -170,6 +192,9 @@ impl StorageHasher for Twox256 {
fn hash(x: &[u8]) -> [u8; 32] {
twox_256(x)
}
fn max_len<K: MaxEncodedLen>() -> usize {
32
}
}
#[cfg(test)]
@@ -187,4 +212,17 @@ mod tests {
let r = Blake2_128Concat::hash(b"foo");
assert_eq!(r.split_at(16), (&blake2_128(b"foo")[..], &b"foo"[..]))
}
#[test]
fn max_lengths() {
use codec::Encode;
let encoded_0u32 = &0u32.encode()[..];
assert_eq!(Twox64Concat::hash(encoded_0u32).len(), Twox64Concat::max_len::<u32>());
assert_eq!(Twox128::hash(encoded_0u32).len(), Twox128::max_len::<u32>());
assert_eq!(Twox256::hash(encoded_0u32).len(), Twox256::max_len::<u32>());
assert_eq!(Blake2_128::hash(encoded_0u32).len(), Blake2_128::max_len::<u32>());
assert_eq!(Blake2_128Concat::hash(encoded_0u32).len(), Blake2_128Concat::max_len::<u32>());
assert_eq!(Blake2_256::hash(encoded_0u32).len(), Blake2_256::max_len::<u32>());
assert_eq!(Identity::hash(encoded_0u32).len(), Identity::max_len::<u32>());
}
}
+23 -2
View File
@@ -1234,7 +1234,10 @@ pub mod pallet_prelude {
EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, DebugNoBound, CloneNoBound, Twox256,
Twox128, Blake2_256, Blake2_128, Identity, Twox64Concat, Blake2_128Concat, ensure,
RuntimeDebug, storage,
traits::{Get, Hooks, IsType, GetPalletVersion, EnsureOrigin, PalletInfoAccess},
traits::{
Get, Hooks, IsType, GetPalletVersion, EnsureOrigin, PalletInfoAccess, StorageInfoTrait,
ConstU32, GetDefault,
},
dispatch::{DispatchResultWithPostInfo, Parameter, DispatchError, DispatchResult},
weights::{DispatchClass, Pays, Weight},
storage::types::{
@@ -1346,6 +1349,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::StorageInfoTrait`], thus all keys
/// and value types must bound [`traits::MaxEncodedLen`].
///
/// ### Macro expansion:
///
/// The macro add this attribute to the struct definition:
@@ -1370,7 +1384,14 @@ 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::StorageInfoTrait`] 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::StorageInfoTrait`] for each storage in the implementation of
/// [`traits::StorageInfoTrait`] for the pallet.
///
/// # Hooks: `#[pallet::hooks]` mandatory
///
@@ -21,14 +21,15 @@
use codec::{Decode, Encode, EncodeLike, FullCodec};
use crate::{
storage::{
StorageAppend, StorageDecodeLength,
StorageAppend, StorageDecodeLength, StoragePrefixedMap,
bounded_vec::BoundedVec,
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
},
traits::{GetDefault, StorageInstance, Get},
traits::{GetDefault, StorageInstance, Get, MaxEncodedLen, StorageInfo},
};
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
use sp_std::vec::Vec;
use sp_arithmetic::traits::SaturatedConversion;
use sp_std::prelude::*;
/// A type that allow to store values for `(key1, key2)` couple. Similar to `StorageMap` but allow
/// to iterate and remove value associated to first key.
@@ -47,14 +48,24 @@ use sp_std::vec::Vec;
/// such as `blake2_128_concat` must be used for Hasher1 (resp. Hasher2). Otherwise, other values
/// in storage can be compromised.
pub struct StorageDoubleMap<
Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind=OptionQuery, OnEmpty=GetDefault
Prefix,
Hasher1,
Key1,
Hasher2,
Key2,
Value,
QueryKind=OptionQuery,
OnEmpty=GetDefault,
MaxValues=GetDefault,
>(
core::marker::PhantomData<(Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty)>
core::marker::PhantomData<
(Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues)
>
);
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::generator::StorageDoubleMap<Key1, Key2, Value> for
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
@@ -63,7 +74,8 @@ where
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
type Query = QueryKind::Query;
type Hasher1 = Hasher1;
@@ -82,9 +94,9 @@ where
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
crate::storage::StoragePrefixedMap<Value> for
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StoragePrefixedMap<Value> for
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
@@ -93,7 +105,8 @@ where
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn module_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>>::module_prefix()
@@ -103,7 +116,7 @@ where
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, QueryKind, OnEmpty, VecValue, VecBound>
impl<Prefix, Hasher1, Key1, Hasher2, Key2, QueryKind, OnEmpty, MaxValues, VecValue, VecBound>
StorageDoubleMap<
Prefix,
Hasher1,
@@ -113,6 +126,7 @@ impl<Prefix, Hasher1, Key1, Hasher2, Key2, QueryKind, OnEmpty, VecValue, VecBoun
BoundedVec<VecValue, VecBound>,
QueryKind,
OnEmpty,
MaxValues,
> where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
@@ -120,7 +134,8 @@ impl<Prefix, Hasher1, Key1, Hasher2, Key2, QueryKind, OnEmpty, VecValue, VecBoun
Key1: FullCodec,
Key2: FullCodec,
QueryKind: QueryKindTrait<BoundedVec<VecValue, VecBound>, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
VecValue: FullCodec,
VecBound: Get<u32>,
{
@@ -147,8 +162,8 @@ impl<Prefix, Hasher1, Key1, Hasher2, Key2, QueryKind, OnEmpty, VecValue, VecBoun
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
@@ -157,7 +172,8 @@ where
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Get the storage key used to fetch a value corresponding to a specific key.
pub fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
@@ -376,8 +392,8 @@ where
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
@@ -386,7 +402,8 @@ where
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Enumerate all elements in the map with first key `k1` in no particular order.
///
@@ -440,8 +457,10 @@ pub trait StorageDoubleMapMetadata {
const HASHER2: frame_metadata::StorageHasher;
}
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty> StorageDoubleMapMetadata
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty> where
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageDoubleMapMetadata for
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
@@ -449,7 +468,8 @@ impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty> StorageDou
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const HASHER1: frame_metadata::StorageHasher = Hasher1::METADATA;
@@ -459,6 +479,36 @@ 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, MaxValues>
crate::traits::StorageInfoTrait for
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
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: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
vec![
StorageInfo {
prefix: Self::final_prefix(),
max_values: MaxValues::get(),
max_size: Some(
Hasher1::max_len::<Key1>()
.saturating_add(Hasher2::max_len::<Key2>())
.saturating_add(Value::max_encoded_len())
.saturated_into(),
),
}
]
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -17,7 +17,7 @@
//! Storage key type.
use crate::hash::{ReversibleStorageHasher, StorageHasher};
use crate::{hash::{ReversibleStorageHasher, StorageHasher}, traits::MaxEncodedLen};
use codec::{Encode, EncodeLike, FullCodec};
use paste::paste;
use sp_std::prelude::*;
@@ -53,6 +53,11 @@ pub trait KeyGenerator {
) -> Vec<u8>;
}
/// The maximum length used by the key in storage.
pub trait KeyGeneratorMaxEncodedLen: KeyGenerator {
fn key_max_encoded_len() -> usize;
}
/// A trait containing methods that are only implemented on the Key struct instead of the entire tuple.
pub trait KeyGeneratorInner: KeyGenerator {
type Hasher: StorageHasher;
@@ -91,6 +96,12 @@ impl<H: StorageHasher, K: FullCodec> KeyGenerator for Key<H, K> {
}
}
impl<H: StorageHasher, K: FullCodec + MaxEncodedLen> KeyGeneratorMaxEncodedLen for Key<H, K> {
fn key_max_encoded_len() -> usize {
H::max_len::<K>()
}
}
impl<H: StorageHasher, K: FullCodec> KeyGeneratorInner for Key<H, K> {
type Hasher = H;
@@ -139,6 +150,20 @@ impl KeyGenerator for Tuple {
}
}
#[impl_trait_for_tuples::impl_for_tuples(2, 18)]
#[tuple_types_custom_trait_bound(KeyGeneratorInner + KeyGeneratorMaxEncodedLen)]
impl KeyGeneratorMaxEncodedLen for Tuple {
fn key_max_encoded_len() -> usize {
let mut len = 0usize;
for_tuples!(
#(
len = len.saturating_add(Tuple::key_max_encoded_len());
)*
);
len
}
}
/// Marker trait to indicate that each element in the tuple encodes like the corresponding element
/// in another tuple.
///
@@ -21,13 +21,14 @@
use codec::{FullCodec, Decode, EncodeLike, Encode};
use crate::{
storage::{
StorageAppend, StorageDecodeLength,
StorageAppend, StorageDecodeLength, StoragePrefixedMap,
bounded_vec::BoundedVec,
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
},
traits::{GetDefault, StorageInstance, Get},
traits::{GetDefault, StorageInstance, Get, MaxEncodedLen, StorageInfo},
};
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.
@@ -43,20 +44,23 @@ use sp_std::prelude::*;
///
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
/// `blake2_128_concat` must be used. Otherwise, other values in storage can be compromised.
pub struct StorageMap<Prefix, Hasher, Key, Value, QueryKind=OptionQuery, OnEmpty=GetDefault>(
core::marker::PhantomData<(Prefix, Hasher, Key, Value, QueryKind, OnEmpty)>
pub struct StorageMap<
Prefix, Hasher, Key, Value, QueryKind=OptionQuery, OnEmpty=GetDefault, MaxValues=GetDefault,
>(
core::marker::PhantomData<(Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues)>
);
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::generator::StorageMap<Key, Value>
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
type Query = QueryKind::Query;
type Hasher = Hasher;
@@ -74,15 +78,17 @@ where
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> crate::storage::StoragePrefixedMap<Value> for
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
StoragePrefixedMap<Value> for
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn module_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageMap<Key, Value>>::module_prefix()
@@ -92,14 +98,15 @@ where
}
}
impl<Prefix, Hasher, Key, QueryKind, OnEmpty, VecValue, VecBound>
StorageMap<Prefix, Hasher, Key, BoundedVec<VecValue, VecBound>, QueryKind, OnEmpty>
impl<Prefix, Hasher, Key, QueryKind, OnEmpty, MaxValues, VecValue, VecBound>
StorageMap<Prefix, Hasher, Key, BoundedVec<VecValue, VecBound>, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
QueryKind: QueryKindTrait<BoundedVec<VecValue, VecBound>, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
VecValue: FullCodec,
VecBound: Get<u32>,
{
@@ -120,15 +127,16 @@ where
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Get the storage key used to fetch a value corresponding to a specific key.
pub fn hashed_key_for<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Vec<u8> {
@@ -283,15 +291,16 @@ where
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Enumerate all elements in the map in no particular order.
///
@@ -327,14 +336,15 @@ pub trait StorageMapMetadata {
const HASHER: frame_metadata::StorageHasher;
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> StorageMapMetadata
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> where
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StorageMapMetadata
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const HASHER: frame_metadata::StorageHasher = Hasher::METADATA;
@@ -343,6 +353,33 @@ impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> StorageMapMetadata
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::StorageInfoTrait for
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
vec![
StorageInfo {
prefix: Self::final_prefix(),
max_values: MaxValues::get(),
max_size: Some(
Hasher::max_len::<Key>()
.saturating_add(Value::max_encoded_len())
.saturated_into(),
),
}
]
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -30,7 +30,7 @@ mod value;
pub use double_map::{StorageDoubleMap, StorageDoubleMapMetadata};
pub use key::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, Key, KeyGenerator,
ReversibleKeyGenerator, TupleToEncodedIter,
ReversibleKeyGenerator, TupleToEncodedIter, KeyGeneratorMaxEncodedLen,
};
pub use map::{StorageMap, StorageMapMetadata};
pub use nmap::{StorageNMap, StorageNMapMetadata};
@@ -24,12 +24,13 @@ use crate::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, OnEmptyGetter,
OptionQuery, QueryKindTrait, TupleToEncodedIter,
},
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength,
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap,
},
traits::{GetDefault, StorageInstance},
traits::{Get, GetDefault, StorageInstance, StorageInfo, MaxEncodedLen},
};
use codec::{Decode, Encode, EncodeLike, FullCodec};
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
use sp_runtime::SaturatedConversion;
use sp_std::prelude::*;
/// A type that allow to store values for an arbitrary number of keys in the form of
@@ -50,18 +51,22 @@ use sp_std::prelude::*;
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher`
/// such as `blake2_128_concat` must be used for the key hashers. Otherwise, other values
/// in storage can be compromised.
pub struct StorageNMap<Prefix, Key, Value, QueryKind = OptionQuery, OnEmpty = GetDefault>(
core::marker::PhantomData<(Prefix, Key, Value, QueryKind, OnEmpty)>,
pub struct StorageNMap<
Prefix, Key, Value, QueryKind = OptionQuery, OnEmpty = GetDefault, MaxValues=GetDefault,
>(
core::marker::PhantomData<(Prefix, Key, Value, QueryKind, OnEmpty, MaxValues)>,
);
impl<Prefix, Key, Value, QueryKind, OnEmpty> crate::storage::generator::StorageNMap<Key, Value>
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::generator::StorageNMap<Key, Value>
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::KeyGenerator,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
type Query = QueryKind::Query;
fn module_prefix() -> &'static [u8] {
@@ -78,14 +83,16 @@ where
}
}
impl<Prefix, Key, Value, QueryKind, OnEmpty> crate::storage::StoragePrefixedMap<Value>
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::StoragePrefixedMap<Value>
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::KeyGenerator,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn module_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageNMap<Key, Value>>::module_prefix()
@@ -95,13 +102,15 @@ where
}
}
impl<Prefix, Key, Value, QueryKind, OnEmpty> StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::KeyGenerator,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Get the storage key used to fetch a value corresponding to a specific key.
pub fn hashed_key_for<KArg: EncodeLikeTuple<Key::KArg> + TupleToEncodedIter>(key: KArg) -> Vec<u8> {
@@ -286,13 +295,15 @@ where
}
}
impl<Prefix, Key, Value, QueryKind, OnEmpty> StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::ReversibleKeyGenerator,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Enumerate all elements in the map with prefix key `kp` in no particular order.
///
@@ -355,14 +366,15 @@ pub trait StorageNMapMetadata {
const HASHERS: &'static [frame_metadata::StorageHasher];
}
impl<Prefix, Key, Value, QueryKind, OnEmpty> StorageNMapMetadata
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty>
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues> StorageNMapMetadata
for StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::KeyGenerator,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
const NAME: &'static str = Prefix::STORAGE_PREFIX;
@@ -372,6 +384,32 @@ where
const HASHERS: &'static [frame_metadata::StorageHasher] = Key::HASHER_METADATA;
}
impl<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::StorageInfoTrait for
StorageNMap<Prefix, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Key: super::key::KeyGenerator + super::key::KeyGeneratorMaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
vec![
StorageInfo {
prefix: Self::final_prefix(),
max_values: MaxValues::get(),
max_size: Some(
Key::key_max_encoded_len()
.saturating_add(Value::max_encoded_len())
.saturated_into(),
),
}
]
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -24,9 +24,11 @@ use crate::{
bounded_vec::BoundedVec,
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
},
traits::{GetDefault, StorageInstance, Get},
traits::{GetDefault, StorageInstance, Get, MaxEncodedLen, StorageInfo},
};
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
use sp_arithmetic::traits::SaturatedConversion;
use sp_std::prelude::*;
/// A type that allow to store a value.
///
@@ -212,6 +214,29 @@ impl<Prefix, Value, QueryKind, OnEmpty> StorageValueMetadata
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
}
impl<Prefix, Value, QueryKind, OnEmpty>
crate::traits::StorageInfoTrait for
StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
{
fn storage_info() -> Vec<StorageInfo> {
vec![
StorageInfo {
prefix: Self::hashed_key(),
max_values: Some(1),
max_size: Some(
Value::max_encoded_len()
.saturated_into(),
),
}
]
}
}
#[cfg(test)]
mod test {
use super::*;
+2 -2
View File
@@ -50,7 +50,7 @@ mod misc;
pub use misc::{
Len, Get, GetDefault, HandleLifetime, TryDrop, Time, UnixTime, IsType, IsSubType, ExecuteBlock,
SameOrOther, OnNewAccount, OnKilledAccount, OffchainWorker, GetBacking, Backing, ExtrinsicCall,
EnsureInherentsAreFirst,
EnsureInherentsAreFirst, ConstU32,
};
mod stored_map;
@@ -73,7 +73,7 @@ pub use hooks::GenesisBuild;
pub mod schedule;
mod storage;
pub use storage::{Instance, StorageInstance};
pub use storage::{Instance, StorageInstance, StorageInfo, StorageInfoTrait};
mod dispatch;
pub use dispatch::{EnsureOrigin, OriginTrait, UnfilteredDispatchable};
@@ -53,6 +53,21 @@ impl<T: Default> Get<T> for GetDefault {
}
}
/// Implement `Get<u32>` and `Get<Option<u32>>` using the given const.
pub struct ConstU32<const T: u32>;
impl<const T: u32> Get<u32> for ConstU32<T> {
fn get() -> u32 {
T
}
}
impl<const T: u32> Get<Option<u32>> for ConstU32<T> {
fn get() -> Option<u32> {
Some(T)
}
}
/// A type for which some values make sense to be able to drop without further consideration.
pub trait TryDrop: Sized {
/// Drop an instance cleanly. Only works if its value represents "no-operation".
@@ -17,6 +17,8 @@
//! Traits for encoding data related to pallet's storage items.
use sp_std::prelude::*;
/// An instance of a pallet in the storage.
///
/// It is required that these instances are unique, to support multiple instances per pallet in the same runtime!
@@ -45,3 +47,30 @@ pub trait StorageInstance {
/// Prefix given to a storage to isolate from other storages in the pallet.
const STORAGE_PREFIX: &'static str;
}
/// Some info about an individual storage in a pallet.
#[derive(codec::Encode, codec::Decode, crate::RuntimeDebug, Eq, PartialEq, Clone)]
pub struct StorageInfo {
/// The prefix of the storage. All keys after the prefix are considered part of the storage
pub prefix: [u8; 32],
/// The maximum number of values in the storage, or none if no maximum specified.
pub max_values: Option<u32>,
/// The maximum size of key/values in the storage, or none if no maximum specified.
pub max_size: Option<u32>,
}
/// A trait to give information about storage.
///
/// It can be used to calculate PoV worst case size.
pub trait StorageInfoTrait {
fn storage_info() -> Vec<StorageInfo>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl StorageInfoTrait for Tuple {
fn storage_info() -> Vec<StorageInfo> {
let mut res = vec![];
for_tuples!( #( res.extend_from_slice(&Tuple::storage_info()); )* );
res
}
}