From fb1bd5735cc765b1b109489d3a440dcb0e3e0038 Mon Sep 17 00:00:00 2001 From: Keith Yeung <kungfukeith11@gmail.com> Date: Mon, 28 Jun 2021 02:17:28 -0700 Subject: [PATCH] Support NMap in generate_storage_alias (#9147) * Support NMap in generate_storage_alias * Verify that 2-key NMap is identical to DoubleMap * Also compare key hashes and make sure they're identical * Fix and add tests for 1-tuple NMap generated by generate_storage_alias --- substrate/frame/support/src/lib.rs | 31 ++++++++++++++++++- .../support/src/storage/generator/nmap.rs | 20 ++++++++++++ .../frame/support/src/storage/types/key.rs | 4 +-- .../frame/support/src/storage/types/nmap.rs | 24 +++++++++----- 4 files changed, 69 insertions(+), 10 deletions(-) diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 45988c1c737..638485360c5 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -100,7 +100,8 @@ impl TypeId for PalletId { } /// Generate a new type alias for [`storage::types::StorageValue`], -/// [`storage::types::StorageMap`] and [`storage::types::StorageDoubleMap`]. +/// [`storage::types::StorageMap`], [`storage::types::StorageDoubleMap`] +/// and [`storage::types::StorageNMap`]. /// /// Useful for creating a *storage-like* struct for test and migrations. /// @@ -154,6 +155,18 @@ macro_rules! generate_storage_alias { >; } }; + ($pallet:ident, $name:ident => NMap<$(($key:ty, $hasher:ty),)+ $value:ty>) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + type $name = $crate::storage::types::StorageNMap< + [<$name Instance>], + ( + $( $crate::storage::types::Key<$hasher, $key>, )+ + ), + $value, + >; + } + }; ($pallet:ident, $name:ident => Value<$value:ty>) => { $crate::paste::paste! { $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); @@ -193,6 +206,22 @@ macro_rules! generate_storage_alias { >; } }; + ( + $pallet:ident, + $name:ident<$t:ident : $bounds:tt> => NMap<$(($key:ty, $hasher:ty),)+ $value:ty> + ) => { + $crate::paste::paste! { + $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); + #[allow(type_alias_bounds)] + type $name<$t : $bounds> = $crate::storage::types::StorageNMap< + [<$name Instance>], + ( + $( $crate::storage::types::Key<$hasher, $key>, )+ + ), + $value, + >; + } + }; ($pallet:ident, $name:ident<$t:ident : $bounds:tt> => Value<$value:ty>) => { $crate::paste::paste! { $crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name); diff --git a/substrate/frame/support/src/storage/generator/nmap.rs b/substrate/frame/support/src/storage/generator/nmap.rs index 62f188a26db..7a320adcaab 100755 --- a/substrate/frame/support/src/storage/generator/nmap.rs +++ b/substrate/frame/support/src/storage/generator/nmap.rs @@ -433,6 +433,26 @@ mod test_iterators { prefix } + #[test] + fn n_map_double_map_identical_key() { + sp_io::TestExternalities::default().execute_with(|| { + NMap::insert((1, 2), 50); + let key_hash = NMap::hashed_key_for((1, 2)); + + { + crate::generate_storage_alias!(Test, NMap => DoubleMap< + (u16, crate::Blake2_128Concat), + (u32, crate::Twox64Concat), + u64 + >); + + let value = NMap::get(1, 2).unwrap(); + assert_eq!(value, 50); + assert_eq!(NMap::hashed_key_for(1, 2), key_hash); + } + }); + } + #[test] fn n_map_reversible_reversible_iteration() { sp_io::TestExternalities::default().execute_with(|| { diff --git a/substrate/frame/support/src/storage/types/key.rs b/substrate/frame/support/src/storage/types/key.rs index a770d1b0fce..def800f62c5 100755 --- a/substrate/frame/support/src/storage/types/key.rs +++ b/substrate/frame/support/src/storage/types/key.rs @@ -110,7 +110,7 @@ impl<H: StorageHasher, K: FullCodec> KeyGeneratorInner for Key<H, K> { } } -#[impl_trait_for_tuples::impl_for_tuples(2, 18)] +#[impl_trait_for_tuples::impl_for_tuples(1, 18)] #[tuple_types_custom_trait_bound(KeyGeneratorInner)] impl KeyGenerator for Tuple { for_tuples!( type Key = ( #(Tuple::Key),* ); ); @@ -150,7 +150,7 @@ impl KeyGenerator for Tuple { } } -#[impl_trait_for_tuples::impl_for_tuples(2, 18)] +#[impl_trait_for_tuples::impl_for_tuples(1, 18)] #[tuple_types_custom_trait_bound(KeyGeneratorInner + KeyGeneratorMaxEncodedLen)] impl KeyGeneratorMaxEncodedLen for Tuple { fn key_max_encoded_len() -> usize { diff --git a/substrate/frame/support/src/storage/types/nmap.rs b/substrate/frame/support/src/storage/types/nmap.rs index a9fc121d42d..fd1ca47b32c 100755 --- a/substrate/frame/support/src/storage/types/nmap.rs +++ b/substrate/frame/support/src/storage/types/nmap.rs @@ -423,7 +423,7 @@ mod test { fn pallet_prefix() -> &'static str { "test" } - const STORAGE_PREFIX: &'static str = "foo"; + const STORAGE_PREFIX: &'static str = "Foo"; } struct ADefault; @@ -445,7 +445,7 @@ mod test { TestExternalities::default().execute_with(|| { let mut k: Vec<u8> = vec![]; k.extend(&twox_128(b"test")); - k.extend(&twox_128(b"foo")); + k.extend(&twox_128(b"Foo")); k.extend(&3u16.blake2_128_concat()); assert_eq!(A::hashed_key_for((&3,)).to_vec(), k); @@ -458,6 +458,16 @@ mod test { assert_eq!(A::get((3,)), Some(10)); assert_eq!(AValueQueryWithAnOnEmpty::get((3,)), 10); + { + crate::generate_storage_alias!(test, Foo => NMap< + (u16, Blake2_128Concat), + u32 + >); + + assert_eq!(Foo::contains_key((3,)), true); + assert_eq!(Foo::get((3,)), Some(10)); + } + A::swap::<Key<Blake2_128Concat, u16>, _, _>((3,), (2,)); assert_eq!(A::contains_key((3,)), false); assert_eq!(A::contains_key((2,)), true); @@ -575,7 +585,7 @@ mod test { AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default ); - assert_eq!(A::NAME, "foo"); + assert_eq!(A::NAME, "Foo"); assert_eq!( AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 98u32.encode() @@ -617,7 +627,7 @@ mod test { TestExternalities::default().execute_with(|| { let mut k: Vec<u8> = vec![]; k.extend(&twox_128(b"test")); - k.extend(&twox_128(b"foo")); + k.extend(&twox_128(b"Foo")); k.extend(&3u16.blake2_128_concat()); k.extend(&30u8.twox_64_concat()); assert_eq!(A::hashed_key_for((3, 30)).to_vec(), k); @@ -761,7 +771,7 @@ mod test { AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default ); - assert_eq!(A::NAME, "foo"); + assert_eq!(A::NAME, "Foo"); assert_eq!( AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 98u32.encode() @@ -844,7 +854,7 @@ mod test { TestExternalities::default().execute_with(|| { let mut k: Vec<u8> = vec![]; k.extend(&twox_128(b"test")); - k.extend(&twox_128(b"foo")); + k.extend(&twox_128(b"Foo")); k.extend(&1u16.blake2_128_concat()); k.extend(&10u16.blake2_128_concat()); k.extend(&100u16.twox_64_concat()); @@ -996,7 +1006,7 @@ mod test { AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default ); - assert_eq!(A::NAME, "foo"); + assert_eq!(A::NAME, "Foo"); assert_eq!( AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 98u32.encode() -- GitLab