mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 21:37:56 +00:00
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:
committed by
GitHub
parent
49b6dfd2e5
commit
65e5fa3aa3
@@ -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 {
|
||||
|
||||
@@ -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>);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user