mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 01:11:10 +00:00
storage doublemap in decl_storage (#1918)
* factorization * introduce GenericUnhashedStorage * implement generator and storage * impl double map in storage macro * improve StorageDoubleMapXX methods * remove storage from example and impl test * remove old comments * wasm compatible * improve imports * rename storages * update runtime impl version * make code less verbose * impl hash config for second key in double map hash available are all of Hashable trait * use double map in decl_storage for contract * fix double map config issue * add hasher into metadata * update impl version and build wasm * doc * add attrs * update metadata version * update runtime version * fix unused storage
This commit is contained in:
@@ -49,6 +49,8 @@ use proc_macro::TokenStream;
|
||||
/// * storage value: `Foo: type`: implements [StorageValue](https://crates.parity.io/srml_support/storage/trait.StorageValue.html)
|
||||
/// * storage map: `Foo: map type => type`: implements [StorageMap](https://crates.parity.io/srml_support/storage/trait.StorageMap.html)
|
||||
/// * storage linked map: `Foo: linked_map type => type`: implements [StorageMap](https://crates.parity.io/srml_support/storage/trait.StorageMap.html) and [EnumarableStorageMap](https://crates.parity.io/srml_support/storage/trait.EnumerableStorageMap.html)
|
||||
/// * storage double map: Foo: double_map u32, $hash(u32) => u32;` implements `StorageDoubleMap` with hasher $hash one available in `Hashable` trait
|
||||
/// /!\ be careful while choosing the Hash, indeed malicious could craft second keys to lower the trie.
|
||||
///
|
||||
/// And it can be extended as such:
|
||||
///
|
||||
@@ -87,7 +89,7 @@ use proc_macro::TokenStream;
|
||||
/// ```
|
||||
/// This struct can be expose as `Config` by `decl_runtime` macro.
|
||||
///
|
||||
/// ## Module with instances
|
||||
/// ### Module with instances
|
||||
///
|
||||
/// `decl_storage!` macro support building modules with instances with the following syntax: (DefaultInstance type
|
||||
/// is optional)
|
||||
|
||||
@@ -541,4 +541,93 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn double_map(self, k1ty: &syn::Type, k2ty: &syn::Type, k2_hasher: TokenStream2) -> TokenStream2 {
|
||||
let Self {
|
||||
scrate,
|
||||
visibility,
|
||||
traitinstance,
|
||||
traittype,
|
||||
type_infos,
|
||||
fielddefault,
|
||||
prefix,
|
||||
name,
|
||||
attrs,
|
||||
instance_opts,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos;
|
||||
let option_simple_1 = option_unwrap(is_option);
|
||||
|
||||
let as_double_map = quote!{ <Self as #scrate::storage::unhashed::generator::StorageDoubleMap<#k1ty, #k2ty, #typ>> };
|
||||
|
||||
let mutate_impl = if !is_option {
|
||||
quote!{
|
||||
#as_double_map::insert(key1, key2, &val, storage)
|
||||
}
|
||||
} else {
|
||||
quote!{
|
||||
match val {
|
||||
Some(ref val) => #as_double_map::insert(key1, key2, &val, storage),
|
||||
None => #as_double_map::remove(key1, key2, storage),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let final_prefix = if let Some(instance) = instance {
|
||||
let const_name = syn::Ident::new(&format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site());
|
||||
quote!{ #instance::#const_name.as_bytes() }
|
||||
} else {
|
||||
quote!{ #prefix.as_bytes() }
|
||||
};
|
||||
|
||||
// generator for double map
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
#visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>(#scrate::storage::generator::PhantomData<(#traitinstance #comma_instance)>);
|
||||
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::unhashed::generator::StorageDoubleMap<#k1ty, #k2ty, #typ> for #name<#traitinstance, #instance> {
|
||||
type Query = #value_type;
|
||||
|
||||
fn prefix() -> &'static [u8] {
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
fn key_for(k1: &#k1ty, k2: &#k2ty) -> Vec<u8> {
|
||||
let mut key = #as_double_map::prefix_for(k1);
|
||||
key.extend(&#scrate::Hashable::#k2_hasher(k2));
|
||||
key
|
||||
}
|
||||
|
||||
fn get<S: #scrate::GenericUnhashedStorage>(key1: &#k1ty, key2: &#k2ty, storage: &S) -> Self::Query {
|
||||
let key = #as_double_map::key_for(key1, key2);
|
||||
storage.get(&key).#option_simple_1(|| #fielddefault)
|
||||
}
|
||||
|
||||
fn take<S: #scrate::GenericUnhashedStorage>(key1: &#k1ty, key2: &#k2ty, storage: &S) -> Self::Query {
|
||||
let key = #as_double_map::key_for(key1, key2);
|
||||
storage.take(&key).#option_simple_1(|| #fielddefault)
|
||||
}
|
||||
|
||||
fn mutate<R, F: FnOnce(&mut Self::Query) -> R, S: #scrate::GenericUnhashedStorage>(key1: &#k1ty, key2: &#k2ty, f: F, storage: &S) -> R {
|
||||
let mut val = #as_double_map::get(key1, key2, storage);
|
||||
|
||||
let ret = f(&mut val);
|
||||
#mutate_impl ;
|
||||
ret
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ struct DeclStorageBuild {
|
||||
enum DeclStorageType {
|
||||
Map(DeclStorageMap),
|
||||
LinkedMap(DeclStorageLinkedMap),
|
||||
DoubleMap(DeclStorageDoubleMap),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
|
||||
@@ -150,6 +151,24 @@ struct DeclStorageLinkedMap {
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDoubleMap {
|
||||
pub map_keyword: ext::CustomToken<DoubleMapKeyword>,
|
||||
pub key1: syn::Type,
|
||||
pub comma_keyword: Token![,],
|
||||
pub key2_hasher: DeclStorageDoubleMapHasher,
|
||||
pub key2: ext::Parens<syn::Type>,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
enum DeclStorageDoubleMapHasher {
|
||||
Blake2_256(ext::CustomToken<Blake2_256Keyword>),
|
||||
Twox256(ext::CustomToken<Twox256Keyword>),
|
||||
Twox128(ext::CustomToken<Twox128Keyword>),
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDefault {
|
||||
pub equal_token: Token![=],
|
||||
@@ -165,4 +184,8 @@ custom_keyword_impl!(AddExtraGenesis, "add_extra_genesis", "storage extra genesi
|
||||
custom_keyword_impl!(DeclStorageGetter, "get", "storage getter");
|
||||
custom_keyword!(MapKeyword, "map", "map as keyword");
|
||||
custom_keyword!(LinkedMapKeyword, "linked_map", "linked_map as keyword");
|
||||
custom_keyword!(DoubleMapKeyword, "double_map", "double_map as keyword");
|
||||
custom_keyword!(Blake2_256Keyword, "blake2_256", "Blake2_256 as keyword");
|
||||
custom_keyword!(Twox256Keyword, "twox_256", "Twox_256 as keyword");
|
||||
custom_keyword!(Twox128Keyword, "twox_128", "Twox_128 as keyword");
|
||||
custom_keyword_impl!(ExtraGenesisSkipPhantomDataField, "extra_genesis_skip_phantom_data_field", "extra_genesis_skip_phantom_data_field as keyword");
|
||||
|
||||
@@ -255,6 +255,9 @@ fn decl_store_extra_genesis(
|
||||
DeclStorageTypeInfosKind::Map {key_type, .. } => {
|
||||
quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key_type, #storage_type)>, )
|
||||
},
|
||||
DeclStorageTypeInfosKind::DoubleMap {key1_type, key2_type, .. } => {
|
||||
quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key1_type, #key2_type, #storage_type)>, )
|
||||
},
|
||||
});
|
||||
opt_build = Some(build.as_ref().map(|b| &b.expr.content).map(|b|quote!( #b ))
|
||||
.unwrap_or_else(|| quote!( (|config: &GenesisConfig<#traitinstance, #instance>| config.#ident.clone()) )));
|
||||
@@ -295,6 +298,17 @@ fn decl_store_extra_genesis(
|
||||
}
|
||||
}}
|
||||
},
|
||||
DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => {
|
||||
quote!{{
|
||||
use #scrate::rstd::{cell::RefCell, marker::PhantomData};
|
||||
use #scrate::codec::{Encode, Decode};
|
||||
|
||||
let data = (#builder)(&self);
|
||||
for (k1, k2, v) in data.into_iter() {
|
||||
<#name<#traitinstance, #instance> as #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ>>::insert(&k1, &k2, &v, &storage);
|
||||
}
|
||||
}}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -581,6 +595,9 @@ fn decl_storage_items(
|
||||
DeclStorageTypeInfosKind::Map { key_type, is_linked: true } => {
|
||||
i.linked_map(key_type)
|
||||
},
|
||||
DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher } => {
|
||||
i.double_map(key1_type, key2_type, key2_hasher)
|
||||
},
|
||||
};
|
||||
impls.extend(implementation)
|
||||
}
|
||||
@@ -657,6 +674,17 @@ fn impl_store_fns(
|
||||
}
|
||||
}
|
||||
}
|
||||
DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => {
|
||||
quote!{
|
||||
pub fn #get_fn<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #value_type
|
||||
where
|
||||
KArg1: #scrate::storage::generator::Borrow<#key1_type>,
|
||||
KArg2: #scrate::storage::generator::Borrow<#key2_type>,
|
||||
{
|
||||
<#name<#traitinstance> as #scrate::storage::unhashed::generator::StorageDoubleMap<#key1_type, #key2_type, #typ>> :: get(k1.borrow(), k2.borrow(), &#scrate::storage::RuntimeStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
items.extend(item);
|
||||
}
|
||||
@@ -714,6 +742,19 @@ fn store_functions_to_metadata (
|
||||
}
|
||||
}
|
||||
},
|
||||
DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher } => {
|
||||
let k1ty = clean_type_string("e!(#key1_type).to_string());
|
||||
let k2ty = clean_type_string("e!(#key2_type).to_string());
|
||||
let k2_hasher = clean_type_string(&key2_hasher.to_string());
|
||||
quote!{
|
||||
#scrate::storage::generator::StorageFunctionType::DoubleMap {
|
||||
key1: #scrate::storage::generator::DecodeDifferent::Encode(#k1ty),
|
||||
key2: #scrate::storage::generator::DecodeDifferent::Encode(#k2ty),
|
||||
value: #scrate::storage::generator::DecodeDifferent::Encode(#styp),
|
||||
key2_hasher: #scrate::storage::generator::DecodeDifferent::Encode(#k2_hasher),
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
let modifier = if type_infos.is_option {
|
||||
quote!{
|
||||
@@ -810,6 +851,11 @@ enum DeclStorageTypeInfosKind<'a> {
|
||||
key_type: &'a syn::Type,
|
||||
is_linked: bool,
|
||||
},
|
||||
DoubleMap {
|
||||
key1_type: &'a syn::Type,
|
||||
key2_type: &'a syn::Type,
|
||||
key2_hasher: TokenStream2,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DeclStorageTypeInfosKind<'a> {
|
||||
@@ -832,6 +878,11 @@ fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos {
|
||||
key_type: &map.key,
|
||||
is_linked: true,
|
||||
}),
|
||||
DeclStorageType::DoubleMap(ref map) => (&map.value, DeclStorageTypeInfosKind::DoubleMap {
|
||||
key1_type: &map.key1,
|
||||
key2_type: &map.key2.content,
|
||||
key2_hasher: { let h = &map.key2_hasher; quote! { #h } },
|
||||
}),
|
||||
};
|
||||
|
||||
let extracted_type = ext::extract_type_option(value_type);
|
||||
|
||||
Reference in New Issue
Block a user