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