mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 19:51:05 +00:00
Refactor away from opaque hashes (#5226)
* System.BlockHash * Fix hash * Introduce K/V iteration in all _concat maps Also move across: - System.Account (blake2_128_concat) - Balances.Locks (twox_64_concat) - ElectionsPhragmen.VotesOf (twox_64_concat) - ElectionsPhragmen.StakeOf (twox_64_concat) - Identity.IdentityOf (twox_64_concat) - Identity.SubsOf (twox_64_concat) - Society.Payouts (twox_64_concat) - Session.NextKeys (twox_64_concat) - Identity.SuperOf (blake2_128_concat) - Session.KeyOwner (blake2_128_concat) - Society.SuspendedCandidates (twox_64_concat) - Society.SuspendedMembers (twox_64_concat) - Society.Vouching (twox_64_concat) - Society.Strikes (twox_64_concat) - System.EventTopics - Balances.Account * Build fixes * Ensure migration happens in correct order * Staking.* * Vesting.* Offences.* * Democracy.* * Babe.* Collective.* * Grandpa.* * Assets.* Benchmark.* Contracts.* Elections.* Asset.* Nicks.* Also introduce real account list * ImOnline.* * Treasury.* * Recovery.* * Final bits. * Docs * Fix one test * Fix test * All passing except the UI tests * Remove linked_map part 1 * Remove linked_map * Some iterator utils for double maps. * Remove old migrations * Introduce tombstone for LinkedMap type * Migration for genesis hash * Fix build * Fix hash * Rename Map is_linked -> unused, keeping backwards compat (#5256) * Update frame/balances/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/elections/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Remove old migration code. * Update frame/system/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update bin/node/runtime/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix hash * fix session migration * Fix watning Co-authored-by: Jaco Greeff <jacogr@gmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Robert Habermeier <rphmeier@gmail.com>
This commit is contained in:
@@ -14,7 +14,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false, features = ["derive"] }
|
||||
frame-metadata = { version = "11.0.0-alpha.2", default-features = false, path = "../metadata" }
|
||||
sp-std = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/std" }
|
||||
sp-io ={ path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
||||
sp-io = { path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
||||
sp-runtime = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-core = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/core" }
|
||||
sp-arithmetic = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/arithmetic" }
|
||||
|
||||
@@ -33,8 +33,8 @@ use proc_macro::TokenStream;
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
/// Foo get(fn foo) config(): u32=12;
|
||||
/// Bar: map hasher(blake2_256) u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): linked_map hasher(blake2_256) u32 => u32;
|
||||
/// Bar: map hasher(identity) u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): map hasher(identity) u32 => u32;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
@@ -70,10 +70,28 @@ use proc_macro::TokenStream;
|
||||
/// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. You will generally want to use one
|
||||
/// of three hashers:
|
||||
/// * `blake2_128_concat`: The default, safe choice. Use if you are unsure or don't care. It is
|
||||
/// secure against user-tainted keys, fairly fast and memory-efficient and supports
|
||||
/// iteration over its keys and values. This must be used if the keys of your map can be
|
||||
/// selected *en masse* by untrusted users.
|
||||
/// * `twox_64_concat`: This is an insecure hasher and can only be used safely if you know that
|
||||
/// the preimages cannot be chosen at will by untrusted users. It is memory-efficient, extremely
|
||||
/// performant and supports iteration over its keys and values. You can safely use this is the
|
||||
/// key is:
|
||||
/// - A (slowly) incrementing index.
|
||||
/// - Known to be the result of a cryptographic hash (though `identity` is a better choice here).
|
||||
/// - Known to be the public key of a cryptographic key pair in existence.
|
||||
/// * `identity`: This is not a hasher at all, and just uses the key material directly. Since it
|
||||
/// does no hashing or appending, it's the fastest possible hasher, however, it's also the least
|
||||
/// secure. It can be used only if you know that the key will be cryptographically/securely
|
||||
/// randomly distributed over the binary encoding space. In most cases this will not be true.
|
||||
/// One case where it is true, however, if where the key is itself the result of a cryptographic
|
||||
/// hash of some existent data.
|
||||
///
|
||||
/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
/// Other hashers will tend to be "opaque" and not support iteration over the keys in the
|
||||
/// map. It is not recommended to use these.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
@@ -85,36 +103,6 @@ use proc_macro::TokenStream;
|
||||
/// twox128(module_prefix) ++ twox128(storage_prefix) ++ hasher(encode(key))
|
||||
/// ```
|
||||
///
|
||||
/// * Linked map: `Foo: linked_map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageLinkedMap`](../frame_support/storage/trait.StorageLinkedMap.html) trait using the
|
||||
/// [`StorageLinkedMap generator`](../frame_support/storage/generator/trait.StorageLinkedMap.html).
|
||||
/// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// All key formatting logic can be accessed in a type-agnostic format via the
|
||||
/// `KeyFormat` trait, which
|
||||
/// is implemented for the storage linked map type as well.
|
||||
///
|
||||
/// The generator key format is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
/// * `storage_prefix`: storage_name
|
||||
/// * `head_prefix`: `"HeadOf" ++ storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// Thus the keys are stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
|
||||
/// ```
|
||||
/// and head is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(head_prefix)
|
||||
/// ```
|
||||
///
|
||||
/// * Double map: `Foo: double_map hasher($hash1) u32, hasher($hash2) u32 => u32`: Implements the
|
||||
/// [`StorageDoubleMap`](../frame_support/storage/trait.StorageDoubleMap.html) trait using the
|
||||
/// [`StorageDoubleMap generator`](../frame_support/storage/generator/trait.StorageDoubleMap.html).
|
||||
@@ -124,14 +112,6 @@ use proc_macro::TokenStream;
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be chosen with care, see
|
||||
/// generator documentation.
|
||||
///
|
||||
/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` or
|
||||
/// `blake2_128_concat` must be used.
|
||||
/// Otherwise, other values of all storage items can be compromised.
|
||||
///
|
||||
/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` or
|
||||
/// `blake2_128_concat` must be used.
|
||||
/// Otherwise, other items in storage with the same first key can be compromised.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
/// * `storage_prefix`: storage_name
|
||||
@@ -145,10 +125,16 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// Supported hashers (ordered from least to best security):
|
||||
///
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated.
|
||||
/// * `identity` - Just the unrefined key material. Use only when it is known to be a secure hash
|
||||
/// already. The most efficient and iterable over keys.
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated. Use only when an untrusted source
|
||||
/// cannot select and insert key values. Very efficient and iterable over keys.
|
||||
/// * `blake2_128_concat` - Blake2 with 128bit + key concatenated. Slower but safe to use in all
|
||||
/// circumstances. Iterable over keys.
|
||||
///
|
||||
/// Deprecated hashers, which do not support iteration over keys include:
|
||||
/// * `twox_128` - TwoX with 128bit.
|
||||
/// * `twox_256` - TwoX with with 256bit.
|
||||
/// * `blake2_128_concat` - Blake2 with 128bit + key concatenated.
|
||||
/// * `blake2_128` - Blake2 with 128bit.
|
||||
/// * `blake2_256` - Blake2 with 256bit.
|
||||
///
|
||||
|
||||
@@ -88,7 +88,7 @@ impl BuilderDef {
|
||||
}}
|
||||
},
|
||||
StorageLineTypeDef::Simple(_) => unreachable!(),
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
quote!{{
|
||||
#data
|
||||
|
||||
@@ -93,7 +93,7 @@ impl GenesisConfigDef {
|
||||
|
||||
let typ = match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => (*value_type).clone(),
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
parse_quote!( Vec<(#key, #value_type)> )
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn impl_getters(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStrea
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
let value = &map.value;
|
||||
quote!{
|
||||
|
||||
@@ -41,20 +41,7 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) ->
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -235,10 +235,6 @@ impl StorageLineDefExt {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
ext::type_contains_ident(&map.key1, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.key2, &def.module_runtime_generic)
|
||||
@@ -249,7 +245,6 @@ impl StorageLineDefExt {
|
||||
let query_type = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => value.clone(),
|
||||
StorageLineTypeDef::Map(map) => map.value.clone(),
|
||||
StorageLineTypeDef::LinkedMap(map) => map.value.clone(),
|
||||
StorageLineTypeDef::DoubleMap(map) => map.value.clone(),
|
||||
};
|
||||
let is_option = ext::extract_type_option(&query_type).is_some();
|
||||
@@ -291,10 +286,6 @@ impl StorageLineDefExt {
|
||||
let key = &map.key;
|
||||
quote!( StorageMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
quote!( StorageLinkedMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
@@ -336,7 +327,6 @@ impl StorageLineDefExt {
|
||||
|
||||
pub enum StorageLineTypeDef {
|
||||
Map(MapDef),
|
||||
LinkedMap(MapDef),
|
||||
DoubleMap(DoubleMapDef),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
@@ -372,6 +362,7 @@ pub enum HasherKind {
|
||||
Twox256,
|
||||
Twox128,
|
||||
Twox64Concat,
|
||||
Identity,
|
||||
}
|
||||
|
||||
impl HasherKind {
|
||||
@@ -383,6 +374,7 @@ impl HasherKind {
|
||||
HasherKind::Twox256 => quote!( Twox256 ),
|
||||
HasherKind::Twox128 => quote!( Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( Twox64Concat ),
|
||||
HasherKind::Identity => quote!( Identity ),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +386,7 @@ impl HasherKind {
|
||||
HasherKind::Twox256 => quote!( StorageHasher::Twox256 ),
|
||||
HasherKind::Twox128 => quote!( StorageHasher::Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( StorageHasher::Twox64Concat ),
|
||||
HasherKind::Identity => quote!( StorageHasher::Identity ),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,7 +413,6 @@ pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStr
|
||||
use #scrate::{
|
||||
StorageValue as _,
|
||||
StorageMap as _,
|
||||
StorageLinkedMap as _,
|
||||
StorageDoubleMap as _,
|
||||
StoragePrefixedMap as _,
|
||||
};
|
||||
|
||||
@@ -27,15 +27,18 @@ mod keyword {
|
||||
syn::custom_keyword!(build);
|
||||
syn::custom_keyword!(get);
|
||||
syn::custom_keyword!(map);
|
||||
syn::custom_keyword!(linked_map);
|
||||
syn::custom_keyword!(double_map);
|
||||
syn::custom_keyword!(blake2_256);
|
||||
syn::custom_keyword!(blake2_128);
|
||||
syn::custom_keyword!(opaque_blake2_256);
|
||||
syn::custom_keyword!(opaque_blake2_128);
|
||||
syn::custom_keyword!(blake2_128_concat);
|
||||
syn::custom_keyword!(twox_256);
|
||||
syn::custom_keyword!(twox_128);
|
||||
syn::custom_keyword!(opaque_twox_256);
|
||||
syn::custom_keyword!(opaque_twox_128);
|
||||
syn::custom_keyword!(twox_64_concat);
|
||||
syn::custom_keyword!(identity);
|
||||
syn::custom_keyword!(hasher);
|
||||
syn::custom_keyword!(tainted);
|
||||
syn::custom_keyword!(natural);
|
||||
syn::custom_keyword!(prehashed);
|
||||
}
|
||||
|
||||
/// Specific `Opt` to implement structure with optional parsing
|
||||
@@ -194,7 +197,6 @@ impl_parse_for_opt!(DeclStorageBuild => keyword::build);
|
||||
#[derive(ToTokens, Debug)]
|
||||
enum DeclStorageType {
|
||||
Map(DeclStorageMap),
|
||||
LinkedMap(DeclStorageLinkedMap),
|
||||
DoubleMap(DeclStorageDoubleMap),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
@@ -203,8 +205,6 @@ impl syn::parse::Parse for DeclStorageType {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
if input.peek(keyword::map) {
|
||||
Ok(Self::Map(input.parse()?))
|
||||
} else if input.peek(keyword::linked_map) {
|
||||
Ok(Self::LinkedMap(input.parse()?))
|
||||
} else if input.peek(keyword::double_map) {
|
||||
Ok(Self::DoubleMap(input.parse()?))
|
||||
} else {
|
||||
@@ -222,15 +222,6 @@ struct DeclStorageMap {
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageLinkedMap {
|
||||
pub map_keyword: keyword::linked_map,
|
||||
pub hasher: Opt<SetHasher>,
|
||||
pub key: syn::Type,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDoubleMap {
|
||||
pub map_keyword: keyword::double_map,
|
||||
@@ -245,29 +236,38 @@ struct DeclStorageDoubleMap {
|
||||
|
||||
#[derive(ToTokens, Debug)]
|
||||
enum Hasher {
|
||||
Blake2_256(keyword::blake2_256),
|
||||
Blake2_128(keyword::blake2_128),
|
||||
Blake2_256(keyword::opaque_blake2_256),
|
||||
Blake2_128(keyword::opaque_blake2_128),
|
||||
Blake2_128Concat(keyword::blake2_128_concat),
|
||||
Twox256(keyword::twox_256),
|
||||
Twox128(keyword::twox_128),
|
||||
Twox256(keyword::opaque_twox_256),
|
||||
Twox128(keyword::opaque_twox_128),
|
||||
Twox64Concat(keyword::twox_64_concat),
|
||||
Identity(keyword::identity),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Hasher {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(keyword::blake2_256) {
|
||||
if lookahead.peek(keyword::opaque_blake2_256) {
|
||||
Ok(Self::Blake2_256(input.parse()?))
|
||||
} else if lookahead.peek(keyword::blake2_128) {
|
||||
} else if lookahead.peek(keyword::opaque_blake2_128) {
|
||||
Ok(Self::Blake2_128(input.parse()?))
|
||||
} else if lookahead.peek(keyword::blake2_128_concat) {
|
||||
Ok(Self::Blake2_128Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_256) {
|
||||
} else if lookahead.peek(keyword::opaque_twox_256) {
|
||||
Ok(Self::Twox256(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_128) {
|
||||
} else if lookahead.peek(keyword::opaque_twox_128) {
|
||||
Ok(Self::Twox128(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_64_concat) {
|
||||
Ok(Self::Twox64Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::identity) {
|
||||
Ok(Self::Identity(input.parse()?))
|
||||
} else if lookahead.peek(keyword::tainted) {
|
||||
Ok(Self::Blake2_128Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::natural) {
|
||||
Ok(Self::Twox64Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::prehashed) {
|
||||
Ok(Self::Identity(input.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@@ -313,6 +313,7 @@ impl From<Hasher> for super::HasherKind {
|
||||
Hasher::Twox256(_) => super::HasherKind::Twox256,
|
||||
Hasher::Twox128(_) => super::HasherKind::Twox128,
|
||||
Hasher::Twox64Concat(_) => super::HasherKind::Twox64Concat,
|
||||
Hasher::Identity(_) => super::HasherKind::Identity,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,7 +465,7 @@ fn parse_storage_line_defs(
|
||||
let span = line.storage_type.span();
|
||||
let no_hasher_error = || syn::Error::new(
|
||||
span,
|
||||
"Default hasher has been removed, use explicit hasher(blake2_256) instead."
|
||||
"Default hasher has been removed, use explicit hasher(blake2_128_concat) instead."
|
||||
);
|
||||
|
||||
let storage_type = match line.storage_type {
|
||||
@@ -475,13 +476,6 @@ fn parse_storage_line_defs(
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::LinkedMap(map) => super::StorageLineTypeDef::LinkedMap(
|
||||
super::MapDef {
|
||||
hasher: map.hasher.inner.ok_or_else(no_hasher_error)?.into(),
|
||||
key: map.key,
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap(
|
||||
super::DoubleMapDef {
|
||||
hasher1: map.hasher1.inner.ok_or_else(no_hasher_error)?.into(),
|
||||
|
||||
@@ -158,47 +158,6 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.to_storage_hasher_struct();
|
||||
|
||||
let head_prefix_str = syn::LitStr::new(
|
||||
&format!("HeadOf{}", line.name.to_string()),
|
||||
line.name.span(),
|
||||
);
|
||||
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
type KeyFormat = Self;
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
|
||||
impl<#impl_trait> #scrate::storage::generator::LinkedMapKeyFormat for #storage_struct {
|
||||
type Hasher = #scrate::#hasher;
|
||||
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
#instance_or_inherent::PREFIX.as_bytes()
|
||||
}
|
||||
|
||||
fn storage_prefix() -> &'static [u8] {
|
||||
#storage_name_str.as_bytes()
|
||||
}
|
||||
|
||||
fn head_prefix() -> &'static [u8] {
|
||||
#head_prefix_str.as_bytes()
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let hasher1 = map.hasher1.to_storage_hasher_struct();
|
||||
let hasher2 = map.hasher2.to_storage_hasher_struct();
|
||||
|
||||
@@ -28,6 +28,7 @@ pub trait Hashable: Sized {
|
||||
fn twox_128(&self) -> [u8; 16];
|
||||
fn twox_256(&self) -> [u8; 32];
|
||||
fn twox_64_concat(&self) -> Vec<u8>;
|
||||
fn identity(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T: Codec> Hashable for T {
|
||||
@@ -49,6 +50,7 @@ impl<T: Codec> Hashable for T {
|
||||
fn twox_64_concat(&self) -> Vec<u8> {
|
||||
self.using_encoded(Twox64Concat::hash)
|
||||
}
|
||||
fn identity(&self) -> Vec<u8> { self.encode() }
|
||||
}
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
@@ -57,6 +59,25 @@ pub trait StorageHasher: 'static {
|
||||
fn hash(x: &[u8]) -> Self::Output;
|
||||
}
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
pub trait ReversibleStorageHasher: StorageHasher {
|
||||
fn reverse(x: &[u8]) -> &[u8];
|
||||
}
|
||||
|
||||
/// Store the key directly.
|
||||
pub struct Identity;
|
||||
impl StorageHasher for Identity {
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
x.to_vec()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Identity {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with `concat(twox64(key), key)`
|
||||
pub struct Twox64Concat;
|
||||
impl StorageHasher for Twox64Concat {
|
||||
@@ -69,6 +90,11 @@ impl StorageHasher for Twox64Concat {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Twox64Concat {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
&x[8..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with `concat(blake2_128(key), key)`
|
||||
pub struct Blake2_128Concat;
|
||||
@@ -82,6 +108,11 @@ impl StorageHasher for Blake2_128Concat {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Blake2_128Concat {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
&x[16..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with blake2 128
|
||||
pub struct Blake2_128;
|
||||
|
||||
@@ -67,11 +67,12 @@ pub mod traits;
|
||||
pub mod weights;
|
||||
|
||||
pub use self::hash::{
|
||||
Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat, Blake2_128Concat, Hashable,
|
||||
Twox256, Twox128, Blake2_256, Blake2_128, Identity, Twox64Concat, Blake2_128Concat, Hashable,
|
||||
StorageHasher
|
||||
};
|
||||
pub use self::storage::{
|
||||
StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap, StoragePrefixedMap
|
||||
StorageValue, StorageMap, StorageDoubleMap, StoragePrefixedMap, IterableStorageMap,
|
||||
IterableStorageDoubleMap,
|
||||
};
|
||||
pub use self::dispatch::{Parameter, Callable, IsSubType};
|
||||
pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};
|
||||
@@ -253,24 +254,24 @@ mod tests {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
pub Data get(fn data) build(|_| vec![(15u32, 42u64)]):
|
||||
linked_map hasher(twox_64_concat) u32 => u64;
|
||||
pub OptionLinkedMap: linked_map hasher(blake2_256) u32 => Option<u32>;
|
||||
map hasher(twox_64_concat) u32 => u64;
|
||||
pub OptionLinkedMap: map hasher(blake2_128_concat) u32 => Option<u32>;
|
||||
pub GenericData get(fn generic_data):
|
||||
linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber;
|
||||
map hasher(identity) T::BlockNumber => T::BlockNumber;
|
||||
pub GenericData2 get(fn generic_data2):
|
||||
linked_map hasher(blake2_256) T::BlockNumber => Option<T::BlockNumber>;
|
||||
map hasher(blake2_128_concat) T::BlockNumber => Option<T::BlockNumber>;
|
||||
pub GetterNoFnKeyword get(no_fn): Option<u32>;
|
||||
|
||||
pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]):
|
||||
double_map hasher(twox_64_concat) u32, hasher(blake2_256) u32 => u64;
|
||||
double_map hasher(twox_64_concat) u32, hasher(blake2_128_concat) u32 => u64;
|
||||
pub GenericDataDM:
|
||||
double_map hasher(blake2_256) T::BlockNumber, hasher(twox_128) T::BlockNumber
|
||||
double_map hasher(blake2_128_concat) T::BlockNumber, hasher(identity) T::BlockNumber
|
||||
=> T::BlockNumber;
|
||||
pub GenericData2DM:
|
||||
double_map hasher(blake2_256) T::BlockNumber, hasher(twox_256) T::BlockNumber
|
||||
double_map hasher(blake2_128_concat) T::BlockNumber, hasher(twox_64_concat) T::BlockNumber
|
||||
=> Option<T::BlockNumber>;
|
||||
pub AppendableDM:
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,8 +287,16 @@ mod tests {
|
||||
|
||||
type Map = Data;
|
||||
|
||||
trait Sorted { fn sorted(self) -> Self; }
|
||||
impl<T: Ord> Sorted for Vec<T> {
|
||||
fn sorted(mut self) -> Self {
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_issue_3318() {
|
||||
fn map_issue_3318() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
assert_eq!(OptionLinkedMap::get(1), Some(1));
|
||||
@@ -297,31 +306,31 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_swap_works() {
|
||||
fn map_swap_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(0, 0);
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
OptionLinkedMap::insert(2, 2);
|
||||
OptionLinkedMap::insert(3, 3);
|
||||
|
||||
let collect = || OptionLinkedMap::enumerate().collect::<Vec<_>>();
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
|
||||
// Two existing
|
||||
OptionLinkedMap::swap(1, 2);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 1), (1, 2), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
|
||||
|
||||
// Back to normal
|
||||
OptionLinkedMap::swap(2, 1);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
|
||||
// Left existing
|
||||
OptionLinkedMap::swap(2, 5);
|
||||
assert_eq!(collect(), vec![(5, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
|
||||
|
||||
// Right existing
|
||||
OptionLinkedMap::swap(5, 2);
|
||||
assert_eq!(collect(), vec![(2, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -356,7 +365,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_basic_insert_remove_should_work() {
|
||||
fn map_basic_insert_remove_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// initialized during genesis
|
||||
assert_eq!(Map::get(&15u32), 42u64);
|
||||
@@ -382,54 +391,45 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_enumeration_and_head_should_work() {
|
||||
fn map_iteration_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Map::head(), Some(15));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(15, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
|
||||
// insert / remove
|
||||
let key = 17u32;
|
||||
Map::insert(key, 4u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 4), (15, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
|
||||
assert_eq!(Map::take(&15), 42u64);
|
||||
assert_eq!(Map::take(&key), 4u64);
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
|
||||
|
||||
// Add couple of more elements
|
||||
Map::insert(key, 42u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
|
||||
Map::insert(key + 1, 43u64);
|
||||
assert_eq!(Map::head(), Some(key + 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key + 1, 43), (key, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
|
||||
|
||||
// mutate
|
||||
let key = key + 2;
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 15;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 15), (key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 15)]);
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 17;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 17), (key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 17)]);
|
||||
|
||||
// remove first
|
||||
Map::remove(&key);
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);
|
||||
|
||||
// remove last from the list
|
||||
Map::remove(&(key - 2));
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
|
||||
|
||||
// remove the last element
|
||||
Map::remove(&(key - 1));
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ mod tests {
|
||||
hasher: StorageHasher::Twox64Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructData(PhantomData::<Test>))
|
||||
@@ -509,10 +509,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("OptionLinkedMap"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u32"),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructOptionLinkedMap(PhantomData::<Test>))
|
||||
@@ -523,10 +523,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Twox128,
|
||||
hasher: StorageHasher::Identity,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
unused: false
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData(PhantomData::<Test>))
|
||||
@@ -537,10 +537,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData2"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
unused: false
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::<Test>))
|
||||
@@ -564,7 +564,7 @@ mod tests {
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_128Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructDataDM(PhantomData::<Test>))
|
||||
@@ -575,11 +575,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericDataDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox128,
|
||||
key2_hasher: StorageHasher::Identity,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::<Test>))
|
||||
@@ -590,11 +590,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData2DM"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox256,
|
||||
key2_hasher: StorageHasher::Twox64Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
@@ -605,11 +605,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("AppendableDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("Vec<u32>"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_128Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::borrow::Borrow;
|
||||
use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use codec::{Ref, FullCodec, FullEncode, Decode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, traits::Len};
|
||||
use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher};
|
||||
|
||||
/// Generator for `StorageDoubleMap` used by `decl_storage`.
|
||||
///
|
||||
@@ -55,6 +56,22 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// The full prefix; just the hash of `module_prefix` concatenated to the hash of
|
||||
/// `storage_prefix`.
|
||||
fn prefix_hash() -> Vec<u8> {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
|
||||
let mut result = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len()
|
||||
);
|
||||
|
||||
result.extend_from_slice(&module_prefix_hashed[..]);
|
||||
result.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
@@ -62,8 +79,7 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the first part of the key used in top storage.
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> Vec<u8>
|
||||
where
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
@@ -82,19 +98,32 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let mut final_key = Self::storage_double_map_final_key1(k1);
|
||||
final_key.extend_from_slice(k2.using_encoded(Self::Hasher2::hash).as_ref());
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key1_hashed = k1.borrow().using_encoded(Self::Hasher1::hash);
|
||||
let key2_hashed = k2.borrow().using_encoded(Self::Hasher2::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len()
|
||||
+ storage_prefix_hashed.len()
|
||||
+ key1_hashed.as_ref().len()
|
||||
+ key2_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key1_hashed.as_ref());
|
||||
final_key.extend_from_slice(key2_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G
|
||||
where
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G where
|
||||
K1: FullEncode,
|
||||
K2: FullEncode,
|
||||
V: FullCodec,
|
||||
@@ -102,32 +131,28 @@ where
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
Self::storage_double_map_final_key(k1, k2)
|
||||
}
|
||||
|
||||
fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::exists(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
G::from_optional_value_to_query(unhashed::get(&Self::storage_double_map_final_key(k1, k2)))
|
||||
}
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
@@ -137,8 +162,12 @@ where
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2)
|
||||
where
|
||||
fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(
|
||||
x_k1: XKArg1,
|
||||
x_k2: XKArg2,
|
||||
y_k1: YKArg1,
|
||||
y_k2: YKArg2
|
||||
) where
|
||||
XKArg1: EncodeLike<K1>,
|
||||
XKArg2: EncodeLike<K2>,
|
||||
YKArg1: EncodeLike<K1>,
|
||||
@@ -160,8 +189,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>,
|
||||
@@ -169,8 +197,7 @@ where
|
||||
unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
@@ -181,8 +208,8 @@ where
|
||||
unhashed::kill_prefix(Self::storage_double_map_final_key1(k1).as_ref())
|
||||
}
|
||||
|
||||
fn iter_prefix<KArg1>(k1: KArg1) -> storage::PrefixIterator<V>
|
||||
where KArg1: ?Sized + EncodeLike<K1>
|
||||
fn iter_prefix<KArg1>(k1: KArg1) -> storage::PrefixIterator<V> where
|
||||
KArg1: ?Sized + EncodeLike<K1>
|
||||
{
|
||||
let prefix = Self::storage_double_map_final_key1(k1);
|
||||
storage::PrefixIterator::<V> {
|
||||
@@ -192,8 +219,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R,
|
||||
@@ -213,8 +239,7 @@ where
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
) -> Result<(), &'static str> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
@@ -246,8 +271,7 @@ where
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
@@ -260,10 +284,10 @@ where
|
||||
.unwrap_or_else(|_| Self::insert(k1, k2, items));
|
||||
}
|
||||
|
||||
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Result<usize, &'static str>
|
||||
where KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len,
|
||||
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Result<usize, &'static str> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(key1, key2);
|
||||
if let Some(v) = unhashed::get_raw(&final_key) {
|
||||
@@ -276,6 +300,135 @@ where
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_keys<
|
||||
OldHasher1: StorageHasher,
|
||||
OldHasher2: StorageHasher,
|
||||
KeyArg1: EncodeLike<K1>,
|
||||
KeyArg2: EncodeLike<K2>,
|
||||
>(key1: KeyArg1, key2: KeyArg2) -> Option<V> {
|
||||
let old_key = {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key1_hashed = key1.borrow().using_encoded(OldHasher1::hash);
|
||||
let key2_hashed = key2.borrow().using_encoded(OldHasher2::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len()
|
||||
+ storage_prefix_hashed.len()
|
||||
+ key1_hashed.as_ref().len()
|
||||
+ key2_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key1_hashed.as_ref());
|
||||
final_key.extend_from_slice(key2_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
};
|
||||
unhashed::take(old_key.as_ref()).map(|value| {
|
||||
unhashed::put(Self::storage_double_map_final_key(key1, key2).as_ref(), &value);
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to iterate through items in a storage map.
|
||||
pub struct MapIterator<K, V, Hasher> {
|
||||
prefix: Vec<u8>,
|
||||
previous_key: Vec<u8>,
|
||||
drain: bool,
|
||||
_phantom: ::sp_std::marker::PhantomData<(K, V, Hasher)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Decode + Sized,
|
||||
V: Decode + Sized,
|
||||
Hasher: ReversibleStorageHasher
|
||||
> Iterator for MapIterator<K, V, Hasher> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<(K, V)> {
|
||||
loop {
|
||||
let maybe_next = sp_io::storage::next_key(&self.previous_key)
|
||||
.filter(|n| n.starts_with(&self.prefix));
|
||||
break match maybe_next {
|
||||
Some(next) => {
|
||||
self.previous_key = next;
|
||||
match unhashed::get::<V>(&self.previous_key) {
|
||||
Some(value) => {
|
||||
if self.drain {
|
||||
unhashed::kill(&self.previous_key)
|
||||
}
|
||||
let mut key_material = Hasher::reverse(&self.previous_key[self.prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => Some((key, value)),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K1: FullCodec,
|
||||
K2: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageDoubleMap<K1, K2, V>,
|
||||
> storage::IterableStorageDoubleMap<K1, K2, V> for G where
|
||||
G::Hasher1: ReversibleStorageHasher,
|
||||
G::Hasher2: ReversibleStorageHasher
|
||||
{
|
||||
type Iterator = MapIterator<K2, V, G::Hasher2>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn iter(k1: impl EncodeLike<K1>) -> Self::Iterator {
|
||||
let prefix = G::storage_double_map_final_key1(k1);
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: false,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn drain(k1: impl EncodeLike<K1>) -> Self::Iterator {
|
||||
let prefix = G::storage_double_map_final_key1(k1);
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: true,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: Fn(O) -> Option<V>>(f: F) {
|
||||
let prefix = G::prefix_hash();
|
||||
let mut previous_key = prefix.clone();
|
||||
loop {
|
||||
match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) {
|
||||
Some(next) => {
|
||||
previous_key = next;
|
||||
let maybe_value = unhashed::get::<O>(&previous_key);
|
||||
match maybe_value {
|
||||
Some(value) => match f(value) {
|
||||
Some(new) => unhashed::put::<V>(&previous_key, &new),
|
||||
None => unhashed::kill(&previous_key),
|
||||
},
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
// Copyright 2019-2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{FullCodec, Encode, Decode, EncodeLike, Ref};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use sp_std::{prelude::*, marker::PhantomData};
|
||||
|
||||
/// Generator for `StorageLinkedMap` used by `decl_storage`.
|
||||
///
|
||||
/// By default final key generation rely on `KeyFormat`.
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// The family of key formats used for this map.
|
||||
type KeyFormat: KeyFormat;
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_key::<KeyArg>(&key)
|
||||
}
|
||||
|
||||
/// Generate the hashed key for head
|
||||
fn storage_linked_map_final_head_key() -> Vec<u8> {
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_head_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-abstracted key format used for a family of linked-map types.
|
||||
///
|
||||
/// # Default mapping of keys to a storage path
|
||||
///
|
||||
/// The key for the head of the map is stored at one fixed path:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(head_prefix)
|
||||
/// ```
|
||||
///
|
||||
/// For each key, the value stored under that key is appended with a
|
||||
/// [`Linkage`](struct.Linkage.html) (which hold previous and next key) at the path:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
|
||||
/// ```
|
||||
///
|
||||
/// Enumeration is done by getting the head of the linked map and then iterating getting the
|
||||
/// value and linkage stored at the key until the found linkage has no next key.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait KeyFormat {
|
||||
/// Hasher. Used for generating final key and final head key.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Module prefix. Used for generating final key.
|
||||
fn module_prefix() -> &'static [u8];
|
||||
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// Storage prefix. Used for generating final head key.
|
||||
fn head_prefix() -> &'static [u8];
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<K>(key: &K) -> Vec<u8>
|
||||
where
|
||||
K: Encode,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key_hashed = key.using_encoded(Self::Hasher::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage to store the head of the linked map.
|
||||
fn storage_linked_map_final_head_key() -> Vec<u8> {
|
||||
[
|
||||
Twox128::hash(Self::module_prefix()),
|
||||
Twox128::hash(Self::head_prefix()),
|
||||
].concat()
|
||||
}
|
||||
}
|
||||
|
||||
/// Linkage data of an element (it's successor and predecessor)
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Linkage<Key> {
|
||||
/// Previous element key in storage (None for the first element)
|
||||
pub previous: Option<Key>,
|
||||
/// Next element key in storage (None for the last element)
|
||||
pub next: Option<Key>,
|
||||
}
|
||||
|
||||
impl<Key> Default for Linkage<Key> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
previous: None,
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode like a linkage.
|
||||
#[derive(Encode)]
|
||||
struct EncodeLikeLinkage<PKey: EncodeLike<Key>, NKey: EncodeLike<Key>, Key: Encode> {
|
||||
// Previous element key in storage (None for the first element)
|
||||
previous: Option<PKey>,
|
||||
// Next element key in storage (None for the last element)
|
||||
next: Option<NKey>,
|
||||
// The key of the linkage this type encode to
|
||||
phantom: core::marker::PhantomData<Key>,
|
||||
}
|
||||
|
||||
/// A key-value pair iterator for enumerable map.
|
||||
pub struct Enumerator<K, V, F> {
|
||||
next: Option<K>,
|
||||
_phantom: PhantomData<(V, F)>,
|
||||
}
|
||||
|
||||
impl<K, V, F> Enumerator<K, V, F> {
|
||||
/// Create an explicit enumerator for testing.
|
||||
#[cfg(test)]
|
||||
pub fn from_head(head: K) -> Self {
|
||||
Enumerator {
|
||||
next: Some(head),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, F> Iterator for Enumerator<K, V, F>
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.next.take()?;
|
||||
|
||||
let (val, linkage): (V, Linkage<K>) = {
|
||||
let next_full_key = F::storage_linked_map_final_key(&next);
|
||||
match read_with_linkage::<K, V>(next_full_key.as_ref()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), next_full_key,
|
||||
);
|
||||
return None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.next = linkage.next;
|
||||
Some((next, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update linkage when this element is removed.
|
||||
///
|
||||
/// Takes care of updating previous and next elements points
|
||||
/// as well as updates head if the element is first or last.
|
||||
fn remove_linkage<K, V, F>(linkage: Linkage<K>)
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
let next_key = linkage.next.as_ref().map(|k| F::storage_linked_map_final_key(k));
|
||||
let prev_key = linkage.previous.as_ref().map(|k| F::storage_linked_map_final_key(k));
|
||||
|
||||
if let Some(prev_key) = prev_key {
|
||||
// Retrieve previous element and update `next`
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(prev_key.as_ref()) {
|
||||
res.1.next = linkage.next;
|
||||
unhashed::put(prev_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
previous value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), prev_key,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// we were first so let's update the head
|
||||
write_head::<&K, K, F>(linkage.next.as_ref());
|
||||
}
|
||||
if let Some(next_key) = next_key {
|
||||
// Update previous of next element
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(next_key.as_ref()) {
|
||||
res.1.previous = linkage.previous;
|
||||
unhashed::put(next_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), next_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the contained data and its linkage.
|
||||
pub(super) fn read_with_linkage<K, V>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
||||
where
|
||||
K: Decode,
|
||||
V: Decode,
|
||||
{
|
||||
unhashed::get(key)
|
||||
}
|
||||
|
||||
/// Generate linkage for newly inserted element.
|
||||
///
|
||||
/// Takes care of updating head and previous head's pointer.
|
||||
pub(super) fn new_head_linkage<KeyArg, K, V, F>(key: KeyArg) -> Linkage<K>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
if let Some(head) = read_head::<K, F>() {
|
||||
// update previous head predecessor
|
||||
{
|
||||
let head_key = F::storage_linked_map_final_key(&head);
|
||||
if let Some((data, linkage)) = read_with_linkage::<K, V>(head_key.as_ref()) {
|
||||
let new_linkage = EncodeLikeLinkage::<_, _, K> {
|
||||
previous: Some(Ref::from(&key)),
|
||||
next: linkage.next.as_ref(),
|
||||
phantom: Default::default(),
|
||||
};
|
||||
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
head value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), head_key,
|
||||
);
|
||||
// Thus we consider we are first - update the head and produce empty linkage
|
||||
|
||||
write_head::<_, _, F>(Some(key));
|
||||
return Linkage::default();
|
||||
}
|
||||
}
|
||||
// update to current head
|
||||
write_head::<_, _, F>(Some(key));
|
||||
// return linkage with pointer to previous head
|
||||
let mut linkage = Linkage::default();
|
||||
linkage.next = Some(head);
|
||||
linkage
|
||||
} else {
|
||||
// we are first - update the head and produce empty linkage
|
||||
write_head::<_, _, F>(Some(key));
|
||||
Linkage::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read current head pointer.
|
||||
pub(crate) fn read_head<K, F>() -> Option<K>
|
||||
where
|
||||
K: Decode,
|
||||
F: KeyFormat,
|
||||
{
|
||||
unhashed::get(F::storage_linked_map_final_head_key().as_ref())
|
||||
}
|
||||
|
||||
/// Overwrite current head pointer.
|
||||
///
|
||||
/// If `None` is given head is removed from storage.
|
||||
pub(super) fn write_head<KeyArg, K, F>(head: Option<KeyArg>)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
match head.as_ref() {
|
||||
Some(head) => unhashed::put(F::storage_linked_map_final_head_key().as_ref(), head),
|
||||
None => unhashed::kill(F::storage_linked_map_final_head_key().as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, G> storage::StorageLinkedMap<K, V> for G
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageLinkedMap<K, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
type Enumerator = Enumerator<K, V, G::KeyFormat>;
|
||||
|
||||
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let val = unhashed::get(Self::storage_linked_map_final_key(key).as_ref());
|
||||
G::from_optional_value_to_query(val)
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
||||
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
||||
let full_value_1 = read_with_linkage::<K, V>(final_key1.as_ref());
|
||||
let full_value_2 = read_with_linkage::<K, V>(final_key2.as_ref());
|
||||
|
||||
match (full_value_1, full_value_2) {
|
||||
// Just keep linkage in order and only swap values.
|
||||
(Some((value1, linkage1)), Some((value2, linkage2))) => {
|
||||
unhashed::put(final_key1.as_ref(), &(value2, linkage1));
|
||||
unhashed::put(final_key2.as_ref(), &(value1, linkage2));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(Some((value, _linkage)), None) => {
|
||||
Self::remove(key1);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key2);
|
||||
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(None, Some((value, _linkage))) => {
|
||||
Self::remove(key2);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key1);
|
||||
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
||||
}
|
||||
// No-op.
|
||||
(None, None) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
let linkage = match read_with_linkage::<_, V>(final_key.as_ref()) {
|
||||
// overwrite but reuse existing linkage
|
||||
Some((_data, linkage)) => linkage,
|
||||
// create new linkage
|
||||
None => new_head_linkage::<_, _, V, G::KeyFormat>(key),
|
||||
};
|
||||
unhashed::put(final_key.as_ref(), &(val, linkage))
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
G::take(key);
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
|
||||
let (mut val, _linkage) = read_with_linkage::<K, V>(final_key.as_ref())
|
||||
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
||||
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::insert(key, val),
|
||||
None => G::remove(key),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let final_key = Self::storage_linked_map_final_key(key);
|
||||
|
||||
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
||||
|
||||
let value = full_value.map(|(data, linkage)| {
|
||||
remove_linkage::<K, V, G::KeyFormat>(linkage);
|
||||
data
|
||||
});
|
||||
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn enumerate() -> Self::Enumerator {
|
||||
Enumerator::<_, _, G::KeyFormat> {
|
||||
next: read_head::<_, G::KeyFormat>(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn head() -> Option<K> {
|
||||
read_head::<_, G::KeyFormat>()
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_linked_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// The translation happens in-place, new keys are inserted at the same time as old keys are
|
||||
/// removed, thus new keys must not collide with still remaining old keys.
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V
|
||||
{
|
||||
let head_key = read_head::<K2, G::KeyFormat>().ok_or(None)?;
|
||||
|
||||
let mut last_key = None;
|
||||
let mut current_key = head_key.clone();
|
||||
|
||||
write_head::<&K, K, G::KeyFormat>(Some(&translate_key(head_key)));
|
||||
|
||||
let translate_linkage = |old: Linkage<K2>| -> Linkage<K> {
|
||||
Linkage {
|
||||
previous: old.previous.map(&translate_key),
|
||||
next: old.next.map(&translate_key),
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let old_raw_key = G::KeyFormat::storage_linked_map_final_key(¤t_key);
|
||||
let x = unhashed::take(old_raw_key.as_ref());
|
||||
let (val, linkage): (V2, Linkage<K2>) = match x {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
// we failed to read value and linkage. Update the last key's linkage
|
||||
// to end the map early, since it's impossible to iterate further.
|
||||
if let Some(last_key) = last_key {
|
||||
let last_raw_key = G::storage_linked_map_final_key(&last_key);
|
||||
if let Some((val, mut linkage))
|
||||
= read_with_linkage::<K, V>(last_raw_key.as_ref())
|
||||
{
|
||||
// defensive: should always happen, since it was just written
|
||||
// in the last iteration of the loop.
|
||||
linkage.next = None;
|
||||
unhashed::put(last_raw_key.as_ref(), &(&val, &linkage));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Some(current_key));
|
||||
}
|
||||
};
|
||||
let next = linkage.next.clone();
|
||||
|
||||
let val = translate_val(val);
|
||||
let linkage = translate_linkage(linkage);
|
||||
|
||||
// and write in the value and linkage under the new key.
|
||||
let new_key = translate_key(current_key.clone());
|
||||
let new_raw_key = G::storage_linked_map_final_key(&new_key);
|
||||
unhashed::put(new_raw_key.as_ref(), &(&val, &linkage));
|
||||
|
||||
match next {
|
||||
None => break,
|
||||
Some(next) => {
|
||||
last_key = Some(new_key);
|
||||
current_key = next
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,9 @@
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::borrow::Borrow;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use codec::{FullCodec, FullEncode, Decode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, traits::Len};
|
||||
use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher};
|
||||
|
||||
/// Generator for `StorageMap` used by `decl_storage`.
|
||||
///
|
||||
@@ -44,6 +45,22 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// The full prefix; just the hash of `module_prefix` concatenated to the hash of
|
||||
/// `storage_prefix`.
|
||||
fn prefix_hash() -> Vec<u8> {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
|
||||
let mut result = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len()
|
||||
);
|
||||
|
||||
result.extend_from_slice(&module_prefix_hashed[..]);
|
||||
result.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
@@ -51,8 +68,7 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8>
|
||||
where
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8> where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
@@ -71,6 +87,107 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to iterate through items in a storage map.
|
||||
pub struct StorageMapIterator<K, V, Hasher> {
|
||||
prefix: Vec<u8>,
|
||||
previous_key: Vec<u8>,
|
||||
drain: bool,
|
||||
_phantom: ::sp_std::marker::PhantomData<(K, V, Hasher)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Decode + Sized,
|
||||
V: Decode + Sized,
|
||||
Hasher: ReversibleStorageHasher
|
||||
> Iterator for StorageMapIterator<K, V, Hasher> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<(K, V)> {
|
||||
loop {
|
||||
let maybe_next = sp_io::storage::next_key(&self.previous_key)
|
||||
.filter(|n| n.starts_with(&self.prefix));
|
||||
break match maybe_next {
|
||||
Some(next) => {
|
||||
self.previous_key = next;
|
||||
match unhashed::get::<V>(&self.previous_key) {
|
||||
Some(value) => {
|
||||
if self.drain {
|
||||
unhashed::kill(&self.previous_key)
|
||||
}
|
||||
let mut key_material = Hasher::reverse(&self.previous_key[self.prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => Some((key, value)),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageMap<K, V>,
|
||||
> storage::IterableStorageMap<K, V> for G where
|
||||
G::Hasher: ReversibleStorageHasher
|
||||
{
|
||||
type Iterator = StorageMapIterator<K, V, G::Hasher>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn iter() -> Self::Iterator {
|
||||
let prefix = G::prefix_hash();
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: false,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn drain() -> Self::Iterator {
|
||||
let prefix = G::prefix_hash();
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: true,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: Fn(K, O) -> Option<V>>(f: F) {
|
||||
let prefix = G::prefix_hash();
|
||||
let mut previous_key = prefix.clone();
|
||||
loop {
|
||||
match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) {
|
||||
Some(next) => {
|
||||
previous_key = next;
|
||||
let maybe_value = unhashed::get::<O>(&previous_key);
|
||||
match maybe_value {
|
||||
Some(value) => {
|
||||
let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => match f(key, value) {
|
||||
Some(new) => unhashed::put::<V>(&previous_key, &new),
|
||||
None => unhashed::kill(&previous_key),
|
||||
},
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
@@ -228,4 +345,26 @@ impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V>
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
|
||||
let old_key = {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key_hashed = key.borrow().using_encoded(OldHasher::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
};
|
||||
unhashed::take(old_key.as_ref()).map(|value| {
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &value);
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,23 +23,20 @@
|
||||
//!
|
||||
//! This is internal api and is subject to change.
|
||||
|
||||
mod linked_map;
|
||||
mod map;
|
||||
mod double_map;
|
||||
mod value;
|
||||
|
||||
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage, KeyFormat as LinkedMapKeyFormat};
|
||||
pub use map::StorageMap;
|
||||
pub use double_map::StorageDoubleMap;
|
||||
pub use value::StorageValue;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use sp_io::TestExternalities;
|
||||
use codec::{Encode, Decode};
|
||||
use crate::storage::{unhashed, generator::{StorageValue, StorageLinkedMap}};
|
||||
use codec::Encode;
|
||||
use crate::storage::{unhashed, generator::StorageValue, IterableStorageMap};
|
||||
|
||||
struct Runtime {}
|
||||
pub trait Trait {
|
||||
@@ -56,16 +53,10 @@ mod tests {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
|
||||
struct NumberNumber {
|
||||
a: u32,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Runtime {
|
||||
Value get(fn value) config(): (u64, u64);
|
||||
NumberMap: linked_map hasher(blake2_256) NumberNumber => u64;
|
||||
NumberMap: map hasher(identity) u32 => u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,41 +80,25 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_translate_works() {
|
||||
use super::linked_map::{self, Enumerator, KeyFormat};
|
||||
|
||||
type Format = <NumberMap as StorageLinkedMap<NumberNumber, u64>>::KeyFormat;
|
||||
|
||||
fn map_translate_works() {
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
// start with a map of u32 -> u32.
|
||||
for i in 0u32..100u32 {
|
||||
let final_key = <Format as KeyFormat>::storage_linked_map_final_key(&i);
|
||||
|
||||
let linkage = linked_map::new_head_linkage::<_, u32, u32, Format>(&i);
|
||||
unhashed::put(final_key.as_ref(), &(&i, linkage));
|
||||
unhashed::put(&NumberMap::hashed_key_for(&i), &(i as u64));
|
||||
}
|
||||
|
||||
let head = linked_map::read_head::<u32, Format>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Enumerator::<u32, u32, Format>::from_head(head).collect::<Vec<_>>(),
|
||||
(0..100).rev().map(|x| (x, x)).collect::<Vec<_>>(),
|
||||
NumberMap::iter().collect::<Vec<_>>(),
|
||||
(0..100).map(|x| (x as u32, x as u64)).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// do translation.
|
||||
NumberMap::translate(
|
||||
|k: u32| NumberNumber { a: k, b: k },
|
||||
|v: u32| (v as u64) << 32 | v as u64,
|
||||
).unwrap();
|
||||
NumberMap::translate(|k: u32, v: u64| if k % 2 == 0 { Some((k as u64) << 32 | v) } else { None });
|
||||
|
||||
assert!(linked_map::read_head::<NumberNumber, Format>().is_some());
|
||||
assert_eq!(
|
||||
NumberMap::enumerate().collect::<Vec<_>>(),
|
||||
(0..100u32).rev().map(|x| (
|
||||
NumberNumber { a: x, b: x },
|
||||
(x as u64) << 32 | x as u64,
|
||||
)).collect::<Vec<_>>(),
|
||||
NumberMap::iter().collect::<Vec<_>>(),
|
||||
(0..50u32).map(|x| x * 2).map(|x| (x, (x as u64) << 32 | x as u64)).collect::<Vec<_>>(),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -202,78 +202,60 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V>;
|
||||
|
||||
/// Migrate an item with the given `key` from a `blake2_256` hasher to the current hasher.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_key_from_blake<KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
|
||||
Self::migrate_key::<crate::hash::Blake2_256, KeyArg>(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed linked map in storage.
|
||||
///
|
||||
/// Similar to `StorageMap` but allows to enumerate other elements and doesn't implement append.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageLinkedMap`]
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// A strongly-typed map in storage whose keys and values can be iterated over.
|
||||
pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Enumerator: Iterator<Item = (K, V)>;
|
||||
type Iterator: Iterator<Item = (K, V)>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
/// Enumerate all elements in the map in no particular order. If you alter the map while doing
|
||||
/// this, you'll get undefined results.
|
||||
fn iter() -> Self::Iterator;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
/// Remove all elements from the map and iterate through them in no particular order. If you
|
||||
/// add elements to the map while doing this, you'll get undefined results.
|
||||
fn drain() -> Self::Iterator;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
/// Translate the values of all elements by a function `f`, in the map in no particular order.
|
||||
/// By returning `None` from `f` for an element, you'll remove it from the map.
|
||||
fn translate<O: Decode, F: Fn(K, O) -> Option<V>>(f: F);
|
||||
}
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
/// A strongly-typed double map in storage whose secondary keys and values can be iterated over.
|
||||
pub trait IterableStorageDoubleMap<
|
||||
K1: FullCodec,
|
||||
K2: FullCodec,
|
||||
V: FullCodec
|
||||
>: StorageDoubleMap<K1, K2, V> {
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Iterator: Iterator<Item = (K2, V)>;
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
/// Enumerate all elements in the map with first key `k1` in no particular order. If you add or
|
||||
/// remove values whose first key is `k1` to the map while doing this, you'll get undefined
|
||||
/// results.
|
||||
fn iter(k1: impl EncodeLike<K1>) -> Self::Iterator;
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
/// Remove all elements from the map with first key `k1` and iterate through them in no
|
||||
/// particular order. If you add elements with first key `k1` to the map while doing this,
|
||||
/// you'll get undefined results.
|
||||
fn drain(k1: impl EncodeLike<K1>) -> Self::Iterator;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Return current head element.
|
||||
fn head() -> Option<K>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn enumerate() -> Self::Enumerator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Translate the keys and values from some previous `(K2, V2)` to the current type.
|
||||
///
|
||||
/// `TK` translates keys from the old type, and `TV` translates values.
|
||||
///
|
||||
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
|
||||
/// The `Err` contains the first key which could not be migrated, or `None` if the
|
||||
/// head of the list could not be read.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This function must be used with care, before being updated the storage still contains the
|
||||
/// old type, thus other calls (such as `get`) will fail at decoding it.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This would typically be called inside the module implementation of on_runtime_upgrade, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_runtime_upgrade`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V;
|
||||
/// Translate the values of all elements by a function `f`, in the map in no particular order.
|
||||
/// By returning `None` from `f` for an element, you'll remove it from the map.
|
||||
fn translate<O: Decode, F: Fn(O) -> Option<V>>(f: F);
|
||||
}
|
||||
|
||||
/// An implementation of a map with a two keys.
|
||||
@@ -377,6 +359,17 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len;
|
||||
|
||||
/// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and
|
||||
/// `OldHasher2` to the current hashers.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_keys<
|
||||
OldHasher1: StorageHasher,
|
||||
OldHasher2: StorageHasher,
|
||||
KeyArg1: EncodeLike<K1>,
|
||||
KeyArg2: EncodeLike<K2>,
|
||||
>(key1: KeyArg1, key2: KeyArg2) -> Option<V>;
|
||||
}
|
||||
|
||||
/// Iterator for prefixed map.
|
||||
@@ -440,7 +433,7 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
|
||||
}
|
||||
|
||||
/// Iter over all value of the storage.
|
||||
fn iter() -> PrefixIterator<Value> {
|
||||
fn iter_values() -> PrefixIterator<Value> {
|
||||
let prefix = Self::final_prefix();
|
||||
PrefixIterator {
|
||||
prefix: prefix.to_vec(),
|
||||
@@ -535,26 +528,26 @@ mod test {
|
||||
assert_eq!(MyStorage::final_prefix().to_vec(), k);
|
||||
|
||||
// test iteration
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u64);
|
||||
unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &2u64);
|
||||
unhashed::put(&[&k[..], &vec![8][..]].concat(), &3u64);
|
||||
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u64);
|
||||
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
|
||||
|
||||
// test removal
|
||||
MyStorage::remove_all();
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
// test migration
|
||||
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u32);
|
||||
unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u32);
|
||||
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
MyStorage::translate_values(|v: u32| v as u64).unwrap();
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2]);
|
||||
MyStorage::remove_all();
|
||||
|
||||
// test migration 2
|
||||
@@ -564,9 +557,9 @@ mod test {
|
||||
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u32);
|
||||
|
||||
// (contains some value that successfully decoded to u64)
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2, 3]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3]);
|
||||
assert_eq!(MyStorage::translate_values(|v: u128| v as u64), Err(2));
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 3]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 3]);
|
||||
MyStorage::remove_all();
|
||||
|
||||
// test that other values are not modified.
|
||||
|
||||
@@ -26,10 +26,16 @@ use sp_runtime::{
|
||||
ConsensusEngineId, DispatchResult, DispatchError,
|
||||
traits::{MaybeSerializeDeserialize, AtLeast32Bit, Saturating, TrailingZeroInput},
|
||||
};
|
||||
|
||||
use crate::dispatch::Parameter;
|
||||
use crate::storage::StorageMap;
|
||||
|
||||
/// Migrate a given account.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait MigrateAccount<A> {
|
||||
/// Migrate the `account`.
|
||||
fn migrate_account(account: &A);
|
||||
}
|
||||
|
||||
/// An abstraction of a value stored within storage, but possibly as part of a larger composite
|
||||
/// item.
|
||||
pub trait StoredMap<K, T> {
|
||||
|
||||
@@ -37,10 +37,10 @@ mod tests {
|
||||
// non-getters: pub / $default
|
||||
|
||||
/// Hello, this is doc!
|
||||
U32 : Option<u32>;
|
||||
pub PUBU32 : Option<u32>;
|
||||
U32MYDEF : Option<u32>;
|
||||
pub PUBU32MYDEF : Option<u32>;
|
||||
U32: Option<u32>;
|
||||
pub PUBU32: Option<u32>;
|
||||
U32MYDEF: Option<u32>;
|
||||
pub PUBU32MYDEF: Option<u32>;
|
||||
|
||||
// getters: pub / $default
|
||||
// we need at least one type which uses T, otherwise GenesisConfig will complain.
|
||||
@@ -59,31 +59,23 @@ mod tests {
|
||||
GetOptU32WithBuilderNone get(fn opt_u32_with_builder_none) build(|_| None): Option<u32>;
|
||||
|
||||
// map non-getters: pub / $default
|
||||
MAPU32 : map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBMAPU32 : map hasher(blake2_256) u32 => Option<String>;
|
||||
MAPU32MYDEF : map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBMAPU32MYDEF : map hasher(blake2_256) u32 => Option<String>;
|
||||
MAPU32: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
pub PUBMAPU32: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
MAPU32MYDEF: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
pub PUBMAPU32MYDEF: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
|
||||
// map getters: pub / $default
|
||||
GETMAPU32 get(fn map_u32_getter): map hasher(blake2_256) u32 => String;
|
||||
pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_256) u32 => String;
|
||||
GETMAPU32 get(fn map_u32_getter): map hasher(blake2_128_concat) u32 => String;
|
||||
pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_128_concat) u32 => String;
|
||||
|
||||
GETMAPU32MYDEF get(fn map_u32_getter_mydef):
|
||||
map hasher(blake2_256) u32 => String = "map".into();
|
||||
map hasher(blake2_128_concat) u32 => String = "map".into();
|
||||
pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef):
|
||||
map hasher(blake2_256) u32 => String = "pubmap".into();
|
||||
|
||||
// linked map
|
||||
LINKEDMAPU32 : linked_map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBLINKEDMAPU32MYDEF : linked_map hasher(blake2_256) u32 => Option<String>;
|
||||
GETLINKEDMAPU32 get(fn linked_map_u32_getter):
|
||||
linked_map hasher(blake2_256) u32 => String;
|
||||
pub PUBGETLINKEDMAPU32MYDEF get(fn pub_linked_map_u32_getter_mydef):
|
||||
linked_map hasher(blake2_256) u32 => String = "pubmap".into();
|
||||
map hasher(blake2_128_concat) u32 => String = "pubmap".into();
|
||||
|
||||
COMPLEXTYPE1: ::std::vec::Vec<<T as Trait>::Origin>;
|
||||
COMPLEXTYPE2: (Vec<Vec<(u16,Box<( )>)>>, u32);
|
||||
COMPLEXTYPE3: [u32;25];
|
||||
COMPLEXTYPE2: (Vec<Vec<(u16, Box<()>)>>, u32);
|
||||
COMPLEXTYPE3: [u32; 25];
|
||||
}
|
||||
add_extra_genesis {
|
||||
build(|_| {});
|
||||
@@ -249,10 +241,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("MAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -263,10 +255,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -277,10 +269,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("MAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -291,10 +283,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -305,10 +297,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -319,10 +311,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -333,10 +325,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -347,72 +339,16 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETLINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("COMPLEXTYPE1"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
@@ -562,17 +498,13 @@ mod test_append_and_len {
|
||||
JustVecWithDefault: Vec<u32> = vec![6, 9];
|
||||
OptionVec: Option<Vec<u32>>;
|
||||
|
||||
MapVec: map hasher(blake2_256) u32 => Vec<u32>;
|
||||
MapVecWithDefault: map hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
MapVec: map hasher(blake2_128_concat) u32 => Vec<u32>;
|
||||
MapVecWithDefault: map hasher(blake2_128_concat) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map hasher(blake2_128_concat) u32 => Option<Vec<u32>>;
|
||||
|
||||
DoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec<u32>;
|
||||
DoubleMapVecWithDefault: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionDoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
|
||||
LinkedMapVec: linked_map hasher(blake2_256) u32 => Vec<u32>;
|
||||
LinkedMapVecWithDefault: linked_map hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionLinkedMapVec: linked_map hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
DoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec<u32>;
|
||||
DoubleMapVecWithDefault: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionDoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Option<Vec<u32>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,13 +576,11 @@ mod test_append_and_len {
|
||||
JustVec::put(&vec![1, 2, 3, 4]);
|
||||
OptionVec::put(&vec![1, 2, 3, 4, 5]);
|
||||
MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]);
|
||||
LinkedMapVec::insert(2, &vec![1, 2, 3]);
|
||||
DoubleMapVec::insert(0, 1, &vec![1, 2]);
|
||||
|
||||
assert_eq!(JustVec::decode_len().unwrap(), 4);
|
||||
assert_eq!(OptionVec::decode_len().unwrap(), 5);
|
||||
assert_eq!(MapVec::decode_len(1).unwrap(), 6);
|
||||
assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3);
|
||||
assert_eq!(DoubleMapVec::decode_len(0, 1).unwrap(), 2);
|
||||
});
|
||||
}
|
||||
@@ -678,16 +608,6 @@ mod test_append_and_len {
|
||||
assert_eq!(OptionMapVec::get(0), None);
|
||||
assert_eq!(OptionMapVec::decode_len(0), Ok(0));
|
||||
|
||||
// linked map
|
||||
assert_eq!(LinkedMapVec::get(0), vec![]);
|
||||
assert_eq!(LinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(LinkedMapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(LinkedMapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionLinkedMapVec::get(0), None);
|
||||
assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
// Double map
|
||||
assert_eq!(DoubleMapVec::get(0, 0), vec![]);
|
||||
assert_eq!(DoubleMapVec::decode_len(0, 1), Ok(0));
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
use frame_support::storage::unhashed;
|
||||
use codec::Encode;
|
||||
use frame_support::{StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, StoragePrefixedMap};
|
||||
use sp_io::{TestExternalities, hashing::{twox_128, blake2_128, blake2_256}};
|
||||
use frame_support::{StorageDoubleMap, StorageMap, StorageValue, StoragePrefixedMap};
|
||||
use sp_io::{TestExternalities, hashing::{twox_64, twox_128, blake2_128}};
|
||||
|
||||
mod no_instance {
|
||||
use codec::{Encode, Decode, EncodeLike};
|
||||
@@ -35,18 +35,15 @@ mod no_instance {
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map hasher(blake2_256) u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
pub Map: map hasher(blake2_128_concat) u32 => u32;
|
||||
pub Map2: map hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32;
|
||||
pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option<u32>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,18 +62,15 @@ mod instance {
|
||||
{
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map hasher(blake2_256) u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
pub Map: map hasher(blake2_128_concat) u32 => u32;
|
||||
pub Map2: map hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32;
|
||||
pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option<u32>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
// See `decl_storage` limitation.
|
||||
@@ -85,6 +79,18 @@ mod instance {
|
||||
}
|
||||
}
|
||||
|
||||
fn twox_64_concat(d: &[u8]) -> Vec<u8> {
|
||||
let mut v = twox_64(d).to_vec();
|
||||
v.extend_from_slice(d);
|
||||
v
|
||||
}
|
||||
|
||||
fn blake2_128_concat(d: &[u8]) -> Vec<u8> {
|
||||
let mut v = blake2_128(d).to_vec();
|
||||
v.extend_from_slice(d);
|
||||
v
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_keys_no_instance() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
@@ -94,41 +100,27 @@ fn final_keys_no_instance() {
|
||||
|
||||
no_instance::Map::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<no_instance::Map>::final_prefix());
|
||||
|
||||
no_instance::Map2::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<no_instance::Map2>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"FinalKeysNone"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
no_instance::LinkedMap::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
no_instance::LinkedMap2::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
no_instance::DoubleMap::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<no_instance::DoubleMap>::final_prefix());
|
||||
|
||||
no_instance::DoubleMap2::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<no_instance::DoubleMap2>::final_prefix());
|
||||
});
|
||||
@@ -143,41 +135,27 @@ fn final_keys_default_instance() {
|
||||
|
||||
<instance::Map<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
<instance::Map2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map2<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"FinalKeysSome"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
<instance::LinkedMap<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
<instance::DoubleMap2<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap2<instance::DefaultInstance>>::final_prefix());
|
||||
});
|
||||
@@ -192,41 +170,27 @@ fn final_keys_instance_2() {
|
||||
|
||||
<instance::Map<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map<instance::Instance2>>::final_prefix());
|
||||
|
||||
<instance::Map2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map2<instance::Instance2>>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
<instance::LinkedMap<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap<instance::Instance2>>::final_prefix());
|
||||
|
||||
<instance::DoubleMap2<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap2<instance::Instance2>>::final_prefix());
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ frame_support::decl_module! {
|
||||
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
pub AppendableDM config(t): double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec<u32>;
|
||||
pub AppendableDM config(t): double_map hasher(identity) u32, hasher(identity) T::BlockNumber => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use frame_support::{
|
||||
DecodeDifferent, StorageMetadata, StorageEntryModifier, StorageEntryType, DefaultByteGetter,
|
||||
StorageEntryMetadata, StorageHasher,
|
||||
},
|
||||
StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap,
|
||||
StorageValue, StorageMap, StorageDoubleMap,
|
||||
};
|
||||
use sp_inherents::{ProvideInherent, InherentData, InherentIdentifier, MakeFatalError};
|
||||
use sp_core::{H256, sr25519};
|
||||
@@ -67,8 +67,7 @@ mod module1 {
|
||||
T::BlockNumber: From<u32> + std::fmt::Display
|
||||
{
|
||||
pub Value config(value): T::GenericType;
|
||||
pub Map: map hasher(blake2_256) u32 => u64;
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u64;
|
||||
pub Map: map hasher(identity) u32 => u64;
|
||||
}
|
||||
|
||||
add_extra_genesis {
|
||||
@@ -136,9 +135,8 @@ mod module2 {
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Module2 {
|
||||
pub Value config(value): T::Amount;
|
||||
pub Map config(map): map hasher(blake2_256) u64 => u64;
|
||||
pub LinkedMap config(linked_map): linked_map hasher(blake2_256) u64 => Vec<u8>;
|
||||
pub DoubleMap config(double_map): double_map hasher(blake2_256) u64, hasher(blake2_256) u64 => u64;
|
||||
pub Map config(map): map hasher(identity) u64 => u64;
|
||||
pub DoubleMap config(double_map): double_map hasher(identity) u64, hasher(identity) u64 => u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,13 +283,11 @@ fn new_test_ext() -> sp_io::TestExternalities {
|
||||
module2: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance1: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance2: None,
|
||||
@@ -314,17 +310,13 @@ fn storage_instance_independence() {
|
||||
module2::Map::<module2::Instance1>::insert(0, 0);
|
||||
module2::Map::<module2::Instance2>::insert(0, 0);
|
||||
module2::Map::<module2::Instance3>::insert(0, 0);
|
||||
module2::LinkedMap::<module2::DefaultInstance>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance1>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance2>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance3>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::DoubleMap::<module2::DefaultInstance>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance1>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance2>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance3>::insert(&0, &0, &0);
|
||||
});
|
||||
// 16 storage values + 4 linked_map head.
|
||||
assert_eq!(storage.top.len(), 16 + 4);
|
||||
// 12 storage values.
|
||||
assert_eq!(storage.top.len(), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -332,7 +324,6 @@ fn storage_with_instance_basic_operation() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type Value = module2::Value<Runtime, module2::Instance1>;
|
||||
type Map = module2::Map<module2::Instance1>;
|
||||
type LinkedMap = module2::LinkedMap<module2::Instance1>;
|
||||
type DoubleMap = module2::DoubleMap<module2::Instance1>;
|
||||
|
||||
assert_eq!(Value::exists(), true);
|
||||
@@ -360,26 +351,6 @@ fn storage_with_instance_basic_operation() {
|
||||
assert_eq!(Map::contains_key(key), false);
|
||||
assert_eq!(Map::get(key), 0);
|
||||
|
||||
assert_eq!(LinkedMap::contains_key(0), true);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
LinkedMap::insert(key, vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![1]);
|
||||
assert_eq!(LinkedMap::take(key), vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
LinkedMap::mutate(key, |a| *a=vec![2]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![2]);
|
||||
LinkedMap::remove(key);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
LinkedMap::insert(key, &vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
|
||||
let key1 = 1;
|
||||
let key2 = 1;
|
||||
assert_eq!(DoubleMap::contains_key(&0, &0), true);
|
||||
@@ -416,10 +387,10 @@ const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
name: DecodeDifferent::Encode("Map"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Identity,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
@@ -430,30 +401,12 @@ const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LinkedMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("Vec<u8>"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructLinkedMap(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("DoubleMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Identity,
|
||||
key2_hasher: StorageHasher::Identity,
|
||||
key1: DecodeDifferent::Encode("u64"),
|
||||
key2: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
|
||||
@@ -109,7 +109,7 @@ mod module {
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}): map hasher(blake2_256) Role => Option<RoleParameters<T>>;
|
||||
}): map hasher(blake2_128_concat) Role => Option<RoleParameters<T>>;
|
||||
|
||||
/// the roles members can enter into
|
||||
pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| {
|
||||
@@ -125,11 +125,11 @@ mod module {
|
||||
|
||||
/// actor accounts associated with a role
|
||||
pub AccountIdsByRole get(fn account_ids_by_role):
|
||||
map hasher(blake2_256) Role => Vec<T::AccountId>;
|
||||
map hasher(blake2_128_concat) Role => Vec<T::AccountId>;
|
||||
|
||||
/// tokens locked until given block number
|
||||
pub Bondage get(fn bondage):
|
||||
map hasher(blake2_256) T::AccountId => T::BlockNumber;
|
||||
map hasher(blake2_128_concat) T::AccountId => T::BlockNumber;
|
||||
|
||||
/// First step before enter a role is registering intent with a new account/key.
|
||||
/// This is done by sending a role_entry_request() from the new account.
|
||||
|
||||
Reference in New Issue
Block a user