Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
implement DoubleMap
Browse files Browse the repository at this point in the history
  • Loading branch information
gui1117 committed Mar 20, 2020
1 parent 9af109d commit 76efb19
Show file tree
Hide file tree
Showing 10 changed files with 587 additions and 453 deletions.
2 changes: 0 additions & 2 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ use proc_macro::TokenStream;
/// * Map: `Foo: map hasher($hash) type => type`: Implements the
/// [`StorageMap`](../frame_support/storage/trait.StorageMap.html) trait using the
/// [`StorageMap generator`](../frame_support/storage/generator/trait.StorageMap.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. You will generally want to use one
Expand Down Expand Up @@ -106,7 +105,6 @@ use proc_macro::TokenStream;
/// * 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).
/// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html).
///
/// `$hash1` and `$hash2` representing choices of hashing algorithms available in the
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be chosen with care, see
Expand Down
1 change: 0 additions & 1 deletion frame/support/procedural/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,6 @@ pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStr
StorageValue as _,
StorageMap as _,
StorageDoubleMap as _,
StoragePrefixedMap as _,
};

#scrate_decl
Expand Down
24 changes: 0 additions & 24 deletions frame/support/procedural/src/storage/storage_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,6 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
StorageLineTypeDef::Map(map) => {
let hasher = map.hasher.to_storage_hasher_struct();
quote!(
impl<#impl_trait> #scrate::storage::StoragePrefixedMap<#value_type>
for #storage_struct #optional_storage_where_clause
{
fn module_prefix() -> &'static [u8] {
#instance_or_inherent::PREFIX.as_bytes()
}

fn storage_prefix() -> &'static [u8] {
#storage_name_str.as_bytes()
}
}

impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
#optional_storage_where_clause
{
Expand Down Expand Up @@ -162,18 +150,6 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
let hasher1 = map.hasher1.to_storage_hasher_struct();
let hasher2 = map.hasher2.to_storage_hasher_struct();
quote!(
impl<#impl_trait> #scrate::storage::StoragePrefixedMap<#value_type>
for #storage_struct #optional_storage_where_clause
{
fn module_prefix() -> &'static [u8] {
#instance_or_inherent::PREFIX.as_bytes()
}

fn storage_prefix() -> &'static [u8] {
#storage_name_str.as_bytes()
}
}

impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
#optional_storage_where_clause
{
Expand Down
113 changes: 112 additions & 1 deletion frame/support/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Hash utilities.
use codec::Codec;
use codec::{Codec, Decode};
use sp_std::prelude::Vec;
use sp_io::hashing::{blake2_128, blake2_256, twox_64, twox_128, twox_256};

Expand Down Expand Up @@ -64,6 +64,20 @@ pub trait ReversibleStorageHasher: StorageHasher {
fn reverse(x: &[u8]) -> &[u8];
}

/// Trait to retrieve some info from hash of type `Key` encoded.
pub trait StorageHasherInfo<Key> {
/// Some info contained in the hash of type `Key` encoded.
type Info;

/// Decode the hash and then decode the info from the decoded hash.
///
/// # WARNING
///
/// Even if info is (), input must be modified to have read the entire encoded hash.
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>;
}

/// Store the key directly.
pub struct Identity;
impl StorageHasher for Identity {
Expand All @@ -77,6 +91,14 @@ impl ReversibleStorageHasher for Identity {
x
}
}
impl<Key: Decode> StorageHasherInfo<Key> for Identity {
type Info = Key;
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
Key::decode(input)
}
}

/// Hash storage keys with `concat(twox64(key), key)`
pub struct Twox64Concat;
Expand All @@ -95,6 +117,15 @@ impl ReversibleStorageHasher for Twox64Concat {
&x[8..]
}
}
impl<Key: Decode> StorageHasherInfo<Key> for Twox64Concat {
type Info = Key;
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 8])?;
Key::decode(input)
}
}

/// Hash storage keys with `concat(blake2_128(key), key)`
pub struct Blake2_128Concat;
Expand All @@ -113,6 +144,15 @@ impl ReversibleStorageHasher for Blake2_128Concat {
&x[16..]
}
}
impl<Key: Decode> StorageHasherInfo<Key> for Blake2_128Concat {
type Info = Key;
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 16])?;
Key::decode(input)
}
}

/// Hash storage keys with blake2 128
pub struct Blake2_128;
Expand All @@ -122,6 +162,15 @@ impl StorageHasher for Blake2_128 {
blake2_128(x)
}
}
impl<Key> StorageHasherInfo<Key> for Blake2_128 {
type Info = ();
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 16])?;
Ok(())
}
}

/// Hash storage keys with blake2 256
pub struct Blake2_256;
Expand All @@ -131,6 +180,15 @@ impl StorageHasher for Blake2_256 {
blake2_256(x)
}
}
impl<Key> StorageHasherInfo<Key> for Blake2_256 {
type Info = ();
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 32])?;
Ok(())
}
}

/// Hash storage keys with twox 128
pub struct Twox128;
Expand All @@ -140,6 +198,15 @@ impl StorageHasher for Twox128 {
twox_128(x)
}
}
impl<Key> StorageHasherInfo<Key> for Twox128 {
type Info = ();
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 16])?;
Ok(())
}
}

/// Hash storage keys with twox 256
pub struct Twox256;
Expand All @@ -149,10 +216,20 @@ impl StorageHasher for Twox256 {
twox_256(x)
}
}
impl<Key> StorageHasherInfo<Key> for Twox256 {
type Info = ();
fn decode_hash_and_then_info<I: codec::Input>(input: &mut I)
-> Result<Self::Info, codec::Error>
{
input.read(&mut [0u8; 32])?;
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use codec::Encode;

#[test]
fn test_twox_64_concat() {
Expand All @@ -165,4 +242,38 @@ mod tests {
let r = Blake2_128Concat::hash(b"foo");
assert_eq!(r.split_at(16), (&blake2_128(b"foo")[..], &b"foo"[..]))
}

#[test]
fn test_storage_hasher_info() {
type KeyType = [u8; 15];
let key: KeyType = [3u8; 15];

let mut r = &Identity::hash(&(&key).encode()[..])[..];
assert_eq!(<Identity as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(key));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Twox64Concat::hash(&(&key).encode()[..])[..];
assert_eq!(<Twox64Concat as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(key));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Twox128::hash(&(&key).encode()[..])[..];
assert_eq!(<Twox128 as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(()));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Twox256::hash(&(&key).encode()[..])[..];
assert_eq!(<Twox256 as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(()));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Blake2_128Concat::hash(&(&key).encode()[..])[..];
assert_eq!(<Blake2_128Concat as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(key));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Blake2_128::hash(&(&key).encode()[..])[..];
assert_eq!(<Blake2_128 as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(()));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.

let mut r = &Blake2_256::hash(&(&key).encode()[..])[..];
assert_eq!(<Blake2_256 as StorageHasherInfo<KeyType>>::decode_hash_and_then_info(&mut r), Ok(()));
assert_eq!(r.len(), 0); // Assert input has indeed decoded the hash.
}
}
27 changes: 12 additions & 15 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ pub use self::hash::{
Twox256, Twox128, Blake2_256, Blake2_128, Identity, Twox64Concat, Blake2_128Concat, Hashable,
StorageHasher
};
pub use self::storage::{
StorageValue, StorageMap, StorageDoubleMap, StoragePrefixedMap, IterableStorageMap,
IterableStorageDoubleMap,
};
pub use self::storage::{StorageValue, StorageMap, StorageDoubleMap};
pub use self::dispatch::{Parameter, Callable, IsSubType};
pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};

Expand Down Expand Up @@ -313,7 +310,7 @@ mod tests {
OptionLinkedMap::insert(2, 2);
OptionLinkedMap::insert(3, 3);

let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
let collect = || OptionLinkedMap::iter_key_value().collect::<Vec<_>>().sorted();
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);

// Two existing
Expand Down Expand Up @@ -393,43 +390,43 @@ mod tests {
#[test]
fn map_iteration_should_work() {
new_test_ext().execute_with(|| {
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
// insert / remove
let key = 17u32;
Map::insert(key, 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
assert_eq!(Map::take(&15), 42u64);
assert_eq!(Map::take(&key), 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![]);

// Add couple of more elements
Map::insert(key, 42u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
Map::insert(key + 1, 43u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);

// mutate
let key = key + 2;
Map::mutate(&key, |val| {
*val = 15;
});
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 15)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 15)]);
Map::mutate(&key, |val| {
*val = 17;
});
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 17)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 17)]);

// remove first
Map::remove(&key);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);

// remove last from the list
Map::remove(&(key - 2));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);

// remove the last element
Map::remove(&(key - 1));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
assert_eq!(Map::iter_key_value().collect::<Vec<_>>().sorted(), vec![]);
});
}

Expand Down
Loading

0 comments on commit 76efb19

Please sign in to comment.