diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index a7de97ffb9..ae9fc72beb 100644 Binary files a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 1631803002..ad2f2696ca 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 47, - impl_version: 47, + spec_version: 48, + impl_version: 48, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 595d3a7abe..98058fe833 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/example/src/lib.rs b/substrate/srml/example/src/lib.rs index 7305110af6..6ceb6da236 100644 --- a/substrate/srml/example/src/lib.rs +++ b/substrate/srml/example/src/lib.rs @@ -66,7 +66,6 @@ decl_storage! { // A map that has enumerable entries. Bar get(bar) config(): linked_map T::AccountId => T::Balance; - // this one uses the default, we'll demonstrate the usage of 'mutate' API. Foo get(foo) config(): T::Balance; } diff --git a/substrate/srml/metadata/src/lib.rs b/substrate/srml/metadata/src/lib.rs index fc8516f775..9b03daafa6 100644 --- a/substrate/srml/metadata/src/lib.rs +++ b/substrate/srml/metadata/src/lib.rs @@ -263,6 +263,12 @@ pub enum StorageFunctionType { value: DecodeDifferentStr, is_linked: bool, }, + DoubleMap { + key1: DecodeDifferentStr, + key2: DecodeDifferentStr, + value: DecodeDifferentStr, + key2_hasher: DecodeDifferentStr, + }, } /// A storage function modifier. @@ -304,8 +310,10 @@ pub enum RuntimeMetadata { V0(RuntimeMetadataDeprecated), /// Version 1 for runtime metadata. No longer used. V1(RuntimeMetadataDeprecated), - /// Version 2 for runtime metadata. - V2(RuntimeMetadataV2), + /// Version 2 for runtime metadata. No longer used. + V2(RuntimeMetadataDeprecated), + /// Version 3 for runtime metadata. + V3(RuntimeMetadataV3), } /// Enum that should fail. @@ -325,10 +333,10 @@ impl Decode for RuntimeMetadataDeprecated { } } -/// The metadata of a runtime version 2. +/// The metadata of a runtime. #[derive(Eq, Encode, PartialEq)] #[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub struct RuntimeMetadataV2 { +pub struct RuntimeMetadataV3 { pub modules: DecodeDifferentArray, } diff --git a/substrate/srml/support/procedural/src/lib.rs b/substrate/srml/support/procedural/src/lib.rs index f1b3344a8b..342745efde 100644 --- a/substrate/srml/support/procedural/src/lib.rs +++ b/substrate/srml/support/procedural/src/lib.rs @@ -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) diff --git a/substrate/srml/support/procedural/src/storage/impls.rs b/substrate/srml/support/procedural/src/storage/impls.rs index d833ead47d..5a8f7f65d5 100644 --- a/substrate/srml/support/procedural/src/storage/impls.rs +++ b/substrate/srml/support/procedural/src/storage/impls.rs @@ -541,4 +541,93 @@ impl<'a, I: Iterator> 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!{ > }; + + 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 { + let mut key = #as_double_map::prefix_for(k1); + key.extend(&#scrate::Hashable::#k2_hasher(k2)); + key + } + + fn get(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(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, 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 + } + + } + } + + } } diff --git a/substrate/srml/support/procedural/src/storage/mod.rs b/substrate/srml/support/procedural/src/storage/mod.rs index 7bb547c4f7..82290e0de4 100644 --- a/substrate/srml/support/procedural/src/storage/mod.rs +++ b/substrate/srml/support/procedural/src/storage/mod.rs @@ -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, + pub key1: syn::Type, + pub comma_keyword: Token![,], + pub key2_hasher: DeclStorageDoubleMapHasher, + pub key2: ext::Parens, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +enum DeclStorageDoubleMapHasher { + Blake2_256(ext::CustomToken), + Twox256(ext::CustomToken), + Twox128(ext::CustomToken), +} + #[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"); diff --git a/substrate/srml/support/procedural/src/storage/transformation.rs b/substrate/srml/support/procedural/src/storage/transformation.rs index 817e42f508..f00b5e8309 100644 --- a/substrate/srml/support/procedural/src/storage/transformation.rs +++ b/substrate/srml/support/procedural/src/storage/transformation.rs @@ -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(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); diff --git a/substrate/srml/support/src/double_map.rs b/substrate/srml/support/src/double_map.rs index a6dad9eabe..80d974064d 100644 --- a/substrate/srml/support/src/double_map.rs +++ b/substrate/srml/support/src/double_map.rs @@ -31,7 +31,9 @@ use sr_std::borrow::Borrow; /// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. /// The first part is a hash of a concatenation of the `PREFIX` and `Key1`. And the second part /// is a hash of a `Key2`. -pub trait StorageDoubleMap { +/// +/// Hasher are implemented in derive_key* methods. +pub trait StorageDoubleMapWithHasher { type Key1: Codec; type Key2: Codec; type Value: Codec + Default; diff --git a/substrate/srml/support/src/hashable.rs b/substrate/srml/support/src/hashable.rs index 9bb383b2a6..886c88b23a 100644 --- a/substrate/srml/support/src/hashable.rs +++ b/substrate/srml/support/src/hashable.rs @@ -27,12 +27,12 @@ pub trait Hashable: Sized { impl Hashable for T { fn blake2_256(&self) -> [u8; 32] { - blake2_256(&self.encode()) + self.using_encoded(blake2_256) } fn twox_128(&self) -> [u8; 16] { - twox_128(&self.encode()) + self.using_encoded(twox_128) } fn twox_256(&self) -> [u8; 32] { - twox_256(&self.encode()) + self.using_encoded(twox_256) } } diff --git a/substrate/srml/support/src/lib.rs b/substrate/srml/support/src/lib.rs index 7914e7d677..cbbdcc7e4c 100644 --- a/substrate/srml/support/src/lib.rs +++ b/substrate/srml/support/src/lib.rs @@ -36,6 +36,7 @@ pub use paste; pub use sr_primitives as runtime_primitives; pub use self::storage::generator::Storage as GenericStorage; +pub use self::storage::unhashed::generator::UnhashedStorage as GenericUnhashedStorage; #[macro_use] pub mod dispatch; @@ -55,10 +56,10 @@ pub mod inherent; mod double_map; pub mod traits; -pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap, EnumerableStorageMap}; +pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap, EnumerableStorageMap, StorageDoubleMap}; pub use self::hashable::Hashable; pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType}; -pub use self::double_map::StorageDoubleMap; +pub use self::double_map::StorageDoubleMapWithHasher; pub use runtime_io::print; #[doc(inline)] @@ -140,6 +141,12 @@ mod tests { use parity_codec::Codec; use runtime_io::{with_externalities, Blake2Hasher}; use runtime_primitives::BuildStorage; + pub use srml_metadata::{ + DecodeDifferent, StorageMetadata, StorageFunctionMetadata, + StorageFunctionType, StorageFunctionModifier, + DefaultByte, DefaultByteGetter, + }; + pub use rstd::marker::PhantomData; pub trait Trait { type BlockNumber: Codec + Default; @@ -164,6 +171,10 @@ mod tests { pub Data get(data) build(|_| vec![(15u32, 42u64)]): linked_map u32 => u64; pub GenericData get(generic_data): linked_map T::BlockNumber => T::BlockNumber; pub GenericData2 get(generic_data2): linked_map T::BlockNumber => Option; + + pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): double_map u32, blake2_256(u32) => u64; + pub GenericDataDM: double_map T::BlockNumber, twox_128(T::BlockNumber) => T::BlockNumber; + pub GenericData2DM: double_map T::BlockNumber, twox_256(T::BlockNumber) => Option; } } @@ -180,7 +191,7 @@ mod tests { type Map = Data; #[test] - fn basic_insert_remove_should_work() { + fn linked_map_basic_insert_remove_should_work() { with_externalities(&mut new_test_ext(), || { // initialised during genesis assert_eq!(Map::get(&15u32), 42u64); @@ -206,7 +217,7 @@ mod tests { } #[test] - fn enumeration_and_head_should_work() { + fn linked_map_enumeration_and_head_should_work() { with_externalities(&mut new_test_ext(), || { assert_eq!(Map::head(), Some(15)); assert_eq!(Map::enumerate().collect::>(), vec![(15, 42)]); @@ -257,4 +268,128 @@ mod tests { }); } + #[test] + fn double_map_basic_insert_remove_remove_prefix_should_work() { + with_externalities(&mut new_test_ext(), || { + type DoubleMap = DataDM; + // initialised during genesis + assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); + + // get / insert / take + let key1 = 17u32; + let key2 = 18u32; + assert_eq!(DoubleMap::get(key1, key2), 0u64); + DoubleMap::insert(key1, key2, 4u64); + assert_eq!(DoubleMap::get(key1, key2), 4u64); + assert_eq!(DoubleMap::take(key1, key2), 4u64); + assert_eq!(DoubleMap::get(key1, key2), 0u64); + + // mutate + DoubleMap::mutate(key1, key2, |val| { + *val = 15; + }); + assert_eq!(DoubleMap::get(key1, key2), 15u64); + + // remove + DoubleMap::remove(key1, key2); + assert_eq!(DoubleMap::get(key1, key2), 0u64); + + // remove prefix + DoubleMap::insert(key1, key2, 4u64); + DoubleMap::insert(key1, key2+1, 4u64); + DoubleMap::insert(key1+1, key2, 4u64); + DoubleMap::insert(key1+1, key2+1, 4u64); + DoubleMap::remove_prefix(key1); + assert_eq!(DoubleMap::get(key1, key2), 0u64); + assert_eq!(DoubleMap::get(key1, key2+1), 0u64); + assert_eq!(DoubleMap::get(key1+1, key2), 4u64); + assert_eq!(DoubleMap::get(key1+1, key2+1), 4u64); + }); + } + + const EXPECTED_METADATA: StorageMetadata = StorageMetadata { + functions: DecodeDifferent::Encode(&[ + StorageFunctionMetadata { + name: DecodeDifferent::Encode("Data"), + modifier: StorageFunctionModifier::Default, + ty: StorageFunctionType::Map{ + key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("u64"), is_linked: true + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructData(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("GenericData"), + modifier: StorageFunctionModifier::Default, + ty: StorageFunctionType::Map{ + key: DecodeDifferent::Encode("T::BlockNumber"), value: DecodeDifferent::Encode("T::BlockNumber"), is_linked: true + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("GenericData2"), + modifier: StorageFunctionModifier::Optional, + ty: StorageFunctionType::Map{ + key: DecodeDifferent::Encode("T::BlockNumber"), value: DecodeDifferent::Encode("T::BlockNumber"), is_linked: true + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("DataDM"), + modifier: StorageFunctionModifier::Default, + ty: StorageFunctionType::DoubleMap{ + key1: DecodeDifferent::Encode("u32"), + key2: DecodeDifferent::Encode("u32"), + value: DecodeDifferent::Encode("u64"), + key2_hasher: DecodeDifferent::Encode("blake2_256"), + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructDataDM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("GenericDataDM"), + modifier: StorageFunctionModifier::Default, + ty: StorageFunctionType::DoubleMap{ + key1: DecodeDifferent::Encode("T::BlockNumber"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + key2_hasher: DecodeDifferent::Encode("twox_128"), + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("GenericData2DM"), + modifier: StorageFunctionModifier::Optional, + ty: StorageFunctionType::DoubleMap{ + key1: DecodeDifferent::Encode("T::BlockNumber"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("T::BlockNumber"), + key2_hasher: DecodeDifferent::Encode("twox_256"), + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + ]) + }; + + #[test] + fn store_metadata() { + let metadata = Module::::store_metadata(); + assert_eq!(EXPECTED_METADATA, metadata); + } } diff --git a/substrate/srml/support/src/metadata.rs b/substrate/srml/support/src/metadata.rs index 7f10b1136e..f7594d27b7 100644 --- a/substrate/srml/support/src/metadata.rs +++ b/substrate/srml/support/src/metadata.rs @@ -16,7 +16,7 @@ pub use srml_metadata::{ DecodeDifferent, FnEncode, RuntimeMetadata, - ModuleMetadata, RuntimeMetadataV2, + ModuleMetadata, RuntimeMetadataV3, DefaultByteGetter, RuntimeMetadataPrefixed, }; @@ -36,8 +36,8 @@ macro_rules! impl_runtime_metadata { ) => { impl $runtime { pub fn metadata() -> $crate::metadata::RuntimeMetadataPrefixed { - $crate::metadata::RuntimeMetadata::V2 ( - $crate::metadata::RuntimeMetadataV2 { + $crate::metadata::RuntimeMetadata::V3 ( + $crate::metadata::RuntimeMetadataV3 { modules: $crate::__runtime_modules_to_metadata!($runtime;; $( $rest )*), } ).into() @@ -377,8 +377,8 @@ mod tests { event_module2::Module with Event Storage Call, ); - const EXPECTED_METADATA: RuntimeMetadata = RuntimeMetadata::V2( - RuntimeMetadataV2 { + const EXPECTED_METADATA: RuntimeMetadata = RuntimeMetadata::V3( + RuntimeMetadataV3 { modules: DecodeDifferent::Encode(&[ ModuleMetadata { name: DecodeDifferent::Encode("system"), diff --git a/substrate/srml/support/src/storage/generator.rs b/substrate/srml/support/src/storage/generator.rs index a0586963b7..18bc769d82 100644 --- a/substrate/srml/support/src/storage/generator.rs +++ b/substrate/srml/support/src/storage/generator.rs @@ -48,6 +48,8 @@ use crate::codec; use crate::rstd::vec::Vec; +#[cfg(feature = "std")] +use crate::storage::unhashed::generator::UnhashedStorage; #[doc(hidden)] pub use crate::rstd::borrow::Borrow; #[doc(hidden)] @@ -101,20 +103,19 @@ pub trait Storage { #[cfg(feature = "std")] impl Storage for (crate::rstd::cell::RefCell<&mut sr_primitives::StorageOverlay>, PhantomData) { fn exists(&self, key: &[u8]) -> bool { - self.0.borrow().contains_key(S::hash(key).as_ref()) + UnhashedStorage::exists(self, &S::hash(key)) } fn get(&self, key: &[u8]) -> Option { - self.0.borrow().get(S::hash(key).as_ref()) - .map(|x| codec::Decode::decode(&mut x.as_slice()).expect("Unable to decode expected type.")) + UnhashedStorage::get(self, &S::hash(key)) } fn put(&self, key: &[u8], val: &T) { - self.0.borrow_mut().insert(S::hash(key).to_vec(), codec::Encode::encode(val)); + UnhashedStorage::put(self, &S::hash(key), val) } fn kill(&self, key: &[u8]) { - self.0.borrow_mut().remove(S::hash(key).as_ref()); + UnhashedStorage::kill(self, &S::hash(key)) } } diff --git a/substrate/srml/support/src/storage/mod.rs b/substrate/srml/support/src/storage/mod.rs index ec758092f7..b1b8766b90 100644 --- a/substrate/srml/support/src/storage/mod.rs +++ b/substrate/srml/support/src/storage/mod.rs @@ -23,6 +23,7 @@ use crate::codec::{Codec, Encode, Decode, KeyedVec, Input}; #[macro_use] pub mod generator; +pub mod unhashed; struct IncrementalInput<'a> { key: &'a [u8], @@ -56,84 +57,73 @@ impl<'a> Input for IncrementalChildInput<'a> { /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - let key = twox_128(key); - runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| { - let mut input = IncrementalInput { - key: &key[..], - pos: 0, - }; - Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") - }) + unhashed::get(&twox_128(key)) } /// Return the value of the item in storage under `key`, or the type's default if there is no /// explicit entry. pub fn get_or_default(key: &[u8]) -> T { - get(key).unwrap_or_else(Default::default) + unhashed::get_or_default(&twox_128(key)) } /// Return the value of the item in storage under `key`, or `default_value` if there is no /// explicit entry. pub fn get_or(key: &[u8], default_value: T) -> T { - get(key).unwrap_or(default_value) + unhashed::get_or(&twox_128(key), default_value) } /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. pub fn get_or_else T>(key: &[u8], default_value: F) -> T { - get(key).unwrap_or_else(default_value) + unhashed::get_or_else(&twox_128(key), default_value) } /// Put `value` in storage under `key`. pub fn put(key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice)); + unhashed::put(&twox_128(key), value) } /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. pub fn take(key: &[u8]) -> Option { - let r = get(key); - if r.is_some() { - kill(key); - } - r + unhashed::take(&twox_128(key)) } /// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, /// the default for its type. pub fn take_or_default(key: &[u8]) -> T { - take(key).unwrap_or_else(Default::default) + unhashed::take_or_default(&twox_128(key)) } /// Return the value of the item in storage under `key`, or `default_value` if there is no /// explicit entry. Ensure there is no explicit entry on return. pub fn take_or(key: &[u8], default_value: T) -> T { - take(key).unwrap_or(default_value) + unhashed::take_or(&twox_128(key), default_value) } /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. Ensure there is no explicit entry on return. pub fn take_or_else T>(key: &[u8], default_value: F) -> T { - take(key).unwrap_or_else(default_value) + unhashed::take_or_else(&twox_128(key), default_value) } /// Check to see if `key` has an explicit entry in storage. pub fn exists(key: &[u8]) -> bool { - runtime_io::exists_storage(&twox_128(key)[..]) + unhashed::exists(&twox_128(key)) } /// Ensure `key` has no explicit entry in storage. pub fn kill(key: &[u8]) { - runtime_io::clear_storage(&twox_128(key)[..]); + unhashed::kill(&twox_128(key)) } /// Get a Vec of bytes from storage. pub fn get_raw(key: &[u8]) -> Option> { - runtime_io::storage(&twox_128(key)[..]) + unhashed::get_raw(&twox_128(key)) } /// Put a raw byte slice into storage. pub fn put_raw(key: &[u8], value: &[u8]) { - runtime_io::set_storage(&twox_128(key)[..], value) + unhashed::put_raw(&twox_128(key), value) } /// The underlying runtime storage. @@ -141,27 +131,58 @@ pub struct RuntimeStorage; impl crate::GenericStorage for RuntimeStorage { fn exists(&self, key: &[u8]) -> bool { - super::storage::exists(key) + exists(key) } /// Load the bytes of a key from storage. Can panic if the type is incorrect. fn get(&self, key: &[u8]) -> Option { - super::storage::get(key) + get(key) } /// Put a value in under a key. fn put(&self, key: &[u8], val: &T) { - super::storage::put(key, val) + put(key, val) } /// Remove the bytes of a key from storage. fn kill(&self, key: &[u8]) { - super::storage::kill(key) + kill(key) } /// Take a value from storage, deleting it after reading. fn take(&self, key: &[u8]) -> Option { - super::storage::take(key) + take(key) + } +} + +impl crate::GenericUnhashedStorage for RuntimeStorage { + fn exists(&self, key: &[u8]) -> bool { + unhashed::exists(key) + } + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. + fn get(&self, key: &[u8]) -> Option { + unhashed::get(key) + } + + /// Put a value in under a key. + fn put(&self, key: &[u8], val: &T) { + unhashed::put(key, val) + } + + /// Remove the bytes of a key from storage. + fn kill(&self, key: &[u8]) { + unhashed::kill(key) + } + + /// Remove the bytes of a key from storage. + fn kill_prefix(&self, prefix: &[u8]) { + unhashed::kill_prefix(prefix) + } + + /// Take a value from storage, deleting it after reading. + fn take(&self, key: &[u8]) -> Option { + unhashed::take(key) } } @@ -374,6 +395,109 @@ impl EnumerableStorageMap for U where U: generator: } } +/// An implementation of a map with a two keys. +/// +/// It provides an important ability to efficiently remove all entries +/// that have a common first key. +/// +/// # Mapping of keys to a storage path +/// +/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. +/// The first part is a hash of a concatenation of the `PREFIX` and `Key1`. And the second part +/// is a hash of a `Key2`. +/// +/// /!\ be careful while choosing the Hash, indeed malicious could craft second keys to lower the trie. +pub trait StorageDoubleMap { + /// The type that get/take returns. + type Query; + + /// Get the prefix key in storage. + fn prefix() -> &'static [u8]; + + /// Get the storage key used to fetch a value corresponding to a specific key. + fn key_for, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Vec; + + /// Get the storage prefix used to fetch keys corresponding to a specific key1. + fn prefix_for>(k1: KArg1) -> Vec; + + /// true if the value is defined in storage. + fn exists, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> bool; + + /// Load the value associated with the given key from the map. + fn get, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query; + + /// Take the value under a key. + fn take, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query; + + /// Store a value to be associated with the given key from the map. + fn insert, KArg2: Borrow, VArg: Borrow>(k1: KArg1, k2: KArg2, val: VArg); + + /// Remove the value under a key. + fn remove, KArg2: Borrow>(k1: KArg1, k2: KArg2); + + /// Removes all entries that shares the `k1` as the first key. + fn remove_prefix>(k1: KArg1); + + /// Mutate the value under a key. + fn mutate(k1: KArg1, k2: KArg2, f: F) -> R + where + KArg1: Borrow, + KArg2: Borrow, + F: FnOnce(&mut Self::Query) -> R; +} + +impl StorageDoubleMap for U +where + U: unhashed::generator::StorageDoubleMap +{ + type Query = U::Query; + + fn prefix() -> &'static [u8] { + >::prefix() + } + + fn key_for, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Vec { + >::key_for(k1.borrow(), k2.borrow()) + } + + fn prefix_for>(k1: KArg1) -> Vec { + >::prefix_for(k1.borrow()) + } + + fn exists, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> bool { + U::exists(k1.borrow(), k2.borrow(), &RuntimeStorage) + } + + fn get, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query { + U::get(k1.borrow(), k2.borrow(), &RuntimeStorage) + } + + fn take, KArg2: Borrow>(k1: KArg1, k2: KArg2) -> Self::Query { + U::take(k1.borrow(), k2.borrow(), &RuntimeStorage) + } + + fn insert, KArg2: Borrow, VArg: Borrow>(k1: KArg1, k2: KArg2, val: VArg) { + U::insert(k1.borrow(), k2.borrow(), val.borrow(), &RuntimeStorage) + } + + fn remove, KArg2: Borrow>(k1: KArg1, k2: KArg2) { + U::remove(k1.borrow(), k2.borrow(), &RuntimeStorage) + } + + fn remove_prefix>(k1: KArg1) { + U::remove_prefix(k1.borrow(), &RuntimeStorage) + } + + fn mutate(k1: KArg1, k2: KArg2, f: F) -> R + where + KArg1: Borrow, + KArg2: Borrow, + F: FnOnce(&mut Self::Query) -> R + { + U::mutate(k1.borrow(), k2.borrow(), f, &RuntimeStorage) + } +} + /// A trait to conveniently store a vector of storable data. pub trait StorageVec { type Item: Default + Sized + Codec; @@ -433,149 +557,6 @@ pub trait StorageVec { } } -pub mod unhashed { - use crate::rstd::borrow::Borrow; - use super::{runtime_io, Codec, Decode, KeyedVec, Vec, IncrementalInput}; - - /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. - pub fn get(key: &[u8]) -> Option { - runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { - let mut input = IncrementalInput { - key, - pos: 0, - }; - Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") - }) - } - - /// Return the value of the item in storage under `key`, or the type's default if there is no - /// explicit entry. - pub fn get_or_default(key: &[u8]) -> T { - get(key).unwrap_or_else(Default::default) - } - - /// Return the value of the item in storage under `key`, or `default_value` if there is no - /// explicit entry. - pub fn get_or(key: &[u8], default_value: T) -> T { - get(key).unwrap_or(default_value) - } - - /// Return the value of the item in storage under `key`, or `default_value()` if there is no - /// explicit entry. - pub fn get_or_else T>(key: &[u8], default_value: F) -> T { - get(key).unwrap_or_else(default_value) - } - - /// Put `value` in storage under `key`. - pub fn put(key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_storage(key, slice)); - } - - /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. - pub fn take(key: &[u8]) -> Option { - let r = get(key); - if r.is_some() { - kill(key); - } - r - } - - /// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, - /// the default for its type. - pub fn take_or_default(key: &[u8]) -> T { - take(key).unwrap_or_else(Default::default) - } - - /// Return the value of the item in storage under `key`, or `default_value` if there is no - /// explicit entry. Ensure there is no explicit entry on return. - pub fn take_or(key: &[u8], default_value: T) -> T { - take(key).unwrap_or(default_value) - } - - /// Return the value of the item in storage under `key`, or `default_value()` if there is no - /// explicit entry. Ensure there is no explicit entry on return. - pub fn take_or_else T>(key: &[u8], default_value: F) -> T { - take(key).unwrap_or_else(default_value) - } - - /// Check to see if `key` has an explicit entry in storage. - pub fn exists(key: &[u8]) -> bool { - runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() - } - - /// Ensure `key` has no explicit entry in storage. - pub fn kill(key: &[u8]) { - runtime_io::clear_storage(key); - } - - /// Ensure keys with the given `prefix` have no entries in storage. - pub fn kill_prefix(prefix: &[u8]) { - runtime_io::clear_prefix(prefix); - } - - /// Get a Vec of bytes from storage. - pub fn get_raw(key: &[u8]) -> Option> { - runtime_io::storage(key) - } - - /// Put a raw byte slice into storage. - pub fn put_raw(key: &[u8], value: &[u8]) { - runtime_io::set_storage(key, value) - } - - /// A trait to conveniently store a vector of storable data. - pub trait StorageVec { - type Item: Default + Sized + Codec; - const PREFIX: &'static [u8]; - - /// Get the current set of items. - fn items() -> Vec { - (0..Self::count()).into_iter().map(Self::item).collect() - } - - /// Set the current set of items. - fn set_items(items: I) - where - I: IntoIterator, - T: Borrow, - { - let mut count: u32 = 0; - - for i in items.into_iter() { - put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); - count = count.checked_add(1).expect("exceeded runtime storage capacity"); - } - - Self::set_count(count); - } - - fn set_item(index: u32, item: &Self::Item) { - if index < Self::count() { - put(&index.to_keyed_vec(Self::PREFIX), item); - } - } - - fn clear_item(index: u32) { - if index < Self::count() { - kill(&index.to_keyed_vec(Self::PREFIX)); - } - } - - fn item(index: u32) -> Self::Item { - get_or_default(&index.to_keyed_vec(Self::PREFIX)) - } - - fn set_count(count: u32) { - (count..Self::count()).for_each(Self::clear_item); - put(&b"len".to_keyed_vec(Self::PREFIX), &count); - } - - fn count() -> u32 { - get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) - } - } -} - /// child storage NOTE could replace unhashed by having only one kind of storage (root being null storage /// key (storage_key can become Option<&[u8]>). /// This module is a currently only a variant of unhashed with additional `storage_key`. diff --git a/substrate/srml/support/src/storage/unhashed/generator.rs b/substrate/srml/support/src/storage/unhashed/generator.rs new file mode 100644 index 0000000000..2b046013bb --- /dev/null +++ b/substrate/srml/support/src/storage/unhashed/generator.rs @@ -0,0 +1,144 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::codec; +use runtime_io::twox_128; +use crate::rstd::vec::Vec; + +/// Abstraction around storage with unhashed access. +pub trait UnhashedStorage { + /// true if the key exists in storage. + fn exists(&self, key: &[u8]) -> bool; + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. + fn get(&self, key: &[u8]) -> Option; + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. Will panic if + /// it's not there. + fn require(&self, key: &[u8]) -> T { self.get(key).expect("Required values must be in storage") } + + /// Load the bytes of a key from storage. Can panic if the type is incorrect. The type's + /// default is returned if it's not there. + fn get_or_default(&self, key: &[u8]) -> T { self.get(key).unwrap_or_default() } + + /// Put a value in under a key. + fn put(&self, key: &[u8], val: &T); + + /// Remove the bytes of a key from storage. + fn kill(&self, key: &[u8]); + + /// Remove the bytes of a key from storage. + fn kill_prefix(&self, prefix: &[u8]); + + /// Take a value from storage, deleting it after reading. + fn take(&self, key: &[u8]) -> Option { + let value = self.get(key); + self.kill(key); + value + } + + /// Take a value from storage, deleting it after reading. + fn take_or_panic(&self, key: &[u8]) -> T { self.take(key).expect("Required values must be in storage") } + + /// Take a value from storage, deleting it after reading. + fn take_or_default(&self, key: &[u8]) -> T { self.take(key).unwrap_or_default() } +} + +// We use a construct like this during when genesis storage is being built. +#[cfg(feature = "std")] +impl UnhashedStorage for (crate::rstd::cell::RefCell<&mut sr_primitives::StorageOverlay>, H) { + fn exists(&self, key: &[u8]) -> bool { + self.0.borrow().contains_key(key) + } + + fn get(&self, key: &[u8]) -> Option { + self.0.borrow().get(key) + .map(|x| codec::Decode::decode(&mut x.as_slice()).expect("Unable to decode expected type.")) + } + + fn put(&self, key: &[u8], val: &T) { + self.0.borrow_mut().insert(key.to_vec(), codec::Encode::encode(val)); + } + + fn kill(&self, key: &[u8]) { + self.0.borrow_mut().remove(key); + } + + fn kill_prefix(&self, prefix: &[u8]) { + self.0.borrow_mut().retain(|key, _| { + !key.starts_with(prefix) + }) + } +} + +/// An implementation of a map with a two keys. +/// +/// It provides an important ability to efficiently remove all entries +/// that have a common first key. +/// +/// # Mapping of keys to a storage path +/// +/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts. +/// The first part is a hash of a concatenation of the `PREFIX` and `Key1`. And the second part +/// is a hash of a `Key2`. +/// +/// /!\ be careful while choosing the Hash, indeed malicious could craft second keys to lower the trie. +pub trait StorageDoubleMap { + /// The type that get/take returns. + type Query; + + /// Get the prefix key in storage. + fn prefix() -> &'static [u8]; + + /// Get the storage key used to fetch a value corresponding to a specific key. + fn key_for(k1: &K1, k2: &K2) -> Vec; + + /// Get the storage prefix used to fetch keys corresponding to a specific key1. + fn prefix_for(k1: &K1) -> Vec { + let mut key = Self::prefix().to_vec(); + codec::Encode::encode_to(k1, &mut key); + twox_128(&key).to_vec() + } + + /// true if the value is defined in storage. + fn exists(k1: &K1, k2: &K2, storage: &S) -> bool { + storage.exists(&Self::key_for(k1, k2)) + } + + /// Load the value associated with the given key from the map. + fn get(k1: &K1, k2: &K2, storage: &S) -> Self::Query; + + /// Take the value under a key. + fn take(k1: &K1, k2: &K2, storage: &S) -> Self::Query; + + /// Store a value to be associated with the given key from the map. + fn insert(k1: &K1, k2: &K2, val: &V, storage: &S) { + storage.put(&Self::key_for(k1, k2), val); + } + + /// Remove the value under a key. + fn remove(k1: &K1, k2: &K2, storage: &S) { + storage.kill(&Self::key_for(k1, k2)); + } + + /// Removes all entries that shares the `k1` as the first key. + fn remove_prefix(k1: &K1, storage: &S) { + storage.kill_prefix(&Self::prefix_for(k1)); + } + + /// Mutate the value under a key. + fn mutate R, S: UnhashedStorage>(k1: &K1, k2: &K2, f: F, storage: &S) -> R; +} diff --git a/substrate/srml/support/src/storage/unhashed/mod.rs b/substrate/srml/support/src/storage/unhashed/mod.rs new file mode 100644 index 0000000000..225c6756b8 --- /dev/null +++ b/substrate/srml/support/src/storage/unhashed/mod.rs @@ -0,0 +1,160 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Operation on unhashed runtime storage + +use crate::rstd::borrow::Borrow; +use super::{runtime_io, Codec, Encode, Decode, KeyedVec, Vec, IncrementalInput}; + +pub mod generator; + +/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. +pub fn get(key: &[u8]) -> Option { + runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key, + pos: 0, + }; + Decode::decode(&mut input).expect("storage is not null, therefore must be a valid type") + }) +} + +/// Return the value of the item in storage under `key`, or the type's default if there is no +/// explicit entry. +pub fn get_or_default(key: &[u8]) -> T { + get(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. +pub fn get_or(key: &[u8], default_value: T) -> T { + get(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. +pub fn get_or_else T>(key: &[u8], default_value: F) -> T { + get(key).unwrap_or_else(default_value) +} + +/// Put `value` in storage under `key`. +pub fn put(key: &[u8], value: &T) { + value.using_encoded(|slice| runtime_io::set_storage(key, slice)); +} + +/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. +pub fn take(key: &[u8]) -> Option { + let r = get(key); + if r.is_some() { + kill(key); + } + r +} + +/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, +/// the default for its type. +pub fn take_or_default(key: &[u8]) -> T { + take(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or(key: &[u8], default_value: T) -> T { + take(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or_else T>(key: &[u8], default_value: F) -> T { + take(key).unwrap_or_else(default_value) +} + +/// Check to see if `key` has an explicit entry in storage. +pub fn exists(key: &[u8]) -> bool { + runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() +} + +/// Ensure `key` has no explicit entry in storage. +pub fn kill(key: &[u8]) { + runtime_io::clear_storage(key); +} + +/// Ensure keys with the given `prefix` have no entries in storage. +pub fn kill_prefix(prefix: &[u8]) { + runtime_io::clear_prefix(prefix); +} + +/// Get a Vec of bytes from storage. +pub fn get_raw(key: &[u8]) -> Option> { + runtime_io::storage(key) +} + +/// Put a raw byte slice into storage. +pub fn put_raw(key: &[u8], value: &[u8]) { + runtime_io::set_storage(key, value) +} + +/// A trait to conveniently store a vector of storable data. +pub trait StorageVec { + type Item: Default + Sized + Codec; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: I) + where + I: IntoIterator, + T: Borrow, + { + let mut count: u32 = 0; + + for i in items.into_iter() { + put(&count.to_keyed_vec(Self::PREFIX), i.borrow()); + count = count.checked_add(1).expect("exceeded runtime storage capacity"); + } + + Self::set_count(count); + } + + fn set_item(index: u32, item: &Self::Item) { + if index < Self::count() { + put(&index.to_keyed_vec(Self::PREFIX), item); + } + } + + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + + fn item(index: u32) -> Self::Item { + get_or_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(Self::clear_item); + put(&b"len".to_keyed_vec(Self::PREFIX), &count); + } + + fn count() -> u32 { + get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) + } +} diff --git a/substrate/srml/support/test/tests/instance.rs b/substrate/srml/support/test/tests/instance.rs index 6f4effbc1a..28ad3ee006 100644 --- a/substrate/srml/support/test/tests/instance.rs +++ b/substrate/srml/support/test/tests/instance.rs @@ -28,7 +28,7 @@ use srml_support::Parameter; use inherents::{ ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError }; -use srml_support::{StorageValue, StorageMap}; +use srml_support::{StorageValue, StorageMap, StorageDoubleMap}; use primitives::{H256, sr25519}; pub trait Currency { @@ -218,6 +218,7 @@ mod module2 { pub Value config(value): T::Amount; pub Map config(map): map u64 => u64; pub LinkedMap config(linked_map): linked_map u64 => u64; + pub DoubleMap config(double_map): double_map u64, blake2_256(u64) => u64; } extra_genesis_skip_phantom_data_field; } @@ -368,10 +369,16 @@ fn new_test_ext() -> runtime_io::TestExternalities { }), module2: Some(module2::GenesisConfig { value: 4, - map: vec![], - linked_map: vec![], + map: vec![(0, 0)], + linked_map: vec![(0, 0)], + double_map: vec![(0, 0, 0)], + }), + module2_Instance1: Some(module2::GenesisConfig { + value: 4, + map: vec![(0, 0)], + linked_map: vec![(0, 0)], + double_map: vec![(0, 0, 0)], }), - module2_Instance1: None, module2_Instance2: None, module2_Instance3: None, }.build_storage().unwrap().0.into() @@ -381,7 +388,7 @@ fn new_test_ext() -> runtime_io::TestExternalities { fn storage_instance_independance() { with_externalities(&mut new_test_ext(), || { let mut map = rstd::collections::btree_map::BTreeMap::new(); - for key in &[ + for key in [ module2::Value::::key().to_vec(), module2::Value::::key().to_vec(), module2::Value::::key().to_vec(), @@ -394,6 +401,10 @@ fn storage_instance_independance() { module2::LinkedMap::::prefix().to_vec(), module2::LinkedMap::::prefix().to_vec(), module2::LinkedMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), + module2::DoubleMap::::prefix().to_vec(), module2::Map::::key_for(0), module2::Map::::key_for(0).to_vec(), module2::Map::::key_for(0).to_vec(), @@ -410,20 +421,32 @@ fn storage_instance_independance() { module2::LinkedMap::::key_for(1).to_vec(), module2::LinkedMap::::key_for(1).to_vec(), module2::LinkedMap::::key_for(1).to_vec(), - ] { + module2::DoubleMap::::prefix_for(1), + module2::DoubleMap::::prefix_for(1).to_vec(), + module2::DoubleMap::::prefix_for(1).to_vec(), + module2::DoubleMap::::prefix_for(1).to_vec(), + module2::DoubleMap::::key_for(1, 1), + module2::DoubleMap::::key_for(1, 1).to_vec(), + module2::DoubleMap::::key_for(1, 1).to_vec(), + module2::DoubleMap::::key_for(1, 1).to_vec(), + ].iter() { assert!(map.insert(key, ()).is_none()) } }); } +// TODO TODO: check configuration doublemapstorage in instances + #[test] fn storage_with_instance_basic_operation() { with_externalities(&mut new_test_ext(), || { type Value = module2::Value; type Map = module2::Map; type LinkedMap = module2::LinkedMap; + type DoubleMap = module2::DoubleMap; - assert_eq!(Value::exists(), false); + assert_eq!(Value::exists(), true); + assert_eq!(Value::get(), 4); Value::put(1); assert_eq!(Value::get(), 1); assert_eq!(Value::take(), 1); @@ -431,10 +454,12 @@ fn storage_with_instance_basic_operation() { Value::mutate(|a| *a=2); assert_eq!(Value::get(), 2); Value::kill(); + assert_eq!(Value::exists(), false); assert_eq!(Value::get(), 0); let key = 1; - assert_eq!(Map::exists(1), false); + assert_eq!(Map::exists(0), true); + assert_eq!(Map::exists(key), false); Map::insert(key, 1); assert_eq!(Map::get(key), 1); assert_eq!(Map::take(key), 1); @@ -442,9 +467,11 @@ fn storage_with_instance_basic_operation() { Map::mutate(key, |a| *a=2); assert_eq!(Map::get(key), 2); Map::remove(key); + assert_eq!(Map::exists(key), false); assert_eq!(Map::get(key), 0); - assert_eq!(LinkedMap::exists(1), false); + assert_eq!(LinkedMap::exists(0), true); + assert_eq!(LinkedMap::exists(key), false); LinkedMap::insert(key, 1); assert_eq!(LinkedMap::get(key), 1); assert_eq!(LinkedMap::take(key), 1); @@ -452,6 +479,20 @@ fn storage_with_instance_basic_operation() { LinkedMap::mutate(key, |a| *a=2); assert_eq!(LinkedMap::get(key), 2); LinkedMap::remove(key); + assert_eq!(LinkedMap::exists(key), false); assert_eq!(LinkedMap::get(key), 0); + + let key1 = 1; + let key2 = 1; + assert_eq!(DoubleMap::exists(0, 0), true); + assert_eq!(DoubleMap::exists(key1, key2), false); + DoubleMap::insert(key1, key2, 1); + assert_eq!(DoubleMap::get(key1, key2), 1); + assert_eq!(DoubleMap::take(key1, key2), 1); + assert_eq!(DoubleMap::get(key1, key2), 0); + DoubleMap::mutate(key1, key2, |a| *a=2); + assert_eq!(DoubleMap::get(key1, key2), 2); + DoubleMap::remove(key1, key2); + assert_eq!(DoubleMap::get(key1, key2), 0); }); }