From 4982d1c2e5b01048bc6012472ed6bc77bf2a4999 Mon Sep 17 00:00:00 2001
From: Svyatoslav Nikolsky <svyatonik@gmail.com>
Date: Thu, 9 Feb 2023 14:09:45 +0300
Subject: [PATCH] MaxValues for maps in parachain maps (#1868)

* MaxValues for maps in parachain maps

* fix compilation
---
 bridges/modules/grandpa/src/lib.rs        |   6 +
 bridges/modules/parachains/src/lib.rs     |  70 +++++++++--
 bridges/modules/parachains/src/mock.rs    |   2 +
 bridges/modules/parachains/src/weights.rs | 142 +++++++++++-----------
 bridges/primitives/parachains/src/lib.rs  |  17 ++-
 5 files changed, 153 insertions(+), 84 deletions(-)

diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs
index 5063ea31438..09b7f55a73c 100644
--- a/bridges/modules/grandpa/src/lib.rs
+++ b/bridges/modules/grandpa/src/lib.rs
@@ -662,6 +662,7 @@ mod tests {
 		assert_err, assert_noop, assert_ok, dispatch::PostDispatchInfo,
 		storage::generator::StorageValue,
 	};
+	use sp_core::Get;
 	use sp_runtime::{Digest, DigestItem, DispatchError};
 
 	fn initialize_substrate_bridge() {
@@ -1242,4 +1243,9 @@ mod tests {
 	}
 
 	generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
+
+	#[test]
+	fn maybe_headers_to_keep_returns_correct_value() {
+		assert_eq!(MaybeHeadersToKeep::<TestRuntime, ()>::get(), Some(mock::HeadersToKeep::get()));
+	}
 }
diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs
index e2f2cbf2d49..1f060e675cb 100644
--- a/bridges/modules/parachains/src/lib.rs
+++ b/bridges/modules/parachains/src/lib.rs
@@ -221,27 +221,39 @@ pub mod pallet {
 	/// - the head of the `ImportedParaHashes` ring buffer
 	#[pallet::storage]
 	pub type ParasInfo<T: Config<I>, I: 'static = ()> = StorageMap<
-		_,
-		<ParasInfoKeyProvider as StorageMapKeyProvider>::Hasher,
-		<ParasInfoKeyProvider as StorageMapKeyProvider>::Key,
-		<ParasInfoKeyProvider as StorageMapKeyProvider>::Value,
+		Hasher = <ParasInfoKeyProvider as StorageMapKeyProvider>::Hasher,
+		Key = <ParasInfoKeyProvider as StorageMapKeyProvider>::Key,
+		Value = <ParasInfoKeyProvider as StorageMapKeyProvider>::Value,
+		QueryKind = OptionQuery,
+		OnEmpty = GetDefault,
+		MaxValues = MaybeMaxParachains<T, I>,
 	>;
 
 	/// State roots of parachain heads which have been imported into the pallet.
 	#[pallet::storage]
 	pub type ImportedParaHeads<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
-		_,
-		<ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher1,
-		<ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key1,
-		<ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher2,
-		<ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key2,
-		StoredParaHeadDataOf<T, I>,
+		Hasher1 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher1,
+		Key1 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key1,
+		Hasher2 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Hasher2,
+		Key2 = <ImportedParaHeadsKeyProvider as StorageDoubleMapKeyProvider>::Key2,
+		Value = StoredParaHeadDataOf<T, I>,
+		QueryKind = OptionQuery,
+		OnEmpty = GetDefault,
+		MaxValues = MaybeMaxTotalParachainHashes<T, I>,
 	>;
 
 	/// A ring buffer of imported parachain head hashes. Ordered by the insertion time.
 	#[pallet::storage]
-	pub(super) type ImportedParaHashes<T: Config<I>, I: 'static = ()> =
-		StorageDoubleMap<_, Blake2_128Concat, ParaId, Twox64Concat, u32, ParaHash>;
+	pub(super) type ImportedParaHashes<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
+		Hasher1 = Blake2_128Concat,
+		Key1 = ParaId,
+		Hasher2 = Twox64Concat,
+		Key2 = u32,
+		Value = ParaHash,
+		QueryKind = OptionQuery,
+		OnEmpty = GetDefault,
+		MaxValues = MaybeMaxTotalParachainHashes<T, I>,
+	>;
 
 	#[pallet::pallet]
 	#[pallet::generate_store(pub(super) trait Store)]
@@ -646,6 +658,27 @@ pub mod pallet {
 			}
 		}
 	}
+
+	/// Returns maximal number of parachains, supported by the pallet.
+	pub struct MaybeMaxParachains<T, I>(PhantomData<(T, I)>);
+
+	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxParachains<T, I> {
+		fn get() -> Option<u32> {
+			Some(T::ParaStoredHeaderDataBuilder::supported_parachains())
+		}
+	}
+
+	/// Returns total number of all parachains hashes/heads, stored by the pallet.
+	pub struct MaybeMaxTotalParachainHashes<T, I>(PhantomData<(T, I)>);
+
+	impl<T: Config<I>, I: 'static> Get<Option<u32>> for MaybeMaxTotalParachainHashes<T, I> {
+		fn get() -> Option<u32> {
+			Some(
+				T::ParaStoredHeaderDataBuilder::supported_parachains()
+					.saturating_mul(T::HeadsToKeep::get()),
+			)
+		}
+	}
 }
 
 /// Single parachain header chain adapter.
@@ -1525,4 +1558,17 @@ mod tests {
 	}
 
 	generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
+
+	#[test]
+	fn maybe_max_parachains_returns_correct_value() {
+		assert_eq!(MaybeMaxParachains::<TestRuntime, ()>::get(), Some(mock::TOTAL_PARACHAINS));
+	}
+
+	#[test]
+	fn maybe_max_total_parachain_hashes_returns_correct_value() {
+		assert_eq!(
+			MaybeMaxTotalParachainHashes::<TestRuntime, ()>::get(),
+			Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()),
+		);
+	}
 }
diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs
index 118a9b1d315..8248964263c 100644
--- a/bridges/modules/parachains/src/mock.rs
+++ b/bridges/modules/parachains/src/mock.rs
@@ -38,6 +38,8 @@ pub const PARAS_PALLET_NAME: &str = "Paras";
 pub const UNTRACKED_PARACHAIN_ID: u32 = 10;
 // use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size`
 pub const MAXIMAL_PARACHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32;
+// total parachains that we use in tests
+pub const TOTAL_PARACHAINS: u32 = 4;
 
 pub type RegularParachainHeader = sp_runtime::testing::Header;
 pub type RegularParachainHasher = BlakeTwo256;
diff --git a/bridges/modules/parachains/src/weights.rs b/bridges/modules/parachains/src/weights.rs
index f6dc73d40c3..5ee1e4d3621 100644
--- a/bridges/modules/parachains/src/weights.rs
+++ b/bridges/modules/parachains/src/weights.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for pallet_bridge_parachains
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-02-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
 //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
@@ -67,33 +67,33 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	///
 	/// The range of component `p` is `[1, 2]`.
 	fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 35_348 nanoseconds.
-		Weight::from_parts(36_906_961, 8113)
-			// Standard Error: 136_143
-			.saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into()))
+		//  Estimated: `5143`
+		// Minimum execution time: 35_160 nanoseconds.
+		Weight::from_parts(36_951_585, 5143)
+			// Standard Error: 336_932
+			.saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into()))
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -104,29 +104,29 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_1kb_proof() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 43_295 nanoseconds.
-		Weight::from_parts(48_018_000, 8113)
+		//  Estimated: `5143`
+		// Minimum execution time: 42_276 nanoseconds.
+		Weight::from_parts(43_525_000, 5143)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -137,29 +137,29 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_16kb_proof() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 86_112 nanoseconds.
-		Weight::from_parts(88_901_000, 8113)
+		//  Estimated: `5143`
+		// Minimum execution time: 85_824 nanoseconds.
+		Weight::from_parts(87_335_000, 5143)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -174,33 +174,33 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	///
 	/// The range of component `p` is `[1, 2]`.
 	fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 35_348 nanoseconds.
-		Weight::from_parts(36_906_961, 8113)
-			// Standard Error: 136_143
-			.saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into()))
+		//  Estimated: `5143`
+		// Minimum execution time: 35_160 nanoseconds.
+		Weight::from_parts(36_951_585, 5143)
+			// Standard Error: 336_932
+			.saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into()))
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
@@ -211,29 +211,29 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_1kb_proof() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 43_295 nanoseconds.
-		Weight::from_parts(48_018_000, 8113)
+		//  Estimated: `5143`
+		// Minimum execution time: 42_276 nanoseconds.
+		Weight::from_parts(43_525_000, 5143)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
@@ -244,29 +244,29 @@ impl WeightInfo for () {
 	///
 	/// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0)
 	///
-	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added:
-	/// 2543, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
+	/// added: 2048, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535,
-	/// mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added:
+	/// 555, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64),
-	/// added: 2539, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size:
+	/// Some(64), added: 2044, mode: MaxEncodedLen)
 	///
 	/// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1)
 	///
-	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196),
-	/// added: 2671, mode: MaxEncodedLen)
+	/// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size:
+	/// Some(196), added: 2176, mode: MaxEncodedLen)
 	fn submit_parachain_heads_with_16kb_proof() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `366`
-		//  Estimated: `8113`
-		// Minimum execution time: 86_112 nanoseconds.
-		Weight::from_parts(88_901_000, 8113)
+		//  Estimated: `5143`
+		// Minimum execution time: 85_824 nanoseconds.
+		Weight::from_parts(87_335_000, 5143)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs
index 388a995fae6..e619fc7b641 100644
--- a/bridges/primitives/parachains/src/lib.rs
+++ b/bridges/primitives/parachains/src/lib.rs
@@ -115,7 +115,10 @@ impl ParaStoredHeaderData {
 
 /// Stored parachain head data builder.
 pub trait ParaStoredHeaderDataBuilder {
-	/// Try to build head data from self.
+	/// Return number of parachains that are supported by this builder.
+	fn supported_parachains() -> u32;
+
+	/// Try to build head data from encoded head of parachain with given id.
 	fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option<ParaStoredHeaderData>;
 }
 
@@ -123,6 +126,10 @@ pub trait ParaStoredHeaderDataBuilder {
 pub struct SingleParaStoredHeaderDataBuilder<C: Parachain>(PhantomData<C>);
 
 impl<C: Parachain> ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder<C> {
+	fn supported_parachains() -> u32 {
+		1
+	}
+
 	fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option<ParaStoredHeaderData> {
 		if para_id == ParaId(C::PARACHAIN_ID) {
 			let header = HeaderOf::<C>::decode(&mut &para_head.0[..]).ok()?;
@@ -139,6 +146,14 @@ impl<C: Parachain> ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBui
 #[impl_trait_for_tuples::impl_for_tuples(1, 30)]
 #[tuple_types_custom_trait_bound(Parachain)]
 impl ParaStoredHeaderDataBuilder for C {
+	fn supported_parachains() -> u32 {
+		let mut result = 0;
+		for_tuples!( #(
+			result += SingleParaStoredHeaderDataBuilder::<C>::supported_parachains();
+		)* );
+		result
+	}
+
 	fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option<ParaStoredHeaderData> {
 		for_tuples!( #(
 			let maybe_para_head = SingleParaStoredHeaderDataBuilder::<C>::try_build(para_id, para_head);
-- 
GitLab