From 9ecb2d3391ee27f1d6c8af1a53416f8224653fd9 Mon Sep 17 00:00:00 2001
From: Svyatoslav Nikolsky <svyatonik@gmail.com>
Date: Wed, 13 Dec 2023 12:31:12 +0300
Subject: [PATCH] Tests for BridgeHub(s) <> remote GRANDPA chain (#2692)

So far the `bridge-hub-test-utils` contained a tests set for testing
BridgeHub runtime that is bridging with the remote **parachain**. But we
have https://github.com/paritytech/polkadot-sdk/pull/2540 coming, which
would add Rococo <> Bulletin chain bridge (where Bulletin = standalone
chain that is using GRANDPA finality). Then it'll be expanded to
Polkadot BH as well.

So this PR adds the same set of tests to the `bridge-hub-test-utils`,
but for the case when remote chain is the chain with GRANDPA finality.
There's a lot of changes in this PR - I'll describe some:
- I've added `BasicParachainRuntime` trait to decrease number of lines
we need to add to `where` clause. Could revert, but imo it is useful;
- `cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data` is
a submodule for generating test data for the test sets.
`from_parachain.rs` is used in tests for the case when remote chain is a
parachain, `from_grandpa_chain.rs` - for the bridges with remote GRANDPA
chains. `mod.rs` has some code, shared by both types of tests;
- `cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data` is
a submodule with all test cases. The `mod.rs` has tests, suitable for
all cases. There's also `wth_parachain.rs` and `with_grandpa_chain.rs`
with the same meaning as above;
- I've merged the "core" code of two previous tests -
`relayed_incoming_message_works` and `complex_relay_extrinsic_works`
into one single `relayed_incoming_message_works` test. So now we are
always constructing extrinsics and are dispatching them using executive
module (meaning all signed extensions are also tested).

New test set is used here:
https://github.com/paritytech/polkadot-sdk/pull/2540. Once this PR is
merged, I'll merge that other PR with master to remove duplicate
changes.

I'm also planning to cleanup generic constraints + remove some
unnecessary assumptions about used chains in a follow-up PRs. But for
now I think this PR has enough changes, so don't want to complicate it
even more.

---

Breaking changes for the code that have used those tests before:
- the `construct_and_apply_extrinsic` callback now accepts the
`RuntimeCall` instead of the `pallet_utility::Call`;
- the `construct_and_apply_extrinsic` now may be called multiple times
for the single test, so make sure the `frame_system::CheckNonce` is
correctly constructed;
- all previous tests have been moved from
`bridge_hub_test_utils::test_cases` to
`bridge_hub_test_utils::test_cases::from_parachain` module;
- there are several changes in test arguments - please refer to
https://github.com/paritytech/polkadot-sdk/compare/sv-tests-for-bridge-with-remote-grandpa-chain?expand=1#diff-79a28d4d3e1749050341c2424f00c4c139825b1a20937767f83e58b95166735c
for details.
---
 Cargo.lock                                    |    2 +
 .../bridge-hub-rococo/tests/tests.rs          |   32 +-
 .../bridge-hub-westend/tests/tests.rs         |   32 +-
 .../bridge-hubs/test-utils/Cargo.toml         |    3 +
 .../bridge-hubs/test-utils/src/lib.rs         |    2 +
 .../bridge-hubs/test-utils/src/test_cases.rs  | 1588 -----------------
 .../src/test_cases/from_grandpa_chain.rs      |  442 +++++
 .../src/test_cases/from_parachain.rs          |  544 ++++++
 .../test-utils/src/test_cases/helpers.rs      |  340 ++++
 .../test-utils/src/test_cases/mod.rs          |  485 +++++
 .../src/test_data/from_grandpa_chain.rs       |  244 +++
 .../src/test_data/from_parachain.rs           |  326 ++++
 .../test-utils/src/test_data/mod.rs           |  144 ++
 .../parachains/runtimes/test-utils/src/lib.rs |   55 +-
 14 files changed, 2588 insertions(+), 1651 deletions(-)
 delete mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
 create mode 100644 cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs

diff --git a/Cargo.lock b/Cargo.lock
index 0e2194829c2..9e17e6803d9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1991,6 +1991,7 @@ dependencies = [
  "frame-executive",
  "frame-support",
  "frame-system",
+ "impl-trait-for-tuples",
  "log",
  "pallet-balances",
  "pallet-bridge-grandpa",
@@ -2009,6 +2010,7 @@ dependencies = [
  "sp-io",
  "sp-keyring",
  "sp-runtime",
+ "sp-std 8.0.0",
  "sp-tracing 10.0.0",
  "staging-parachain-info",
  "staging-xcm",
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
index 1d544a9dcfa..662012a4b41 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs
@@ -26,7 +26,6 @@ use bridge_hub_rococo_runtime::{
 };
 use codec::{Decode, Encode};
 use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8};
-use frame_system::pallet_prelude::HeaderFor;
 use parachains_common::{rococo::fee::WeightToFee, AccountId, AuraId, Balance};
 use sp_keyring::AccountKeyring::Alice;
 use sp_runtime::{
@@ -46,13 +45,16 @@ fn construct_extrinsic(
 	sender: sp_keyring::AccountKeyring,
 	call: RuntimeCall,
 ) -> UncheckedExtrinsic {
+	let account_id = AccountId32::from(sender.public());
 	let extra: SignedExtra = (
 		frame_system::CheckNonZeroSender::<Runtime>::new(),
 		frame_system::CheckSpecVersion::<Runtime>::new(),
 		frame_system::CheckTxVersion::<Runtime>::new(),
 		frame_system::CheckGenesis::<Runtime>::new(),
 		frame_system::CheckEra::<Runtime>::from(Era::immortal()),
-		frame_system::CheckNonce::<Runtime>::from(0),
+		frame_system::CheckNonce::<Runtime>::from(
+			frame_system::Pallet::<Runtime>::account(&account_id).nonce,
+		),
 		frame_system::CheckWeight::<Runtime>::new(),
 		pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
 		BridgeRejectObsoleteHeadersAndMessages::default(),
@@ -62,7 +64,7 @@ fn construct_extrinsic(
 	let signature = payload.using_encoded(|e| sender.sign(e));
 	UncheckedExtrinsic::new_signed(
 		call,
-		AccountId32::from(sender.public()).into(),
+		account_id.into(),
 		Signature::Sr25519(signature.clone()),
 		extra,
 	)
@@ -70,10 +72,9 @@ fn construct_extrinsic(
 
 fn construct_and_apply_extrinsic(
 	relayer_at_target: sp_keyring::AccountKeyring,
-	batch: pallet_utility::Call<Runtime>,
+	call: RuntimeCall,
 ) -> sp_runtime::DispatchOutcome {
-	let batch_call = RuntimeCall::Utility(batch);
-	let xt = construct_extrinsic(relayer_at_target, batch_call);
+	let xt = construct_extrinsic(relayer_at_target, call);
 	let r = Executive::apply_extrinsic(xt);
 	r.unwrap()
 }
@@ -85,10 +86,6 @@ fn construct_and_estimate_extrinsic_fee(batch: pallet_utility::Call<Runtime>) ->
 	TransactionPayment::compute_fee(xt.encoded_size() as _, &batch_info, 0)
 }
 
-fn executive_init_block(header: &HeaderFor<Runtime>) {
-	Executive::initialize_block(header)
-}
-
 fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
 	bridge_hub_test_utils::CollatorSessionKeys::new(
 		AccountId::from(Alice),
@@ -237,10 +234,9 @@ mod bridge_hub_rococo_tests {
 	#[test]
 	fn relayed_incoming_message_works() {
 		// from Westend
-		bridge_hub_test_utils::test_cases::relayed_incoming_message_works::<
+		bridge_hub_test_utils::test_cases::from_parachain::relayed_incoming_message_works::<
 			Runtime,
 			AllPalletsWithoutSystem,
-			XcmConfig,
 			ParachainSystem,
 			BridgeGrandpaWestendInstance,
 			BridgeParachainWestendInstance,
@@ -250,17 +246,19 @@ mod bridge_hub_rococo_tests {
 			collator_session_keys(),
 			bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
 			bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID,
+			BridgeHubWestendChainId::get(),
 			SIBLING_PARACHAIN_ID,
 			Rococo,
 			XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND,
 			|| (),
+			construct_and_apply_extrinsic,
 		)
 	}
 
 	#[test]
 	pub fn complex_relay_extrinsic_works() {
 		// for Westend
-		bridge_hub_test_utils::test_cases::complex_relay_extrinsic_works::<
+		bridge_hub_test_utils::test_cases::from_parachain::complex_relay_extrinsic_works::<
 			Runtime,
 			AllPalletsWithoutSystem,
 			XcmConfig,
@@ -277,10 +275,8 @@ mod bridge_hub_rococo_tests {
 			BridgeHubWestendChainId::get(),
 			Rococo,
 			XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND,
-			ExistentialDeposit::get(),
-			executive_init_block,
-			construct_and_apply_extrinsic,
 			|| (),
+			construct_and_apply_extrinsic,
 		);
 	}
 
@@ -304,7 +300,7 @@ mod bridge_hub_rococo_tests {
 
 	#[test]
 	pub fn can_calculate_fee_for_complex_message_delivery_transaction() {
-		let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_delivery_transaction::<
+		let estimated = bridge_hub_test_utils::test_cases::from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::<
 			Runtime,
 			BridgeGrandpaWestendInstance,
 			BridgeParachainWestendInstance,
@@ -327,7 +323,7 @@ mod bridge_hub_rococo_tests {
 
 	#[test]
 	pub fn can_calculate_fee_for_complex_message_confirmation_transaction() {
-		let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_confirmation_transaction::<
+		let estimated = bridge_hub_test_utils::test_cases::from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::<
 			Runtime,
 			BridgeGrandpaWestendInstance,
 			BridgeParachainWestendInstance,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
index 73bddb16369..ffa2fb1cfdc 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs
@@ -32,7 +32,6 @@ use bridge_to_rococo_config::{
 };
 use codec::{Decode, Encode};
 use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8};
-use frame_system::pallet_prelude::HeaderFor;
 use parachains_common::{westend::fee::WeightToFee, AccountId, AuraId, Balance};
 use sp_keyring::AccountKeyring::Alice;
 use sp_runtime::{
@@ -52,13 +51,16 @@ fn construct_extrinsic(
 	sender: sp_keyring::AccountKeyring,
 	call: RuntimeCall,
 ) -> UncheckedExtrinsic {
+	let account_id = AccountId32::from(sender.public());
 	let extra: SignedExtra = (
 		frame_system::CheckNonZeroSender::<Runtime>::new(),
 		frame_system::CheckSpecVersion::<Runtime>::new(),
 		frame_system::CheckTxVersion::<Runtime>::new(),
 		frame_system::CheckGenesis::<Runtime>::new(),
 		frame_system::CheckEra::<Runtime>::from(Era::immortal()),
-		frame_system::CheckNonce::<Runtime>::from(0),
+		frame_system::CheckNonce::<Runtime>::from(
+			frame_system::Pallet::<Runtime>::account(&account_id).nonce,
+		),
 		frame_system::CheckWeight::<Runtime>::new(),
 		pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
 		BridgeRejectObsoleteHeadersAndMessages::default(),
@@ -68,7 +70,7 @@ fn construct_extrinsic(
 	let signature = payload.using_encoded(|e| sender.sign(e));
 	UncheckedExtrinsic::new_signed(
 		call,
-		AccountId32::from(sender.public()).into(),
+		account_id.into(),
 		Signature::Sr25519(signature.clone()),
 		extra,
 	)
@@ -76,10 +78,9 @@ fn construct_extrinsic(
 
 fn construct_and_apply_extrinsic(
 	relayer_at_target: sp_keyring::AccountKeyring,
-	batch: pallet_utility::Call<Runtime>,
+	call: RuntimeCall,
 ) -> sp_runtime::DispatchOutcome {
-	let batch_call = RuntimeCall::Utility(batch);
-	let xt = construct_extrinsic(relayer_at_target, batch_call);
+	let xt = construct_extrinsic(relayer_at_target, call);
 	let r = Executive::apply_extrinsic(xt);
 	r.unwrap()
 }
@@ -91,10 +92,6 @@ fn construct_and_estimate_extrinsic_fee(batch: pallet_utility::Call<Runtime>) ->
 	TransactionPayment::compute_fee(xt.encoded_size() as _, &batch_info, 0)
 }
 
-fn executive_init_block(header: &HeaderFor<Runtime>) {
-	Executive::initialize_block(header)
-}
-
 fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
 	bridge_hub_test_utils::CollatorSessionKeys::new(
 		AccountId::from(Alice),
@@ -222,10 +219,9 @@ fn message_dispatch_routing_works() {
 
 #[test]
 fn relayed_incoming_message_works() {
-	bridge_hub_test_utils::test_cases::relayed_incoming_message_works::<
+	bridge_hub_test_utils::test_cases::from_parachain::relayed_incoming_message_works::<
 		Runtime,
 		AllPalletsWithoutSystem,
-		XcmConfig,
 		ParachainSystem,
 		BridgeGrandpaRococoInstance,
 		BridgeParachainRococoInstance,
@@ -235,16 +231,18 @@ fn relayed_incoming_message_works() {
 		collator_session_keys(),
 		bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID,
 		bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
+		BridgeHubRococoChainId::get(),
 		SIBLING_PARACHAIN_ID,
 		Westend,
 		XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO,
 		|| (),
+		construct_and_apply_extrinsic,
 	)
 }
 
 #[test]
 pub fn complex_relay_extrinsic_works() {
-	bridge_hub_test_utils::test_cases::complex_relay_extrinsic_works::<
+	bridge_hub_test_utils::test_cases::from_parachain::complex_relay_extrinsic_works::<
 		Runtime,
 		AllPalletsWithoutSystem,
 		XcmConfig,
@@ -261,10 +259,8 @@ pub fn complex_relay_extrinsic_works() {
 		BridgeHubRococoChainId::get(),
 		Westend,
 		XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO,
-		ExistentialDeposit::get(),
-		executive_init_block,
-		construct_and_apply_extrinsic,
 		|| (),
+		construct_and_apply_extrinsic,
 	);
 }
 
@@ -288,7 +284,7 @@ pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() {
 
 #[test]
 pub fn can_calculate_fee_for_complex_message_delivery_transaction() {
-	let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_delivery_transaction::<
+	let estimated = bridge_hub_test_utils::test_cases::from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::<
 		Runtime,
 		BridgeGrandpaRococoInstance,
 		BridgeParachainRococoInstance,
@@ -311,7 +307,7 @@ pub fn can_calculate_fee_for_complex_message_delivery_transaction() {
 
 #[test]
 pub fn can_calculate_fee_for_complex_message_confirmation_transaction() {
-	let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_confirmation_transaction::<
+	let estimated = bridge_hub_test_utils::test_cases::from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::<
 		Runtime,
 		BridgeGrandpaRococoInstance,
 		BridgeParachainRococoInstance,
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
index 7325a87165c..35cab04fcc4 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml
@@ -8,6 +8,7 @@ license = "Apache-2.0"
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] }
+impl-trait-for-tuples = "0.2"
 log = { version = "0.4.20", default-features = false }
 
 # Substrate
@@ -19,6 +20,7 @@ sp-core = { path = "../../../../../substrate/primitives/core", default-features
 sp-io = { path = "../../../../../substrate/primitives/io", default-features = false }
 sp-keyring = { path = "../../../../../substrate/primitives/keyring" }
 sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false }
+sp-std = { path = "../../../../../substrate/primitives/std", default-features = false }
 sp-tracing = { path = "../../../../../substrate/primitives/tracing" }
 pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false }
 pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false }
@@ -90,6 +92,7 @@ std = [
 	"sp-core/std",
 	"sp-io/std",
 	"sp-runtime/std",
+	"sp-std/std",
 	"xcm-builder/std",
 	"xcm-executor/std",
 	"xcm/std",
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
index 26eb09b73fa..445f001f1a4 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs
@@ -17,5 +17,7 @@
 //! Module contains predefined test-case scenarios for "BridgeHub" `Runtime`s.
 
 pub mod test_cases;
+pub mod test_data;
+
 pub use bp_test_utils::test_header;
 pub use parachains_runtimes_test_utils::*;
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs
deleted file mode 100644
index ddcba3b0399..00000000000
--- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs
+++ /dev/null
@@ -1,1588 +0,0 @@
-// Copyright (C) Parity Technologies (UK) Ltd.
-// This file is part of Cumulus.
-
-// Cumulus 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.
-
-// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
-
-//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
-
-use bp_messages::{
-	source_chain::TargetHeaderChain,
-	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch, SourceHeaderChain},
-	LaneId, MessageKey, OutboundLaneData, UnrewardedRelayersState, Weight,
-};
-use bp_parachains::{BestParaHeadHash, ParaInfo};
-use bp_polkadot_core::parachains::{ParaHash, ParaId};
-use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
-use bp_runtime::{HeaderOf, Parachain, StorageProofSize, UnderlyingChainOf};
-use bp_test_utils::{make_default_justification, prepare_parachain_heads_proof};
-use bridge_runtime_common::{
-	messages::{
-		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
-		BridgedChain as MessageBridgedChain, MessageBridge,
-	},
-	messages_generation::{
-		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
-		prepare_messages_storage_proof,
-	},
-	messages_xcm_extension::{XcmAsPlainPayload, XcmBlobMessageDispatchResult},
-};
-use codec::Encode;
-use frame_support::{
-	assert_ok,
-	traits::{Get, OnFinalize, OnInitialize, OriginTrait, PalletInfoAccess},
-};
-use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor};
-use pallet_bridge_grandpa::BridgedHeader;
-use parachains_common::AccountId;
-use parachains_runtimes_test_utils::{
-	mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf,
-	XcmReceivedFrom,
-};
-use sp_core::H256;
-use sp_keyring::AccountKeyring::*;
-use sp_runtime::{
-	traits::{Header as HeaderT, Zero},
-	AccountId32,
-};
-use xcm::{
-	latest::prelude::*,
-	prelude::{AlwaysLatest, GetVersion},
-};
-use xcm_builder::DispatchBlobError;
-use xcm_executor::{
-	traits::{TransactAsset, WeightBounds},
-	XcmExecutor,
-};
-
-// Re-export test_case from assets
-pub use asset_test_utils::include_teleports_for_native_asset_works;
-
-type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
-	parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
-
-// Re-export test_case from `parachains-runtimes-test-utils`
-pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
-
-/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
-pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	runtime_para_id: u32,
-	runtime_call_encode: Box<
-		dyn Fn(pallet_bridge_grandpa::Call<Runtime, GrandpaPalletInstance>) -> Vec<u8>,
-	>,
-) where
-	Runtime: frame_system::Config
-		+ pallet_balances::Config
-		+ pallet_session::Config
-		+ pallet_xcm::Config
-		+ parachain_info::Config
-		+ pallet_collator_selection::Config
-		+ cumulus_pallet_parachain_system::Config
-		+ pallet_bridge_grandpa::Config<GrandpaPalletInstance>,
-	GrandpaPalletInstance: 'static,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-{
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_para_id(runtime_para_id.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			// check mode before
-			assert_eq!(
-				pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
-				Err(())
-			);
-
-			// encode `initialize` call
-			let initialize_call = runtime_call_encode(pallet_bridge_grandpa::Call::<
-				Runtime,
-				GrandpaPalletInstance,
-			>::initialize {
-				init_data: test_data::initialization_data::<Runtime, GrandpaPalletInstance>(12345),
-			});
-
-			// overestimate - check weight for `pallet_bridge_grandpa::Pallet::initialize()` call
-			let require_weight_at_most =
-				<Runtime as frame_system::Config>::DbWeight::get().reads_writes(7, 7);
-
-			// execute XCM with Transacts to `initialize bridge` as governance does
-			assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
-				initialize_call,
-				require_weight_at_most
-			)
-			.ensure_complete());
-
-			// check mode after
-			assert_eq!(
-				pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
-				Ok(bp_runtime::BasicOperatingMode::Normal)
-			);
-		})
-}
-
-/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
-/// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
-/// For SystemParachains we expect unpaid execution.
-pub fn handle_export_message_from_system_parachain_to_outbound_queue_works<
-	Runtime,
-	XcmConfig,
-	MessagesPalletInstance,
->(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	runtime_para_id: u32,
-	sibling_parachain_id: u32,
-	unwrap_pallet_bridge_messages_event: Box<
-		dyn Fn(Vec<u8>) -> Option<pallet_bridge_messages::Event<Runtime, MessagesPalletInstance>>,
-	>,
-	export_message_instruction: fn() -> Instruction<XcmConfig::RuntimeCall>,
-	expected_lane_id: LaneId,
-	existential_deposit: Option<MultiAsset>,
-	maybe_paid_export_message: Option<MultiAsset>,
-	prepare_configuration: impl Fn(),
-) where
-	Runtime: frame_system::Config
-		+ pallet_balances::Config
-		+ pallet_session::Config
-		+ pallet_xcm::Config
-		+ parachain_info::Config
-		+ pallet_collator_selection::Config
-		+ cumulus_pallet_parachain_system::Config
-		+ pallet_bridge_messages::Config<MessagesPalletInstance>,
-	XcmConfig: xcm_executor::Config,
-	MessagesPalletInstance: 'static,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-{
-	assert_ne!(runtime_para_id, sibling_parachain_id);
-	let sibling_parachain_location = MultiLocation::new(1, Parachain(sibling_parachain_id));
-
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_para_id(runtime_para_id.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			prepare_configuration();
-
-			// check queue before
-			assert_eq!(
-				pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
-					expected_lane_id
-				),
-				Err(())
-			);
-
-			// prepare `ExportMessage`
-			let xcm = if let Some(fee) = maybe_paid_export_message {
-				// deposit ED to origin (if needed)
-				if let Some(ed) = existential_deposit {
-					XcmConfig::AssetTransactor::deposit_asset(
-						&ed,
-						&sibling_parachain_location,
-						Some(&XcmContext::with_message_id([0; 32])),
-					)
-					.expect("deposited ed");
-				}
-				// deposit fee to origin
-				XcmConfig::AssetTransactor::deposit_asset(
-					&fee,
-					&sibling_parachain_location,
-					Some(&XcmContext::with_message_id([0; 32])),
-				)
-				.expect("deposited fee");
-
-				Xcm(vec![
-					WithdrawAsset(MultiAssets::from(vec![fee.clone()])),
-					BuyExecution { fees: fee, weight_limit: Unlimited },
-					export_message_instruction(),
-				])
-			} else {
-				Xcm(vec![
-					UnpaidExecution { weight_limit: Unlimited, check_origin: None },
-					export_message_instruction(),
-				])
-			};
-
-			// execute XCM
-			let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
-			assert_ok!(XcmExecutor::<XcmConfig>::execute_xcm(
-				sibling_parachain_location,
-				xcm,
-				hash,
-				RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
-			)
-			.ensure_complete());
-
-			// check queue after
-			assert_eq!(
-				pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
-					expected_lane_id
-				),
-				Ok(OutboundLaneData {
-					oldest_unpruned_nonce: 1,
-					latest_received_nonce: 0,
-					latest_generated_nonce: 1,
-				})
-			);
-
-			// check events
-			let mut events = <frame_system::Pallet<Runtime>>::events()
-				.into_iter()
-				.filter_map(|e| unwrap_pallet_bridge_messages_event(e.event.encode()));
-			assert!(
-				events.any(|e| matches!(e, pallet_bridge_messages::Event::MessageAccepted { .. }))
-			);
-		})
-}
-
-/// Test-case makes sure that Runtime can route XCM messages received in inbound queue,
-/// We just test here `MessageDispatch` configuration.
-/// We expect that runtime can route messages:
-///     1. to Parent (relay chain)
-///     2. to Sibling parachain
-pub fn message_dispatch_routing_works<
-	Runtime,
-	AllPalletsWithoutSystem,
-	XcmConfig,
-	HrmpChannelOpener,
-	MessagesPalletInstance,
-	RuntimeNetwork,
-	BridgedNetwork,
-	NetworkDistanceAsParentCount,
->(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	runtime_para_id: u32,
-	sibling_parachain_id: u32,
-	unwrap_cumulus_pallet_parachain_system_event: Box<
-		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_parachain_system::Event<Runtime>>,
-	>,
-	unwrap_cumulus_pallet_xcmp_queue_event: Box<
-		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
-	>,
-	expected_lane_id: LaneId,
-	prepare_configuration: impl Fn(),
-) where
-	Runtime: frame_system::Config
-		+ pallet_balances::Config
-		+ pallet_session::Config
-		+ pallet_xcm::Config
-		+ parachain_info::Config
-		+ pallet_collator_selection::Config
-		+ cumulus_pallet_parachain_system::Config
-		+ cumulus_pallet_xcmp_queue::Config
-		+ pallet_bridge_messages::Config<MessagesPalletInstance, InboundPayload = XcmAsPlainPayload>,
-	AllPalletsWithoutSystem:
-		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
-	<Runtime as frame_system::Config>::AccountId:
-		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
-	XcmConfig: xcm_executor::Config,
-	MessagesPalletInstance: 'static,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
-	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
-		Call = cumulus_pallet_parachain_system::Call<Runtime>,
-	>,
-	RuntimeNetwork: Get<NetworkId>,
-	BridgedNetwork: Get<NetworkId>,
-	NetworkDistanceAsParentCount: Get<u8>,
-{
-	assert_ne!(runtime_para_id, sibling_parachain_id);
-
-	struct NetworkWithParentCount<N, C>(core::marker::PhantomData<(N, C)>);
-	impl<N: Get<NetworkId>, C: Get<u8>> Get<MultiLocation> for NetworkWithParentCount<N, C> {
-		fn get() -> MultiLocation {
-			MultiLocation { parents: C::get(), interior: X1(GlobalConsensus(N::get())) }
-		}
-	}
-
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_safe_xcm_version(XCM_VERSION)
-		.with_para_id(runtime_para_id.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			prepare_configuration();
-
-			let mut alice = [0u8; 32];
-			alice[0] = 1;
-
-			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
-				2,
-				AccountId::from(alice).into(),
-			);
-			// 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP)
-			let bridging_message =
-				test_data::simulate_message_exporter_on_bridged_chain::<
-					BridgedNetwork,
-					NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
-					AlwaysLatest,
-				>(
-					(RuntimeNetwork::get(), Here)
-				);
-			let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
-				test_data::dispatch_message(expected_lane_id, 1, bridging_message)
-			);
-			assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched));
-
-			// check events - UpwardMessageSent
-			let mut events = <frame_system::Pallet<Runtime>>::events()
-				.into_iter()
-				.filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode()));
-			assert!(
-				events.any(|e| matches!(e, cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }))
-			);
-
-			// 2. this message is sent from other global consensus with destination of this Runtime sibling parachain (HRMP)
-			let bridging_message =
-				test_data::simulate_message_exporter_on_bridged_chain::<
-					BridgedNetwork,
-					NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
-					AlwaysLatest,
-				>(
-					(RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))),
-				);
-
-			// 2.1. WITHOUT opened hrmp channel -> RoutingError
-			let result =
-				<<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
-					DispatchMessage {
-						key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
-						data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
-					}
-				);
-			assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))));
-
-			// check events - no XcmpMessageSent
-			assert_eq!(<frame_system::Pallet<Runtime>>::events()
-						   .into_iter()
-						   .filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
-						   .count(), 0);
-
-			// 2.1. WITH hrmp channel -> Ok
-			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(runtime_para_id.into(), sibling_parachain_id.into(), included_head, &alice);
-			let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
-				DispatchMessage {
-					key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
-					data: DispatchMessageData { payload: Ok(bridging_message) },
-				}
-			);
-			assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched));
-
-			// check events - XcmpMessageSent
-			let mut events = <frame_system::Pallet<Runtime>>::events()
-				.into_iter()
-				.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()));
-			assert!(
-				events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
-			);
-		})
-}
-
-/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
-/// with proofs (finality, para heads, message) independently submitted.
-pub fn relayed_incoming_message_works<Runtime, AllPalletsWithoutSystem, XcmConfig, HrmpChannelOpener, GPI, PPI, MPI, MB>(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	runtime_para_id: u32,
-	bridged_para_id: u32,
-	sibling_parachain_id: u32,
-	local_relay_chain_id: NetworkId,
-	lane_id: LaneId,
-	prepare_configuration: impl Fn(),
-) where
-	Runtime: frame_system::Config
-	+ pallet_balances::Config
-	+ pallet_session::Config
-	+ pallet_xcm::Config
-	+ parachain_info::Config
-	+ pallet_collator_selection::Config
-	+ cumulus_pallet_parachain_system::Config
-	+ cumulus_pallet_xcmp_queue::Config
-	+ pallet_bridge_grandpa::Config<GPI>
-	+ pallet_bridge_parachains::Config<PPI>
-	+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
-	AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Runtime>>
-		+ OnFinalize<BlockNumberFor<Runtime>>,
-	GPI: 'static,
-	PPI: 'static,
-	MPI: 'static,
-	MB: MessageBridge,
-	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	XcmConfig: xcm_executor::Config,
-	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
-		Call = cumulus_pallet_parachain_system::Call<Runtime>,
-	>,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From<FromBridgedChainMessagesProof<ParaHash>>,
-	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-	<Runtime as frame_system::Config>::AccountId:
-	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
-	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
-	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
-{
-	assert_ne!(runtime_para_id, sibling_parachain_id);
-
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_safe_xcm_version(XCM_VERSION)
-		.with_para_id(runtime_para_id.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			prepare_configuration();
-
-			let mut alice = [0u8; 32];
-			alice[0] = 1;
-
-			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
-				2,
-				AccountId::from(alice).into(),
-			);
-			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
-				runtime_para_id.into(),
-				sibling_parachain_id.into(),
-				included_head,
-				&alice,
-			);
-
-			// start with bridged chain block#0
-			let init_data = test_data::initialization_data::<Runtime, GPI>(0);
-			pallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
-				RuntimeHelper::<Runtime>::root_origin(),
-				init_data,
-			)
-			.unwrap();
-
-			// set up relayer details and proofs
-
-			let message_destination =
-				X2(GlobalConsensus(local_relay_chain_id), Parachain(sibling_parachain_id));
-			// some random numbers (checked by test)
-			let message_nonce = 1;
-			let para_header_number = 5;
-			let relay_header_number = 1;
-
-			let relayer_at_target = Bob;
-			let relayer_id_on_target: AccountIdOf<Runtime> = relayer_at_target.public().into();
-			let relayer_at_source = Dave;
-			let relayer_id_on_source: AccountId32 = relayer_at_source.public().into();
-
-			let xcm = vec![xcm::v3::Instruction::<()>::ClearOrigin; 42];
-			let expected_dispatch = xcm::latest::Xcm::<()>({
-				let mut expected_instructions = xcm.clone();
-				// dispatch prepends bridge pallet instance
-				expected_instructions.insert(
-					0,
-					DescendOrigin(X1(PalletInstance(
-						<pallet_bridge_messages::Pallet<Runtime, MPI> as PalletInfoAccess>::index()
-							as u8,
-					))),
-				);
-				expected_instructions
-			});
-			// generate bridged relay chain finality, parachain heads and message proofs,
-			// to be submitted by relayer to this chain.
-			let (
-				relay_chain_header,
-				grandpa_justification,
-				bridged_para_head,
-				parachain_heads,
-				para_heads_proof,
-				message_proof,
-			) = test_data::make_complex_relayer_delivery_proofs::<
-				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
-				MB,
-				(),
-			>(
-				lane_id,
-				xcm.into(),
-				message_nonce,
-				message_destination,
-				para_header_number,
-				relay_header_number,
-				bridged_para_id,
-			);
-
-			// submit bridged relay chain finality proof
-			{
-				let result = pallet_bridge_grandpa::Pallet::<Runtime, GPI>::submit_finality_proof(
-					RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target.clone()),
-					Box::new(relay_chain_header.clone()),
-					grandpa_justification,
-				);
-				assert_ok!(result);
-				assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
-			}
-
-			// verify finality proof correctly imported
-			assert_eq!(
-				pallet_bridge_grandpa::BestFinalized::<Runtime, GPI>::get().unwrap().1,
-				relay_chain_header.hash()
-			);
-			assert!(pallet_bridge_grandpa::ImportedHeaders::<Runtime, GPI>::contains_key(
-				relay_chain_header.hash()
-			));
-
-			// submit parachain heads proof
-			{
-				let result =
-					pallet_bridge_parachains::Pallet::<Runtime, PPI>::submit_parachain_heads(
-						RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target.clone()),
-						(relay_header_number, relay_chain_header.hash().into()),
-						parachain_heads,
-						para_heads_proof,
-					);
-				assert_ok!(result);
-				assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
-			}
-			// verify parachain head proof correctly imported
-			assert_eq!(
-				pallet_bridge_parachains::ParasInfo::<Runtime, PPI>::get(ParaId(bridged_para_id)),
-				Some(ParaInfo {
-					best_head_hash: BestParaHeadHash {
-						at_relay_block_number: relay_header_number,
-						head_hash: bridged_para_head.hash()
-					},
-					next_imported_hash_position: 1,
-				})
-			);
-
-			// import message
-			assert!(RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
-				sibling_parachain_id.into()
-			)
-			.is_none());
-			assert_eq!(
-				pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
-					.last_delivered_nonce(),
-				0,
-			);
-			// submit message proof
-			{
-				let result = pallet_bridge_messages::Pallet::<Runtime, MPI>::receive_messages_proof(
-					RuntimeHelper::<Runtime>::origin_of(relayer_id_on_target),
-					relayer_id_on_source.into(),
-					message_proof.into(),
-					1,
-					Weight::MAX / 1000,
-				);
-				assert_ok!(result);
-				assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
-			}
-			// verify message correctly imported and dispatched
-			assert_eq!(
-				pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
-					.last_delivered_nonce(),
-				1,
-			);
-
-			// verify relayed bridged XCM message is dispatched to destination sibling para
-			let dispatched = RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
-				sibling_parachain_id.into(),
-			)
-			.unwrap();
-			// verify contains original message
-			let dispatched = xcm::latest::Xcm::<()>::try_from(dispatched).unwrap();
-			let mut dispatched_clone = dispatched.clone();
-			for (idx, expected_instr) in expected_dispatch.0.iter().enumerate() {
-				assert_eq!(expected_instr, &dispatched.0[idx]);
-				assert_eq!(expected_instr, &dispatched_clone.0.remove(0));
-			}
-			match dispatched_clone.0.len() {
-				0 => (),
-				1 => assert!(matches!(dispatched_clone.0[0], SetTopic(_))),
-				count => assert!(false, "Unexpected messages count: {:?}", count),
-			}
-		})
-}
-
-/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
-/// with proofs (finality, para heads, message) batched together in signed extrinsic.
-/// Also verifies relayer transaction signed extensions work as intended.
-pub fn complex_relay_extrinsic_works<Runtime, AllPalletsWithoutSystem, XcmConfig, HrmpChannelOpener, GPI, PPI, MPI, MB>(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	runtime_para_id: u32,
-	bridged_para_id: u32,
-	sibling_parachain_id: u32,
-	bridged_chain_id: bp_runtime::ChainId,
-	local_relay_chain_id: NetworkId,
-	lane_id: LaneId,
-	existential_deposit: BalanceOf<Runtime>,
-	executive_init_block: fn(&HeaderFor<Runtime>),
-	construct_and_apply_extrinsic: fn(
-		sp_keyring::AccountKeyring,
-		pallet_utility::Call::<Runtime>
-	) -> sp_runtime::DispatchOutcome,
-	prepare_configuration: impl Fn(),
-) where
-	Runtime: frame_system::Config
-	+ pallet_balances::Config
-	+ pallet_utility::Config
-	+ pallet_session::Config
-	+ pallet_xcm::Config
-	+ parachain_info::Config
-	+ pallet_collator_selection::Config
-	+ cumulus_pallet_parachain_system::Config
-	+ cumulus_pallet_xcmp_queue::Config
-	+ pallet_bridge_grandpa::Config<GPI>
-	+ pallet_bridge_parachains::Config<PPI>
-	+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
-	+ pallet_bridge_relayers::Config,
-	AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Runtime>>
-		+ OnFinalize<BlockNumberFor<Runtime>>,
-	GPI: 'static,
-	PPI: 'static,
-	MPI: 'static,
-	MB: MessageBridge,
-	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	XcmConfig: xcm_executor::Config,
-	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
-		Call = cumulus_pallet_parachain_system::Call<Runtime>,
-	>,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: From<FromBridgedChainMessagesProof<ParaHash>>,
-	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-	<Runtime as frame_system::Config>::AccountId:
-	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
-	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
-	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
-	<Runtime as pallet_utility::Config>::RuntimeCall:
-	From<pallet_bridge_grandpa::Call<Runtime, GPI>>
-	+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
-	+ From<pallet_bridge_messages::Call<Runtime, MPI>>
-{
-	assert_ne!(runtime_para_id, sibling_parachain_id);
-
-	// Relayer account at local/this BH.
-	let relayer_at_target = Bob;
-	let relayer_id_on_target: AccountIdOf<Runtime> = relayer_at_target.public().into();
-	let relayer_initial_balance = existential_deposit * 100000u32.into();
-	// Relayer account at remote/bridged BH.
-	let relayer_at_source = Dave;
-	let relayer_id_on_source: AccountId32 = relayer_at_source.public().into();
-
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_safe_xcm_version(XCM_VERSION)
-		.with_para_id(runtime_para_id.into())
-		.with_balances(vec![(relayer_id_on_target.clone(), relayer_initial_balance)])
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			prepare_configuration();
-
-			let mut alice = [0u8; 32];
-			alice[0] = 1;
-
-			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
-				2,
-				AccountId::from(alice).into(),
-			);
-			let zero: BlockNumberFor<Runtime> = 0u32.into();
-			let genesis_hash = frame_system::Pallet::<Runtime>::block_hash(zero);
-			let mut header: HeaderFor<Runtime> = bp_test_utils::test_header(1u32.into());
-			header.set_parent_hash(genesis_hash);
-			executive_init_block(&header);
-
-			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
-				runtime_para_id.into(),
-				sibling_parachain_id.into(),
-				included_head,
-				&alice,
-			);
-
-			// start with bridged chain block#0
-			let init_data = test_data::initialization_data::<Runtime, GPI>(0);
-			pallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
-				RuntimeHelper::<Runtime>::root_origin(),
-				init_data,
-			)
-			.unwrap();
-
-			// set up relayer details and proofs
-
-			let message_destination =
-				X2(GlobalConsensus(local_relay_chain_id), Parachain(sibling_parachain_id));
-			// some random numbers (checked by test)
-			let message_nonce = 1;
-			let para_header_number = 5;
-			let relay_header_number = 1;
-
-			let xcm = vec![xcm::latest::Instruction::<()>::ClearOrigin; 42];
-			let expected_dispatch = xcm::latest::Xcm::<()>({
-				let mut expected_instructions = xcm.clone();
-				// dispatch prepends bridge pallet instance
-				expected_instructions.insert(
-					0,
-					DescendOrigin(X1(PalletInstance(
-						<pallet_bridge_messages::Pallet<Runtime, MPI> as PalletInfoAccess>::index()
-							as u8,
-					))),
-				);
-				expected_instructions
-			});
-			// generate bridged relay chain finality, parachain heads and message proofs,
-			// to be submitted by relayer to this chain.
-			let (
-				relay_chain_header,
-				grandpa_justification,
-				bridged_para_head,
-				parachain_heads,
-				para_heads_proof,
-				message_proof,
-			) = test_data::make_complex_relayer_delivery_proofs::<
-				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
-				MB,
-				(),
-			>(
-				lane_id,
-				xcm.clone().into(),
-				message_nonce,
-				message_destination,
-				para_header_number,
-				relay_header_number,
-				bridged_para_id,
-			);
-
-			let relay_chain_header_hash = relay_chain_header.hash();
-			let batch = test_data::make_complex_relayer_delivery_batch::<Runtime, GPI, PPI, MPI>(
-				relay_chain_header,
-				grandpa_justification,
-				parachain_heads,
-				para_heads_proof,
-				message_proof,
-				relayer_id_on_source,
-			);
-
-			// sanity checks - before relayer extrinsic
-			assert!(RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
-				sibling_parachain_id.into()
-			)
-			.is_none());
-			assert_eq!(
-				pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
-					.last_delivered_nonce(),
-				0,
-			);
-			let msg_proofs_rewards_account = RewardsAccountParams::new(
-				lane_id,
-				bridged_chain_id,
-				RewardsAccountOwner::ThisChain,
-			);
-			assert_eq!(
-				pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
-					relayer_id_on_target.clone(),
-					msg_proofs_rewards_account
-				),
-				None,
-			);
-
-			// construct and apply extrinsic containing batch calls:
-			//   bridged relay chain finality proof
-			//   + parachain heads proof
-			//   + submit message proof
-			let dispatch_outcome = construct_and_apply_extrinsic(relayer_at_target, batch);
-
-			// verify finality proof correctly imported
-			assert_ok!(dispatch_outcome);
-			assert_eq!(
-				<pallet_bridge_grandpa::BestFinalized<Runtime, GPI>>::get().unwrap().1,
-				relay_chain_header_hash
-			);
-			assert!(<pallet_bridge_grandpa::ImportedHeaders<Runtime, GPI>>::contains_key(
-				relay_chain_header_hash
-			));
-			// verify parachain head proof correctly imported
-			assert_eq!(
-				pallet_bridge_parachains::ParasInfo::<Runtime, PPI>::get(ParaId(bridged_para_id)),
-				Some(ParaInfo {
-					best_head_hash: BestParaHeadHash {
-						at_relay_block_number: relay_header_number,
-						head_hash: bridged_para_head.hash()
-					},
-					next_imported_hash_position: 1,
-				})
-			);
-			// verify message correctly imported and dispatched
-			assert_eq!(
-				pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(lane_id)
-					.last_delivered_nonce(),
-				1,
-			);
-			// verify relayer is refunded
-			assert!(pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
-				relayer_id_on_target,
-				msg_proofs_rewards_account
-			)
-			.is_some());
-
-			// verify relayed bridged XCM message is dispatched to destination sibling para
-			let dispatched = RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
-				sibling_parachain_id.into(),
-			)
-			.unwrap();
-			// verify contains original message
-			let dispatched = xcm::latest::Xcm::<()>::try_from(dispatched).unwrap();
-			let mut dispatched_clone = dispatched.clone();
-			for (idx, expected_instr) in expected_dispatch.0.iter().enumerate() {
-				assert_eq!(expected_instr, &dispatched.0[idx]);
-				assert_eq!(expected_instr, &dispatched_clone.0.remove(0));
-			}
-			match dispatched_clone.0.len() {
-				0 => (),
-				1 => assert!(matches!(dispatched_clone.0[0], SetTopic(_))),
-				count => assert!(false, "Unexpected messages count: {:?}", count),
-			}
-		})
-}
-
-/// Estimates XCM execution fee for paid `ExportMessage` processing.
-pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer<
-	Runtime,
-	XcmConfig,
-	WeightToFee,
->() -> u128
-where
-	Runtime: frame_system::Config + pallet_balances::Config,
-	XcmConfig: xcm_executor::Config,
-	WeightToFee: frame_support::weights::WeightToFee<Balance = BalanceOf<Runtime>>,
-	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
-{
-	// data here are not relevant for weighing
-	let mut xcm = Xcm(vec![
-		WithdrawAsset(MultiAssets::from(vec![MultiAsset {
-			id: Concrete(MultiLocation { parents: 1, interior: Here }),
-			fun: Fungible(34333299),
-		}])),
-		BuyExecution {
-			fees: MultiAsset {
-				id: Concrete(MultiLocation { parents: 1, interior: Here }),
-				fun: Fungible(34333299),
-			},
-			weight_limit: Unlimited,
-		},
-		ExportMessage {
-			network: Polkadot,
-			destination: X1(Parachain(1000)),
-			xcm: Xcm(vec![
-				ReserveAssetDeposited(MultiAssets::from(vec![MultiAsset {
-					id: Concrete(MultiLocation {
-						parents: 2,
-						interior: X1(GlobalConsensus(Kusama)),
-					}),
-					fun: Fungible(1000000000000),
-				}])),
-				ClearOrigin,
-				BuyExecution {
-					fees: MultiAsset {
-						id: Concrete(MultiLocation {
-							parents: 2,
-							interior: X1(GlobalConsensus(Kusama)),
-						}),
-						fun: Fungible(1000000000000),
-					},
-					weight_limit: Unlimited,
-				},
-				DepositAsset {
-					assets: Wild(AllCounted(1)),
-					beneficiary: MultiLocation {
-						parents: 0,
-						interior: X1(xcm::latest::prelude::AccountId32 {
-							network: None,
-							id: [
-								212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159,
-								214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165,
-								109, 162, 125,
-							],
-						}),
-					},
-				},
-				SetTopic([
-					116, 82, 194, 132, 171, 114, 217, 165, 23, 37, 161, 177, 165, 179, 247, 114,
-					137, 101, 147, 70, 28, 157, 168, 32, 154, 63, 74, 228, 152, 180, 5, 63,
-				]),
-			]),
-		},
-		DepositAsset {
-			assets: Wild(All),
-			beneficiary: MultiLocation { parents: 1, interior: X1(Parachain(1000)) },
-		},
-		SetTopic([
-			36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219,
-			157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122,
-		]),
-	]);
-
-	// get weight
-	let weight = XcmConfig::Weigher::weight(&mut xcm);
-	assert_ok!(weight);
-	let weight = weight.unwrap();
-	// check if sane
-	let max_expected = Runtime::BlockWeights::get().max_block / 10;
-	assert!(
-		weight.all_lte(max_expected),
-		"calculated weight: {:?}, max_expected: {:?}",
-		weight,
-		max_expected
-	);
-
-	// check fee, should not be 0
-	let estimated_fee = WeightToFee::weight_to_fee(&weight);
-	assert!(estimated_fee > BalanceOf::<Runtime>::zero());
-
-	sp_tracing::try_init_simple();
-	log::error!(
-		target: "bridges::estimate",
-		"Estimate fee: {:?} for `ExportMessage` for runtime: {:?}",
-		estimated_fee,
-		Runtime::Version::get(),
-	);
-
-	estimated_fee.into()
-}
-
-/// Estimates transaction fee for default message delivery transaction (batched with required
-/// proofs) from bridged parachain.
-pub fn can_calculate_fee_for_complex_message_delivery_transaction<Runtime, GPI, PPI, MPI, MB>(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	compute_extrinsic_fee: fn(pallet_utility::Call::<Runtime>) -> u128,
-) -> u128
-where
-	Runtime: frame_system::Config
-		+ pallet_balances::Config
-		+ pallet_session::Config
-		+ pallet_xcm::Config
-		+ parachain_info::Config
-		+ pallet_collator_selection::Config
-		+ cumulus_pallet_parachain_system::Config
-		+ pallet_bridge_grandpa::Config<GPI>
-		+ pallet_bridge_parachains::Config<PPI>
-		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
-		+ pallet_utility::Config,
-	GPI: 'static,
-	PPI: 'static,
-	MPI: 'static,
-	MB: MessageBridge,
-	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
-		From<FromBridgedChainMessagesProof<ParaHash>>,
-	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-	<Runtime as frame_system::Config>::AccountId:
-	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
-	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
-	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
-	<Runtime as pallet_utility::Config>::RuntimeCall:
-	From<pallet_bridge_grandpa::Call<Runtime, GPI>>
-	+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
-	+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
-{
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_safe_xcm_version(XCM_VERSION)
-		.with_para_id(1000.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			// generate bridged relay chain finality, parachain heads and message proofs,
-			// to be submitted by relayer to this chain.
-			//
-			// we don't care about parameter values here, apart from the XCM message size. But we
-			// do not need to have a large message here, because we're charging for every byte of
-			// the message additionally
-			let (
-				relay_chain_header,
-				grandpa_justification,
-				_,
-				parachain_heads,
-				para_heads_proof,
-				message_proof,
-			) = test_data::make_complex_relayer_delivery_proofs::<
-				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
-				MB,
-				(),
-			>(
-				LaneId::default(),
-				vec![xcm::v3::Instruction::<()>::ClearOrigin; 1_024].into(),
-				1,
-				X2(GlobalConsensus(Polkadot), Parachain(1_000)),
-				1,
-				5,
-				1_000,
-			);
-
-			// generate batch call that provides finality for bridged relay and parachains + message
-			// proof
-			let batch = test_data::make_complex_relayer_delivery_batch::<Runtime, GPI, PPI, MPI>(
-				relay_chain_header,
-				grandpa_justification,
-				parachain_heads,
-				para_heads_proof,
-				message_proof,
-				Dave.public().into(),
-			);
-			let estimated_fee = compute_extrinsic_fee(batch);
-
-			log::error!(
-				target: "bridges::estimate",
-				"Estimate fee: {:?} for single message delivery for runtime: {:?}",
-				estimated_fee,
-				Runtime::Version::get(),
-			);
-
-			estimated_fee
-		})
-}
-
-/// Estimates transaction fee for default message confirmation transaction (batched with required
-/// proofs) from bridged parachain.
-pub fn can_calculate_fee_for_complex_message_confirmation_transaction<Runtime, GPI, PPI, MPI, MB>(
-	collator_session_key: CollatorSessionKeys<Runtime>,
-	compute_extrinsic_fee: fn(pallet_utility::Call::<Runtime>) -> u128,
-) -> u128
-where
-	Runtime: frame_system::Config
-		+ pallet_balances::Config
-		+ pallet_session::Config
-		+ pallet_xcm::Config
-		+ parachain_info::Config
-		+ pallet_collator_selection::Config
-		+ cumulus_pallet_parachain_system::Config
-		+ pallet_bridge_grandpa::Config<GPI>
-		+ pallet_bridge_parachains::Config<PPI>
-		+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
-		+ pallet_utility::Config,
-	GPI: 'static,
-	PPI: 'static,
-	MPI: 'static,
-	MB: MessageBridge,
-	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-	<<MB as MessageBridge>::ThisChain as bp_runtime::Chain>::AccountId: From<AccountId32>,
-	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
-		From<FromBridgedChainMessagesProof<ParaHash>>,
-	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-	<Runtime as frame_system::Config>::AccountId:
-	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
-	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
-	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
-	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
-		<<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain as TargetHeaderChain<
-			XcmAsPlainPayload,
-			Runtime::AccountId,
-		>>::MessagesDeliveryProof: From<FromBridgedChainMessagesDeliveryProof<ParaHash>>,
-	<Runtime as pallet_utility::Config>::RuntimeCall:
-	From<pallet_bridge_grandpa::Call<Runtime, GPI>>
-	+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
-	+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
-{
-	ExtBuilder::<Runtime>::default()
-		.with_collators(collator_session_key.collators())
-		.with_session_keys(collator_session_key.session_keys())
-		.with_safe_xcm_version(XCM_VERSION)
-		.with_para_id(1000.into())
-		.with_tracing()
-		.build()
-		.execute_with(|| {
-			// generate bridged relay chain finality, parachain heads and message proofs,
-			// to be submitted by relayer to this chain.
-			let unrewarded_relayers = UnrewardedRelayersState {
-				unrewarded_relayer_entries: 1,
-				total_messages: 1,
-				..Default::default()
-			};
-			let (
-				relay_chain_header,
-				grandpa_justification,
-				_,
-				parachain_heads,
-				para_heads_proof,
-				message_delivery_proof,
-			) = test_data::make_complex_relayer_confirmation_proofs::<
-				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
-				MB,
-				(),
-			>(LaneId::default(), 1, 5, 1_000, Alice.public().into(), unrewarded_relayers.clone());
-
-			// generate batch call that provides finality for bridged relay and parachains + message
-			// proof
-			let batch = test_data::make_complex_relayer_confirmation_batch::<Runtime, GPI, PPI, MPI>(
-				relay_chain_header,
-				grandpa_justification,
-				parachain_heads,
-				para_heads_proof,
-				message_delivery_proof,
-				unrewarded_relayers,
-			);
-			let estimated_fee = compute_extrinsic_fee(batch);
-
-			log::error!(
-				target: "bridges::estimate",
-				"Estimate fee: {:?} for single message confirmation for runtime: {:?}",
-				estimated_fee,
-				Runtime::Version::get(),
-			);
-
-			estimated_fee
-		})
-}
-
-pub mod test_data {
-	use super::*;
-	use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
-	use bp_messages::{DeliveredMessages, InboundLaneData, MessageNonce, UnrewardedRelayer};
-	use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
-	use bp_runtime::{BasicOperatingMode, HashOf};
-	use bp_test_utils::authority_list;
-	use sp_runtime::{DigestItem, SaturatedConversion};
-	use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
-	use xcm_executor::traits::{validate_export, ExportXcm};
-
-	pub fn prepare_inbound_xcm<InnerXcmRuntimeCall>(
-		xcm_message: Xcm<InnerXcmRuntimeCall>,
-		destination: InteriorMultiLocation,
-	) -> Vec<u8> {
-		let location = xcm::VersionedInteriorMultiLocation::V3(destination);
-		let xcm = xcm::VersionedXcm::<InnerXcmRuntimeCall>::V3(xcm_message);
-		// this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor
-		// or public fields, so just tuple
-		// (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed
-		// to the storage)
-		(location, xcm).encode().encode()
-	}
-
-	/// Prepare a batch call with relay finality proof, parachain head proof and message proof.
-	pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI>(
-		relay_chain_header: BridgedHeader<Runtime, GPI>,
-		grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
-		parachain_heads: Vec<(ParaId, ParaHash)>,
-		para_heads_proof: ParaHeadsProof,
-		message_proof: FromBridgedChainMessagesProof<ParaHash>,
-		relayer_id_at_bridged_chain: AccountId32,
-	) -> pallet_utility::Call<Runtime> where
-		Runtime:pallet_bridge_grandpa::Config<GPI>
-			+ pallet_bridge_parachains::Config<PPI>
-			+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
-			+ pallet_utility::Config,
-		GPI: 'static,
-		PPI: 'static,
-		MPI: 'static,
-		ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-		<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-		<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
-			From<FromBridgedChainMessagesProof<ParaHash>>,
-		<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
-		<Runtime as pallet_utility::Config>::RuntimeCall:
-			From<pallet_bridge_grandpa::Call<Runtime, GPI>>
-			+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
-			+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
-	{
-		let relay_chain_header_hash = relay_chain_header.hash();
-		let relay_chain_header_number = *relay_chain_header.number();
-		let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
-			finality_target: Box::new(relay_chain_header),
-			justification: grandpa_justification,
-		};
-		let submit_para_head =
-			pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
-				at_relay_block: (
-					relay_chain_header_number.saturated_into(),
-					relay_chain_header_hash.into(),
-				),
-				parachains: parachain_heads,
-				parachain_heads_proof: para_heads_proof,
-			};
-		let submit_message = pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
-			relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
-			proof: message_proof.into(),
-			messages_count: 1,
-			dispatch_weight: Weight::from_parts(1000000000, 0),
-		};
-		pallet_utility::Call::<Runtime>::batch_all {
-			calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()],
-		}
-	}
-
-	/// Prepare a batch call with relay finality proof, parachain head proof and message delivery
-	/// proof.
-	pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, PPI, MPI>(
-		relay_chain_header: BridgedHeader<Runtime, GPI>,
-		grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
-		parachain_heads: Vec<(ParaId, ParaHash)>,
-		para_heads_proof: ParaHeadsProof,
-		message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash>,
-		relayers_state: UnrewardedRelayersState,
-	) -> pallet_utility::Call<Runtime> where
-		Runtime:pallet_bridge_grandpa::Config<GPI>
-			+ pallet_bridge_parachains::Config<PPI>
-			+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
-			+ pallet_utility::Config,
-		GPI: 'static,
-		PPI: 'static,
-		MPI: 'static,
-		ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
-		<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
-		<<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain as TargetHeaderChain<
-			XcmAsPlainPayload,
-			Runtime::AccountId,
-		>>::MessagesDeliveryProof: From<FromBridgedChainMessagesDeliveryProof<ParaHash>>,
-		<Runtime as pallet_utility::Config>::RuntimeCall:
-			From<pallet_bridge_grandpa::Call<Runtime, GPI>>
-			+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
-			+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
-	{
-		let relay_chain_header_hash = relay_chain_header.hash();
-		let relay_chain_header_number = *relay_chain_header.number();
-		let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
-			finality_target: Box::new(relay_chain_header),
-			justification: grandpa_justification,
-		};
-		let submit_para_head =
-			pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
-				at_relay_block: (
-					relay_chain_header_number.saturated_into(),
-					relay_chain_header_hash.into(),
-				),
-				parachains: parachain_heads,
-				parachain_heads_proof: para_heads_proof,
-			};
-		let submit_message_delivery_proof =
-			pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
-				proof: message_delivery_proof.into(),
-				relayers_state,
-			};
-		pallet_utility::Call::<Runtime>::batch_all {
-			calls: vec![
-				submit_grandpa.into(),
-				submit_para_head.into(),
-				submit_message_delivery_proof.into(),
-			],
-		}
-	}
-
-	/// Prepare storage proofs of messages, stored at the source chain.
-	pub fn make_complex_relayer_delivery_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
-		lane_id: LaneId,
-		xcm_message: Xcm<InnerXcmRuntimeCall>,
-		message_nonce: MessageNonce,
-		message_destination: Junctions,
-		para_header_number: u32,
-		relay_header_number: u32,
-		bridged_para_id: u32,
-	) -> (
-		HeaderOf<BridgedRelayChain>,
-		GrandpaJustification<HeaderOf<BridgedRelayChain>>,
-		ParaHead,
-		Vec<(ParaId, ParaHash)>,
-		ParaHeadsProof,
-		FromBridgedChainMessagesProof<ParaHash>,
-	)
-	where
-		BridgedRelayChain: ChainWithGrandpa,
-		HashOf<BridgedRelayChain>: From<H256>,
-		MB: MessageBridge,
-		<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-		<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-		UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	{
-		let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
-		let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
-		// prepare para storage proof containing message
-		let (para_state_root, para_storage_proof) = prepare_messages_storage_proof::<MB>(
-			lane_id,
-			message_nonce..=message_nonce,
-			None,
-			message_size,
-			message_payload,
-			encode_all_messages,
-			encode_lane_data,
-		);
-
-		let (
-			relay_chain_header,
-			justification,
-			bridged_para_head,
-			parachain_heads,
-			para_heads_proof,
-		) = make_complex_bridged_heads_proof::<BridgedRelayChain, MB>(
-			para_state_root,
-			para_header_number,
-			relay_header_number,
-			bridged_para_id,
-		);
-
-		let message_proof = FromBridgedChainMessagesProof {
-			bridged_header_hash: bridged_para_head.hash(),
-			storage_proof: para_storage_proof,
-			lane: lane_id,
-			nonces_start: message_nonce,
-			nonces_end: message_nonce,
-		};
-
-		(
-			relay_chain_header,
-			justification,
-			bridged_para_head,
-			parachain_heads,
-			para_heads_proof,
-			message_proof,
-		)
-	}
-
-	/// Prepare storage proofs of message confirmations, stored at the target chain.
-	pub fn make_complex_relayer_confirmation_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
-		lane_id: LaneId,
-		para_header_number: u32,
-		relay_header_number: u32,
-		bridged_para_id: u32,
-		relayer_id_at_this_chain: AccountId32,
-		relayers_state: UnrewardedRelayersState,
-	) -> (
-		HeaderOf<BridgedRelayChain>,
-		GrandpaJustification<HeaderOf<BridgedRelayChain>>,
-		ParaHead,
-		Vec<(ParaId, ParaHash)>,
-		ParaHeadsProof,
-		FromBridgedChainMessagesDeliveryProof<ParaHash>,
-	)
-	where
-		BridgedRelayChain: ChainWithGrandpa,
-		HashOf<BridgedRelayChain>: From<H256>,
-		MB: MessageBridge,
-		<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-		<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-		<<MB as MessageBridge>::ThisChain as bp_runtime::Chain>::AccountId: From<AccountId32>,
-		UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	{
-		// prepare para storage proof containing message delivery proof
-		let (para_state_root, para_storage_proof) = prepare_message_delivery_storage_proof::<MB>(
-			lane_id,
-			InboundLaneData {
-				relayers: vec![
-					UnrewardedRelayer {
-						relayer: relayer_id_at_this_chain.into(),
-						messages: DeliveredMessages::new(1)
-					};
-					relayers_state.unrewarded_relayer_entries as usize
-				]
-				.into(),
-				last_confirmed_nonce: 1,
-			},
-			StorageProofSize::Minimal(0),
-		);
-
-		let (
-			relay_chain_header,
-			justification,
-			bridged_para_head,
-			parachain_heads,
-			para_heads_proof,
-		) = make_complex_bridged_heads_proof::<BridgedRelayChain, MB>(
-			para_state_root,
-			para_header_number,
-			relay_header_number,
-			bridged_para_id,
-		);
-
-		let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
-			bridged_header_hash: bridged_para_head.hash(),
-			storage_proof: para_storage_proof,
-			lane: lane_id,
-		};
-
-		(
-			relay_chain_header,
-			justification,
-			bridged_para_head,
-			parachain_heads,
-			para_heads_proof,
-			message_delivery_proof,
-		)
-	}
-
-	/// Make bridged parachain header with given state root and relay header that is finalizing it.
-	pub fn make_complex_bridged_heads_proof<BridgedRelayChain, MB>(
-		para_state_root: ParaHash,
-		para_header_number: u32,
-		relay_header_number: u32,
-		bridged_para_id: u32,
-	) -> (
-		HeaderOf<BridgedRelayChain>,
-		GrandpaJustification<HeaderOf<BridgedRelayChain>>,
-		ParaHead,
-		Vec<(ParaId, ParaHash)>,
-		ParaHeadsProof,
-	)
-	where
-		BridgedRelayChain: ChainWithGrandpa,
-		HashOf<BridgedRelayChain>: From<H256>,
-		MB: MessageBridge,
-		<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
-		<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
-		UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
-	{
-		let bridged_para_head = ParaHead(
-			bp_test_utils::test_header_with_root::<HeaderOf<MB::BridgedChain>>(
-				para_header_number.into(),
-				para_state_root.into(),
-			)
-			.encode(),
-		);
-		let (relay_state_root, para_heads_proof, parachain_heads) =
-			prepare_parachain_heads_proof::<HeaderOf<MB::BridgedChain>>(vec![(
-				bridged_para_id,
-				bridged_para_head.clone(),
-			)]);
-		assert_eq!(bridged_para_head.hash(), parachain_heads[0].1);
-
-		// import bridged relay chain block#1 with state root containing head#5 of bridged parachain
-		let mut relay_chain_header: BridgedRelayChain::Header =
-			bp_test_utils::test_header_with_root(
-				relay_header_number.into(),
-				relay_state_root.into(),
-			);
-		// to compute proper cost of GRANDPA call, let's add some dummy bytes to header, so that the
-		// `submit_finality_proof` call size would be close to maximal expected (and refundable)
-		let expected_bytes_in_grandpa_call = BridgedRelayChain::AVERAGE_HEADER_SIZE
-			.saturating_mul(BridgedRelayChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY)
-			.saturating_add(BridgedRelayChain::MAX_MANDATORY_HEADER_SIZE)
-			as usize;
-		let extra_bytes_required =
-			expected_bytes_in_grandpa_call.saturating_sub(relay_chain_header.encoded_size());
-		relay_chain_header
-			.digest_mut()
-			.push(DigestItem::Other(vec![42; extra_bytes_required]));
-
-		let justification = make_default_justification(&relay_chain_header);
-		(relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof)
-	}
-
-	/// Helper that creates InitializationData mock data, that can be used to initialize bridge
-	/// GRANDPA pallet
-	pub fn initialization_data<
-		Runtime: pallet_bridge_grandpa::Config<GrandpaPalletInstance>,
-		GrandpaPalletInstance: 'static,
-	>(
-		block_number: u32,
-	) -> bp_header_chain::InitializationData<BridgedHeader<Runtime, GrandpaPalletInstance>> {
-		bp_header_chain::InitializationData {
-			header: Box::new(bp_test_utils::test_header(block_number.into())),
-			authority_list: authority_list(),
-			set_id: 1,
-			operating_mode: BasicOperatingMode::Normal,
-		}
-	}
-
-	/// Dummy xcm
-	pub(crate) fn dummy_xcm() -> Xcm<()> {
-		vec![Trap(42)].into()
-	}
-
-	pub(crate) fn dispatch_message(
-		lane_id: LaneId,
-		nonce: MessageNonce,
-		payload: Vec<u8>,
-	) -> DispatchMessage<Vec<u8>> {
-		DispatchMessage {
-			key: MessageKey { lane_id, nonce },
-			data: DispatchMessageData { payload: Ok(payload) },
-		}
-	}
-
-	/// Macro used for simulate_export_message and capturing bytes
-	macro_rules! grab_haul_blob (
-		($name:ident, $grabbed_payload:ident) => {
-			std::thread_local! {
-				static $grabbed_payload: std::cell::RefCell<Option<Vec<u8>>> = std::cell::RefCell::new(None);
-			}
-
-			struct $name;
-			impl HaulBlob for $name {
-				fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError>{
-					$grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob));
-					Ok(())
-				}
-			}
-		}
-	);
-
-	/// Simulates `HaulBlobExporter` and all its wrapping and captures generated plain bytes,
-	/// which are transferred over bridge.
-	pub(crate) fn simulate_message_exporter_on_bridged_chain<
-		SourceNetwork: Get<NetworkId>,
-		DestinationNetwork: Get<MultiLocation>,
-		DestinationVersion: GetVersion,
-	>(
-		(destination_network, destination_junctions): (NetworkId, Junctions),
-	) -> Vec<u8> {
-		grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD);
-
-		// lets pretend that some parachain on bridged chain exported the message
-		let universal_source_on_bridged_chain =
-			X2(GlobalConsensus(SourceNetwork::get()), Parachain(5678));
-		let channel = 1_u32;
-
-		// simulate XCM message export
-		let (ticket, fee) = validate_export::<
-			HaulBlobExporter<GrabbingHaulBlob, DestinationNetwork, DestinationVersion, ()>,
-		>(
-			destination_network,
-			channel,
-			universal_source_on_bridged_chain,
-			destination_junctions,
-			dummy_xcm(),
-		)
-		.expect("validate_export to pass");
-		log::info!(
-			target: "simulate_message_exporter_on_bridged_chain",
-			"HaulBlobExporter::validate fee: {:?}",
-			fee
-		);
-		let xcm_hash = HaulBlobExporter::<
-			GrabbingHaulBlob,
-			DestinationNetwork,
-			DestinationVersion,
-			(),
-		>::deliver(ticket)
-		.expect("deliver to pass");
-		log::info!(
-			target: "simulate_message_exporter_on_bridged_chain",
-			"HaulBlobExporter::deliver xcm_hash: {:?}",
-			xcm_hash
-		);
-
-		GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("Encoded message should be here"))
-	}
-}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
new file mode 100644
index 00000000000..3a0bbf8f571
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs
@@ -0,0 +1,442 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities
+//! with remote GRANDPA chain.
+
+use crate::{
+	test_cases::{helpers, run_test},
+	test_data,
+};
+
+use bp_header_chain::ChainWithGrandpa;
+use bp_messages::{
+	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
+	UnrewardedRelayersState,
+};
+use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
+use bp_runtime::{HashOf, UnderlyingChainOf};
+use bridge_runtime_common::{
+	messages::{
+		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
+		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
+	},
+	messages_xcm_extension::XcmAsPlainPayload,
+};
+use frame_support::traits::{Get, OnFinalize, OnInitialize, OriginTrait};
+use frame_system::pallet_prelude::BlockNumberFor;
+use parachains_runtimes_test_utils::{
+	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, ValidatorIdOf,
+};
+use sp_keyring::AccountKeyring::*;
+use sp_runtime::{traits::Header as HeaderT, AccountId32};
+use xcm::latest::prelude::*;
+
+/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
+/// with proofs (finality, message) independently submitted.
+/// Also verifies relayer transaction signed extensions work as intended.
+pub fn relayed_incoming_message_works<
+	Runtime,
+	AllPalletsWithoutSystem,
+	HrmpChannelOpener,
+	GPI,
+	MPI,
+	MB,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	bridged_chain_id: bp_runtime::ChainId,
+	sibling_parachain_id: u32,
+	local_relay_chain_id: NetworkId,
+	lane_id: LaneId,
+	prepare_configuration: impl Fn(),
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ pallet_bridge_grandpa::Config<
+			GPI,
+			BridgedChain = UnderlyingChainOf<MessageBridgedChain<MB>>,
+		> + pallet_bridge_messages::Config<MPI>
+		+ pallet_bridge_relayers::Config,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	GPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
+		MessagesProof = FromBridgedChainMessagesProof<HashOf<MessageBridgedChain<MB>>>,
+	>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as frame_system::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	helpers::relayed_incoming_message_works::<
+		Runtime,
+		AllPalletsWithoutSystem,
+		HrmpChannelOpener,
+		MPI,
+	>(
+		collator_session_key,
+		runtime_para_id,
+		sibling_parachain_id,
+		local_relay_chain_id,
+		construct_and_apply_extrinsic,
+		|relayer_id_at_this_chain,
+		 relayer_id_at_bridged_chain,
+		 message_destination,
+		 message_nonce,
+		 xcm| {
+			let relay_header_number = 5u32.into();
+
+			prepare_configuration();
+
+			// start with bridged relay chain block#0
+			helpers::initialize_bridge_grandpa_pallet::<Runtime, GPI>(
+				test_data::initialization_data::<Runtime, GPI>(0),
+			);
+
+			// generate bridged relay chain finality, parachain heads and message proofs,
+			// to be submitted by relayer to this chain.
+			let (relay_chain_header, grandpa_justification, message_proof) =
+				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<MB, ()>(
+					lane_id,
+					xcm.into(),
+					message_nonce,
+					message_destination,
+					relay_header_number,
+				);
+
+			let relay_chain_header_hash = relay_chain_header.hash();
+			vec![
+				(
+					pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+						finality_target: Box::new(relay_chain_header),
+						justification: grandpa_justification,
+					}.into(),
+					helpers::VerifySubmitGrandpaFinalityProofOutcome::<Runtime, GPI>::expect_best_header_hash(relay_chain_header_hash),
+				),
+				(
+					pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+						relayer_id_at_bridged_chain,
+						proof: message_proof,
+						messages_count: 1,
+						dispatch_weight: Weight::from_parts(1000000000, 0),
+					}.into(),
+					Box::new((
+						helpers::VerifySubmitMessagesProofOutcome::<Runtime, MPI>::expect_last_delivered_nonce(lane_id, 1),
+						helpers::VerifyRelayerRewarded::<Runtime>::expect_relayer_reward(
+							relayer_id_at_this_chain,
+							RewardsAccountParams::new(
+								lane_id,
+								bridged_chain_id,
+								RewardsAccountOwner::ThisChain,
+							),
+						),
+					)),
+				),
+			]
+		},
+	);
+}
+
+/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
+/// with proofs (finality, message) batched together in signed extrinsic.
+/// Also verifies relayer transaction signed extensions work as intended.
+pub fn complex_relay_extrinsic_works<
+	Runtime,
+	AllPalletsWithoutSystem,
+	XcmConfig,
+	HrmpChannelOpener,
+	GPI,
+	MPI,
+	MB,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	sibling_parachain_id: u32,
+	bridged_chain_id: bp_runtime::ChainId,
+	local_relay_chain_id: NetworkId,
+	lane_id: LaneId,
+	prepare_configuration: impl Fn(),
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ pallet_bridge_grandpa::Config<
+			GPI,
+			BridgedChain = UnderlyingChainOf<MessageBridgedChain<MB>>,
+		> + pallet_bridge_messages::Config<MPI>
+		+ pallet_bridge_relayers::Config
+		+ pallet_utility::Config,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	GPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
+		MessagesProof = FromBridgedChainMessagesProof<HashOf<MessageBridgedChain<MB>>>,
+	>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	<Runtime as frame_system::Config>::RuntimeCall: From<pallet_utility::Call<Runtime>>,
+{
+	helpers::relayed_incoming_message_works::<
+		Runtime,
+		AllPalletsWithoutSystem,
+		HrmpChannelOpener,
+		MPI,
+	>(
+		collator_session_key,
+		runtime_para_id,
+		sibling_parachain_id,
+		local_relay_chain_id,
+		construct_and_apply_extrinsic,
+		|relayer_id_at_this_chain,
+		 relayer_id_at_bridged_chain,
+		 message_destination,
+		 message_nonce,
+		 xcm| {
+			let relay_header_number = 1u32.into();
+
+			prepare_configuration();
+
+			// start with bridged relay chain block#0
+			helpers::initialize_bridge_grandpa_pallet::<Runtime, GPI>(
+				test_data::initialization_data::<Runtime, GPI>(0),
+			);
+
+			// generate bridged relay chain finality, parachain heads and message proofs,
+			// to be submitted by relayer to this chain.
+			let (relay_chain_header, grandpa_justification, message_proof) =
+				test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<MB, ()>(
+					lane_id,
+					xcm.into(),
+					message_nonce,
+					message_destination,
+					relay_header_number,
+				);
+
+			let relay_chain_header_hash = relay_chain_header.hash();
+			vec![(
+				pallet_utility::Call::<Runtime>::batch_all {
+					calls: vec![
+						pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+							finality_target: Box::new(relay_chain_header),
+							justification: grandpa_justification,
+						}.into(),
+						pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+							relayer_id_at_bridged_chain,
+							proof: message_proof,
+							messages_count: 1,
+							dispatch_weight: Weight::from_parts(1000000000, 0),
+						}.into(),
+					],
+				}.into(),
+				Box::new((
+					helpers::VerifySubmitGrandpaFinalityProofOutcome::<Runtime, GPI>::expect_best_header_hash(relay_chain_header_hash),
+					helpers::VerifySubmitMessagesProofOutcome::<Runtime, MPI>::expect_last_delivered_nonce(lane_id, 1),
+					helpers::VerifyRelayerRewarded::<Runtime>::expect_relayer_reward(
+						relayer_id_at_this_chain,
+						RewardsAccountParams::new(
+							lane_id,
+							bridged_chain_id,
+							RewardsAccountOwner::ThisChain,
+						),
+					),
+				)),
+			)]
+		},
+	);
+}
+
+/// Estimates transaction fee for default message delivery transaction (batched with required
+/// proofs) from bridged GRANDPA chain.
+pub fn can_calculate_fee_for_complex_message_delivery_transaction<Runtime, GPI, MPI, MB>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	compute_extrinsic_fee: fn(pallet_utility::Call<Runtime>) -> u128,
+) -> u128
+where
+	Runtime: BasicParachainRuntime
+		+ pallet_bridge_grandpa::Config<
+			GPI,
+			BridgedChain = UnderlyingChainOf<MessageBridgedChain<MB>>,
+		> + pallet_bridge_messages::Config<
+			MPI,
+			InboundPayload = XcmAsPlainPayload,
+			InboundRelayer = bp_runtime::AccountIdOf<MessageBridgedChain<MB>>,
+		> + pallet_utility::Config,
+	GPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain + ChainWithGrandpa,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
+		MessagesProof = FromBridgedChainMessagesProof<HashOf<MessageBridgedChain<MB>>>,
+	>,
+	bp_runtime::AccountIdOf<MessageBridgedChain<MB>>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	run_test::<Runtime, _>(collator_session_key, 1000, vec![], || {
+		// generate bridged relay chain finality, parachain heads and message proofs,
+		// to be submitted by relayer to this chain.
+		//
+		// we don't care about parameter values here, apart from the XCM message size. But we
+		// do not need to have a large message here, because we're charging for every byte of
+		// the message additionally
+		let (relay_chain_header, grandpa_justification, message_proof) =
+			test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<MB, ()>(
+				LaneId::default(),
+				vec![xcm::v3::Instruction::<()>::ClearOrigin; 1_024].into(),
+				1,
+				X2(GlobalConsensus(Polkadot), Parachain(1_000)),
+				1u32.into(),
+			);
+
+		// generate batch call that provides finality for bridged relay and parachains + message
+		// proof
+		let batch = test_data::from_grandpa_chain::make_complex_relayer_delivery_batch::<
+			Runtime,
+			GPI,
+			MPI,
+		>(
+			relay_chain_header,
+			grandpa_justification,
+			message_proof,
+			Dave.public().into(),
+		);
+		let estimated_fee = compute_extrinsic_fee(batch);
+
+		log::error!(
+			target: "bridges::estimate",
+			"Estimate fee: {:?} for single message delivery for runtime: {:?}",
+			estimated_fee,
+			Runtime::Version::get(),
+		);
+
+		estimated_fee
+	})
+}
+
+/// Estimates transaction fee for default message confirmation transaction (batched with required
+/// proofs) from bridged GRANDPA chain.
+pub fn can_calculate_fee_for_complex_message_confirmation_transaction<Runtime, GPI, MPI, MB>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	compute_extrinsic_fee: fn(pallet_utility::Call<Runtime>) -> u128,
+) -> u128
+where
+	Runtime: BasicParachainRuntime
+		+ pallet_bridge_grandpa::Config<
+			GPI,
+			BridgedChain = UnderlyingChainOf<MessageBridgedChain<MB>>,
+		> + pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	<<MB as MessageBridge>::ThisChain as bp_runtime::Chain>::AccountId: From<AccountId32>,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
+		XcmAsPlainPayload,
+		Runtime::AccountId,
+		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
+			HashOf<UnderlyingChainOf<MessageBridgedChain<MB>>>,
+		>,
+	>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	bp_runtime::AccountIdOf<MessageThisChain<MB>>: From<sp_core::sr25519::Public>,
+{
+	run_test::<Runtime, _>(collator_session_key, 1000, vec![], || {
+		// generate bridged relay chain finality, parachain heads and message proofs,
+		// to be submitted by relayer to this chain.
+		let unrewarded_relayers = UnrewardedRelayersState {
+			unrewarded_relayer_entries: 1,
+			total_messages: 1,
+			..Default::default()
+		};
+		let (relay_chain_header, grandpa_justification, message_delivery_proof) =
+			test_data::from_grandpa_chain::make_complex_relayer_confirmation_proofs::<MB, ()>(
+				LaneId::default(),
+				1u32.into(),
+				Alice.public().into(),
+				unrewarded_relayers.clone(),
+			);
+
+		// generate batch call that provides finality for bridged relay and parachains + message
+		// proof
+		let batch = test_data::from_grandpa_chain::make_complex_relayer_confirmation_batch::<
+			Runtime,
+			GPI,
+			MPI,
+		>(
+			relay_chain_header,
+			grandpa_justification,
+			message_delivery_proof,
+			unrewarded_relayers,
+		);
+		let estimated_fee = compute_extrinsic_fee(batch);
+
+		log::error!(
+			target: "bridges::estimate",
+			"Estimate fee: {:?} for single message confirmation for runtime: {:?}",
+			estimated_fee,
+			Runtime::Version::get(),
+		);
+
+		estimated_fee
+	})
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
new file mode 100644
index 00000000000..7e4ff8f874d
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs
@@ -0,0 +1,544 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities
+//! with remote parachain.
+
+use crate::{
+	test_cases::{helpers, run_test},
+	test_data,
+};
+
+use bp_messages::{
+	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
+	UnrewardedRelayersState,
+};
+use bp_polkadot_core::parachains::ParaHash;
+use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
+use bp_runtime::{Parachain, UnderlyingChainOf};
+use bridge_runtime_common::{
+	messages::{
+		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
+		BridgedChain as MessageBridgedChain, MessageBridge,
+	},
+	messages_xcm_extension::XcmAsPlainPayload,
+};
+use frame_support::traits::{Get, OnFinalize, OnInitialize, OriginTrait};
+use frame_system::pallet_prelude::BlockNumberFor;
+use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber};
+use parachains_runtimes_test_utils::{
+	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, ValidatorIdOf,
+};
+use sp_keyring::AccountKeyring::*;
+use sp_runtime::{traits::Header as HeaderT, AccountId32};
+use xcm::latest::prelude::*;
+
+/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
+/// with proofs (finality, para heads, message) independently submitted.
+/// Also verifies relayer transaction signed extensions work as intended.
+pub fn relayed_incoming_message_works<
+	Runtime,
+	AllPalletsWithoutSystem,
+	HrmpChannelOpener,
+	GPI,
+	PPI,
+	MPI,
+	MB,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	bridged_para_id: u32,
+	bridged_chain_id: bp_runtime::ChainId,
+	sibling_parachain_id: u32,
+	local_relay_chain_id: NetworkId,
+	lane_id: LaneId,
+	prepare_configuration: impl Fn(),
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ cumulus_pallet_parachain_system::Config
+		+ pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI>
+		+ pallet_bridge_relayers::Config,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain:
+		SourceHeaderChain<MessagesProof = FromBridgedChainMessagesProof<ParaHash>>,
+	<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber>,
+	ParaHash: From<
+		<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash,
+	>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as frame_system::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	helpers::relayed_incoming_message_works::<
+		Runtime,
+		AllPalletsWithoutSystem,
+		HrmpChannelOpener,
+		MPI,
+	>(
+		collator_session_key,
+		runtime_para_id,
+		sibling_parachain_id,
+		local_relay_chain_id,
+		construct_and_apply_extrinsic,
+		|relayer_id_at_this_chain,
+		 relayer_id_at_bridged_chain,
+		 message_destination,
+		 message_nonce,
+		 xcm| {
+			let para_header_number = 5;
+			let relay_header_number = 1;
+
+			prepare_configuration();
+
+			// start with bridged relay chain block#0
+			helpers::initialize_bridge_grandpa_pallet::<Runtime, GPI>(
+				test_data::initialization_data::<Runtime, GPI>(0),
+			);
+
+			// generate bridged relay chain finality, parachain heads and message proofs,
+			// to be submitted by relayer to this chain.
+			let (
+				relay_chain_header,
+				grandpa_justification,
+				parachain_head,
+				parachain_heads,
+				para_heads_proof,
+				message_proof,
+			) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
+				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
+				MB,
+				(),
+			>(
+				lane_id,
+				xcm.into(),
+				message_nonce,
+				message_destination,
+				para_header_number,
+				relay_header_number,
+				bridged_para_id,
+			);
+
+			let parachain_head_hash = parachain_head.hash();
+			let relay_chain_header_hash = relay_chain_header.hash();
+			let relay_chain_header_number = *relay_chain_header.number();
+			vec![
+				(
+					pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+						finality_target: Box::new(relay_chain_header),
+						justification: grandpa_justification,
+					}.into(),
+					helpers::VerifySubmitGrandpaFinalityProofOutcome::<Runtime, GPI>::expect_best_header_hash(relay_chain_header_hash),
+				),
+				(
+					pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
+						at_relay_block: (relay_chain_header_number, relay_chain_header_hash),
+						parachains: parachain_heads,
+						parachain_heads_proof: para_heads_proof,
+					}.into(),
+					helpers::VerifySubmitParachainHeaderProofOutcome::<Runtime, PPI>::expect_best_header_hash(bridged_para_id, parachain_head_hash),
+				),
+				(
+					pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+						relayer_id_at_bridged_chain,
+						proof: message_proof,
+						messages_count: 1,
+						dispatch_weight: Weight::from_parts(1000000000, 0),
+					}.into(),
+					Box::new((
+						helpers::VerifySubmitMessagesProofOutcome::<Runtime, MPI>::expect_last_delivered_nonce(lane_id, 1),
+						helpers::VerifyRelayerRewarded::<Runtime>::expect_relayer_reward(
+							relayer_id_at_this_chain,
+							RewardsAccountParams::new(
+								lane_id,
+								bridged_chain_id,
+								RewardsAccountOwner::ThisChain,
+							),
+						),
+					)),
+				),
+			]
+		},
+	);
+}
+
+/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
+/// with proofs (finality, para heads, message) batched together in signed extrinsic.
+/// Also verifies relayer transaction signed extensions work as intended.
+pub fn complex_relay_extrinsic_works<
+	Runtime,
+	AllPalletsWithoutSystem,
+	XcmConfig,
+	HrmpChannelOpener,
+	GPI,
+	PPI,
+	MPI,
+	MB,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	bridged_para_id: u32,
+	sibling_parachain_id: u32,
+	bridged_chain_id: bp_runtime::ChainId,
+	local_relay_chain_id: NetworkId,
+	lane_id: LaneId,
+	prepare_configuration: impl Fn(),
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ cumulus_pallet_parachain_system::Config
+		+ pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI>
+		+ pallet_bridge_relayers::Config
+		+ pallet_utility::Config,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain:
+		SourceHeaderChain<MessagesProof = FromBridgedChainMessagesProof<ParaHash>>,
+	<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber>,
+	ParaHash: From<
+		<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash,
+	>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+	<Runtime as frame_system::Config>::RuntimeCall: From<pallet_utility::Call<Runtime>>,
+{
+	helpers::relayed_incoming_message_works::<
+		Runtime,
+		AllPalletsWithoutSystem,
+		HrmpChannelOpener,
+		MPI,
+	>(
+		collator_session_key,
+		runtime_para_id,
+		sibling_parachain_id,
+		local_relay_chain_id,
+		construct_and_apply_extrinsic,
+		|relayer_id_at_this_chain,
+		 relayer_id_at_bridged_chain,
+		 message_destination,
+		 message_nonce,
+		 xcm| {
+			let para_header_number = 5;
+			let relay_header_number = 1;
+
+			prepare_configuration();
+
+			// start with bridged relay chain block#0
+			helpers::initialize_bridge_grandpa_pallet::<Runtime, GPI>(
+				test_data::initialization_data::<Runtime, GPI>(0),
+			);
+
+			// generate bridged relay chain finality, parachain heads and message proofs,
+			// to be submitted by relayer to this chain.
+			let (
+				relay_chain_header,
+				grandpa_justification,
+				parachain_head,
+				parachain_heads,
+				para_heads_proof,
+				message_proof,
+			) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
+				<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
+				MB,
+				(),
+			>(
+				lane_id,
+				xcm.into(),
+				message_nonce,
+				message_destination,
+				para_header_number,
+				relay_header_number,
+				bridged_para_id,
+			);
+
+			let parachain_head_hash = parachain_head.hash();
+			let relay_chain_header_hash = relay_chain_header.hash();
+			let relay_chain_header_number = *relay_chain_header.number();
+			vec![(
+				pallet_utility::Call::<Runtime>::batch_all {
+					calls: vec![
+						pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+							finality_target: Box::new(relay_chain_header),
+							justification: grandpa_justification,
+						}.into(),
+						pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
+							at_relay_block: (relay_chain_header_number, relay_chain_header_hash),
+							parachains: parachain_heads,
+							parachain_heads_proof: para_heads_proof,
+						}.into(),
+						pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+							relayer_id_at_bridged_chain,
+							proof: message_proof,
+							messages_count: 1,
+							dispatch_weight: Weight::from_parts(1000000000, 0),
+						}.into(),
+					],
+				}.into(),
+				Box::new((
+					helpers::VerifySubmitGrandpaFinalityProofOutcome::<Runtime, GPI>::expect_best_header_hash(relay_chain_header_hash),
+					helpers::VerifySubmitParachainHeaderProofOutcome::<Runtime, PPI>::expect_best_header_hash(bridged_para_id, parachain_head_hash),
+					helpers::VerifySubmitMessagesProofOutcome::<Runtime, MPI>::expect_last_delivered_nonce(lane_id, 1),
+					helpers::VerifyRelayerRewarded::<Runtime>::expect_relayer_reward(
+						relayer_id_at_this_chain,
+						RewardsAccountParams::new(
+							lane_id,
+							bridged_chain_id,
+							RewardsAccountOwner::ThisChain,
+						),
+					),
+				)),
+			)]
+		},
+	);
+}
+
+/// Estimates transaction fee for default message delivery transaction (batched with required
+/// proofs) from bridged parachain.
+pub fn can_calculate_fee_for_complex_message_delivery_transaction<Runtime, GPI, PPI, MPI, MB>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	compute_extrinsic_fee: fn(pallet_utility::Call::<Runtime>) -> u128,
+) -> u128
+where
+	Runtime: frame_system::Config
+		+ pallet_balances::Config
+		+ pallet_session::Config
+		+ pallet_xcm::Config
+		+ parachain_info::Config
+		+ pallet_collator_selection::Config
+		+ cumulus_pallet_parachain_system::Config
+		+ pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
+		From<FromBridgedChainMessagesProof<ParaHash>>,
+		<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain: bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber>,
+	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
+	<Runtime as frame_system::Config>::AccountId:
+	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as pallet_utility::Config>::RuntimeCall:
+	From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+	+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+	+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	run_test::<Runtime, _>(collator_session_key, 1000, vec![], || {
+		// generate bridged relay chain finality, parachain heads and message proofs,
+		// to be submitted by relayer to this chain.
+		//
+		// we don't care about parameter values here, apart from the XCM message size. But we
+		// do not need to have a large message here, because we're charging for every byte of
+		// the message additionally
+		let (
+			relay_chain_header,
+			grandpa_justification,
+			_,
+			parachain_heads,
+			para_heads_proof,
+			message_proof,
+		) = test_data::from_parachain::make_complex_relayer_delivery_proofs::<
+			<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
+			MB,
+			(),
+		>(
+			LaneId::default(),
+			vec![xcm::v3::Instruction::<()>::ClearOrigin; 1_024].into(),
+			1,
+			X2(GlobalConsensus(Polkadot), Parachain(1_000)),
+			1,
+			5,
+			1_000,
+		);
+
+		// generate batch call that provides finality for bridged relay and parachains + message
+		// proof
+		let batch = test_data::from_parachain::make_complex_relayer_delivery_batch::<
+			Runtime,
+			GPI,
+			PPI,
+			MPI,
+		>(
+			relay_chain_header,
+			grandpa_justification,
+			parachain_heads,
+			para_heads_proof,
+			message_proof,
+			Dave.public().into(),
+		);
+		let estimated_fee = compute_extrinsic_fee(batch);
+
+		log::error!(
+			target: "bridges::estimate",
+			"Estimate fee: {:?} for single message delivery for runtime: {:?}",
+			estimated_fee,
+			Runtime::Version::get(),
+		);
+
+		estimated_fee
+	})
+}
+
+/// Estimates transaction fee for default message confirmation transaction (batched with required
+/// proofs) from bridged parachain.
+pub fn can_calculate_fee_for_complex_message_confirmation_transaction<Runtime, GPI, PPI, MPI, MB>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	compute_extrinsic_fee: fn(pallet_utility::Call::<Runtime>) -> u128,
+) -> u128
+where
+	Runtime: frame_system::Config
+		+ pallet_balances::Config
+		+ pallet_session::Config
+		+ pallet_xcm::Config
+		+ parachain_info::Config
+		+ pallet_collator_selection::Config
+		+ cumulus_pallet_parachain_system::Config
+		+ pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	<<MB as MessageBridge>::ThisChain as bp_runtime::Chain>::AccountId: From<AccountId32>,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
+		From<FromBridgedChainMessagesProof<ParaHash>>,
+	<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain: bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber>,
+	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
+	<Runtime as frame_system::Config>::AccountId:
+	Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	AccountIdOf<Runtime>: From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+		<<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain as TargetHeaderChain<
+			XcmAsPlainPayload,
+			Runtime::AccountId,
+		>>::MessagesDeliveryProof: From<FromBridgedChainMessagesDeliveryProof<ParaHash>>,
+	<Runtime as pallet_utility::Config>::RuntimeCall:
+	From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+	+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+	+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	run_test::<Runtime, _>(collator_session_key, 1000, vec![], || {
+		// generate bridged relay chain finality, parachain heads and message proofs,
+		// to be submitted by relayer to this chain.
+		let unrewarded_relayers = UnrewardedRelayersState {
+			unrewarded_relayer_entries: 1,
+			total_messages: 1,
+			..Default::default()
+		};
+		let (
+			relay_chain_header,
+			grandpa_justification,
+			_,
+			parachain_heads,
+			para_heads_proof,
+			message_delivery_proof,
+		) = test_data::from_parachain::make_complex_relayer_confirmation_proofs::<
+			<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain,
+			MB,
+			(),
+		>(LaneId::default(), 1, 5, 1_000, Alice.public().into(), unrewarded_relayers.clone());
+
+		// generate batch call that provides finality for bridged relay and parachains + message
+		// proof
+		let batch = test_data::from_parachain::make_complex_relayer_confirmation_batch::<
+			Runtime,
+			GPI,
+			PPI,
+			MPI,
+		>(
+			relay_chain_header,
+			grandpa_justification,
+			parachain_heads,
+			para_heads_proof,
+			message_delivery_proof,
+			unrewarded_relayers,
+		);
+		let estimated_fee = compute_extrinsic_fee(batch);
+
+		log::error!(
+			target: "bridges::estimate",
+			"Estimate fee: {:?} for single message confirmation for runtime: {:?}",
+			estimated_fee,
+			Runtime::Version::get(),
+		);
+
+		estimated_fee
+	})
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
new file mode 100644
index 00000000000..192aa017fff
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs
@@ -0,0 +1,340 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Module contains tests code, that is shared by all types of bridges
+
+use crate::test_cases::{run_test, RuntimeHelper};
+
+use asset_test_utils::BasicParachainRuntime;
+use bp_messages::{LaneId, MessageNonce};
+use bp_polkadot_core::parachains::{ParaHash, ParaId};
+use bp_relayers::RewardsAccountParams;
+use frame_support::{
+	assert_ok,
+	traits::{OnFinalize, OnInitialize, PalletInfoAccess},
+};
+use frame_system::pallet_prelude::BlockNumberFor;
+use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader};
+use parachains_common::AccountId;
+use parachains_runtimes_test_utils::{
+	mock_open_hrmp_channel, AccountIdOf, CollatorSessionKeys, ValidatorIdOf,
+};
+use sp_core::Get;
+use sp_keyring::AccountKeyring::*;
+use sp_runtime::AccountId32;
+use sp_std::marker::PhantomData;
+use xcm::latest::prelude::*;
+
+#[impl_trait_for_tuples::impl_for_tuples(30)]
+pub trait VerifyTransactionOutcome {
+	fn verify_outcome(&self);
+}
+
+impl VerifyTransactionOutcome for Box<dyn VerifyTransactionOutcome> {
+	fn verify_outcome(&self) {
+		VerifyTransactionOutcome::verify_outcome(&**self)
+	}
+}
+
+/// Checks that the best finalized header hash in the bridge GRANDPA pallet equals to given one.
+pub struct VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>,
+	GPI: 'static,
+{
+	expected_best_hash: BridgedBlockHash<Runtime, GPI>,
+}
+
+impl<Runtime, GPI> VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>,
+	GPI: 'static,
+{
+	/// Expect given header hash to be the best after transaction.
+	pub fn expect_best_header_hash(
+		expected_best_hash: BridgedBlockHash<Runtime, GPI>,
+	) -> Box<dyn VerifyTransactionOutcome> {
+		Box::new(Self { expected_best_hash })
+	}
+}
+
+impl<Runtime, GPI> VerifyTransactionOutcome
+	for VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>,
+	GPI: 'static,
+{
+	fn verify_outcome(&self) {
+		assert_eq!(
+			pallet_bridge_grandpa::BestFinalized::<Runtime, GPI>::get().unwrap().1,
+			self.expected_best_hash
+		);
+		assert!(pallet_bridge_grandpa::ImportedHeaders::<Runtime, GPI>::contains_key(
+			self.expected_best_hash
+		));
+	}
+}
+
+/// Checks that the best parachain header hash in the bridge parachains pallet equals to given one.
+pub struct VerifySubmitParachainHeaderProofOutcome<Runtime, PPI> {
+	bridged_para_id: u32,
+	expected_best_hash: ParaHash,
+	_marker: PhantomData<(Runtime, PPI)>,
+}
+
+impl<Runtime, PPI> VerifySubmitParachainHeaderProofOutcome<Runtime, PPI>
+where
+	Runtime: pallet_bridge_parachains::Config<PPI>,
+	PPI: 'static,
+{
+	/// Expect given header hash to be the best after transaction.
+	pub fn expect_best_header_hash(
+		bridged_para_id: u32,
+		expected_best_hash: ParaHash,
+	) -> Box<dyn VerifyTransactionOutcome> {
+		Box::new(Self { bridged_para_id, expected_best_hash, _marker: PhantomData })
+	}
+}
+
+impl<Runtime, PPI> VerifyTransactionOutcome
+	for VerifySubmitParachainHeaderProofOutcome<Runtime, PPI>
+where
+	Runtime: pallet_bridge_parachains::Config<PPI>,
+	PPI: 'static,
+{
+	fn verify_outcome(&self) {
+		assert_eq!(
+			pallet_bridge_parachains::ParasInfo::<Runtime, PPI>::get(ParaId(self.bridged_para_id))
+				.map(|info| info.best_head_hash.head_hash),
+			Some(self.expected_best_hash),
+		);
+	}
+}
+
+/// Checks that the latest delivered nonce in the bridge messages pallet equals to given one.
+pub struct VerifySubmitMessagesProofOutcome<Runtime, MPI> {
+	lane: LaneId,
+	expected_nonce: MessageNonce,
+	_marker: PhantomData<(Runtime, MPI)>,
+}
+
+impl<Runtime, MPI> VerifySubmitMessagesProofOutcome<Runtime, MPI>
+where
+	Runtime: pallet_bridge_messages::Config<MPI>,
+	MPI: 'static,
+{
+	/// Expect given delivered nonce to be the latest after transaction.
+	pub fn expect_last_delivered_nonce(
+		lane: LaneId,
+		expected_nonce: MessageNonce,
+	) -> Box<dyn VerifyTransactionOutcome> {
+		Box::new(Self { lane, expected_nonce, _marker: PhantomData })
+	}
+}
+
+impl<Runtime, MPI> VerifyTransactionOutcome for VerifySubmitMessagesProofOutcome<Runtime, MPI>
+where
+	Runtime: pallet_bridge_messages::Config<MPI>,
+	MPI: 'static,
+{
+	fn verify_outcome(&self) {
+		assert_eq!(
+			pallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(self.lane)
+				.last_delivered_nonce(),
+			self.expected_nonce,
+		);
+	}
+}
+
+/// Verifies that relayer is rewarded at this chain.
+pub struct VerifyRelayerRewarded<Runtime: frame_system::Config> {
+	relayer: Runtime::AccountId,
+	reward_params: RewardsAccountParams,
+}
+
+impl<Runtime> VerifyRelayerRewarded<Runtime>
+where
+	Runtime: pallet_bridge_relayers::Config,
+{
+	/// Expect given delivered nonce to be the latest after transaction.
+	pub fn expect_relayer_reward(
+		relayer: Runtime::AccountId,
+		reward_params: RewardsAccountParams,
+	) -> Box<dyn VerifyTransactionOutcome> {
+		Box::new(Self { relayer, reward_params })
+	}
+}
+
+impl<Runtime> VerifyTransactionOutcome for VerifyRelayerRewarded<Runtime>
+where
+	Runtime: pallet_bridge_relayers::Config,
+{
+	fn verify_outcome(&self) {
+		assert!(pallet_bridge_relayers::RelayerRewards::<Runtime>::get(
+			&self.relayer,
+			&self.reward_params,
+		)
+		.is_some());
+	}
+}
+
+/// Initialize bridge GRANDPA pallet.
+pub(crate) fn initialize_bridge_grandpa_pallet<Runtime, GPI>(
+	init_data: bp_header_chain::InitializationData<BridgedHeader<Runtime, GPI>>,
+) where
+	Runtime: pallet_bridge_grandpa::Config<GPI>,
+{
+	pallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
+		RuntimeHelper::<Runtime>::root_origin(),
+		init_data,
+	)
+	.unwrap();
+}
+
+/// Runtime calls and their verifiers.
+pub type CallsAndVerifiers<Runtime> =
+	Vec<(<Runtime as frame_system::Config>::RuntimeCall, Box<dyn VerifyTransactionOutcome>)>;
+
+/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
+/// with proofs (finality, message) independently submitted.
+pub fn relayed_incoming_message_works<Runtime, AllPalletsWithoutSystem, HrmpChannelOpener, MPI>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	sibling_parachain_id: u32,
+	local_relay_chain_id: NetworkId,
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+	prepare_message_proof_import: impl FnOnce(
+		Runtime::AccountId,
+		Runtime::InboundRelayer,
+		InteriorMultiLocation,
+		MessageNonce,
+		Xcm<()>,
+	) -> CallsAndVerifiers<Runtime>,
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ pallet_bridge_messages::Config<MPI>,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	MPI: 'static,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	AccountIdOf<Runtime>: From<AccountId32> + From<sp_core::sr25519::Public>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+{
+	let relayer_at_target = Bob;
+	let relayer_id_on_target: AccountId32 = relayer_at_target.public().into();
+	let relayer_at_source = Dave;
+	let relayer_id_on_source: AccountId32 = relayer_at_source.public().into();
+
+	assert_ne!(runtime_para_id, sibling_parachain_id);
+
+	run_test::<Runtime, _>(
+		collator_session_key,
+		runtime_para_id,
+		vec![(
+			relayer_id_on_target.clone().into(),
+			Runtime::ExistentialDeposit::get() * 100000u32.into(),
+		)],
+		|| {
+			let mut alice = [0u8; 32];
+			alice[0] = 1;
+
+			let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
+				2,
+				AccountId::from(alice).into(),
+			);
+			mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
+				runtime_para_id.into(),
+				sibling_parachain_id.into(),
+				included_head,
+				&alice,
+			);
+
+			// set up relayer details and proofs
+
+			let message_destination =
+				X2(GlobalConsensus(local_relay_chain_id), Parachain(sibling_parachain_id));
+			// some random numbers (checked by test)
+			let message_nonce = 1;
+
+			let xcm = vec![xcm::v3::Instruction::<()>::ClearOrigin; 42];
+			let expected_dispatch = xcm::latest::Xcm::<()>({
+				let mut expected_instructions = xcm.clone();
+				// dispatch prepends bridge pallet instance
+				expected_instructions.insert(
+					0,
+					DescendOrigin(X1(PalletInstance(
+						<pallet_bridge_messages::Pallet<Runtime, MPI> as PalletInfoAccess>::index()
+							as u8,
+					))),
+				);
+				expected_instructions
+			});
+
+			execute_and_verify_calls::<Runtime>(
+				relayer_at_target,
+				construct_and_apply_extrinsic,
+				prepare_message_proof_import(
+					relayer_id_on_target.clone().into(),
+					relayer_id_on_source.clone().into(),
+					message_destination,
+					message_nonce,
+					xcm.clone().into(),
+				),
+			);
+
+			// verify that imported XCM contains original message
+			let imported_xcm =
+				RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
+					sibling_parachain_id.into(),
+				)
+				.unwrap();
+			let dispatched = xcm::latest::Xcm::<()>::try_from(imported_xcm).unwrap();
+			let mut dispatched_clone = dispatched.clone();
+			for (idx, expected_instr) in expected_dispatch.0.iter().enumerate() {
+				assert_eq!(expected_instr, &dispatched.0[idx]);
+				assert_eq!(expected_instr, &dispatched_clone.0.remove(0));
+			}
+			match dispatched_clone.0.len() {
+				0 => (),
+				1 => assert!(matches!(dispatched_clone.0[0], SetTopic(_))),
+				count => assert!(false, "Unexpected messages count: {:?}", count),
+			}
+		},
+	)
+}
+
+/// Execute every call and verify its outcome.
+fn execute_and_verify_calls<Runtime: frame_system::Config>(
+	submitter: sp_keyring::AccountKeyring,
+	construct_and_apply_extrinsic: fn(
+		sp_keyring::AccountKeyring,
+		<Runtime as frame_system::Config>::RuntimeCall,
+	) -> sp_runtime::DispatchOutcome,
+	calls_and_verifiers: CallsAndVerifiers<Runtime>,
+) {
+	for (call, verifier) in calls_and_verifiers {
+		let dispatch_outcome = construct_and_apply_extrinsic(submitter, call);
+		assert_ok!(dispatch_outcome);
+		verifier.verify_outcome();
+	}
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
new file mode 100644
index 00000000000..cc347ecf30d
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs
@@ -0,0 +1,485 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
+//!
+//! This file contains tests, suitable for all bridge runtimes. See `from_parachain` and
+//! `from_grandpa_chain` submodules for tests, that are specific to the bridged chain type.
+
+pub mod from_grandpa_chain;
+pub mod from_parachain;
+
+pub(crate) mod helpers;
+
+use crate::test_data;
+
+use asset_test_utils::BasicParachainRuntime;
+use bp_messages::{
+	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
+	LaneId, MessageKey, OutboundLaneData,
+};
+use bridge_runtime_common::messages_xcm_extension::{
+	XcmAsPlainPayload, XcmBlobMessageDispatchResult,
+};
+use codec::Encode;
+use frame_support::{
+	assert_ok,
+	traits::{Get, OnFinalize, OnInitialize, OriginTrait},
+};
+use frame_system::pallet_prelude::BlockNumberFor;
+use parachains_common::AccountId;
+use parachains_runtimes_test_utils::{
+	mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf,
+	XcmReceivedFrom,
+};
+use sp_runtime::{traits::Zero, AccountId32};
+use xcm::{latest::prelude::*, AlwaysLatest};
+use xcm_builder::DispatchBlobError;
+use xcm_executor::{
+	traits::{TransactAsset, WeightBounds},
+	XcmExecutor,
+};
+
+// Re-export test_case from assets
+pub use asset_test_utils::include_teleports_for_native_asset_works;
+
+pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
+	parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
+
+// Re-export test_case from `parachains-runtimes-test-utils`
+pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
+
+/// Prepare default runtime storage and run test within this context.
+pub fn run_test<Runtime, T>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	balances: Vec<(Runtime::AccountId, Runtime::Balance)>,
+	test: impl FnOnce() -> T,
+) -> T
+where
+	Runtime: BasicParachainRuntime,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+{
+	ExtBuilder::<Runtime>::default()
+		.with_collators(collator_session_key.collators())
+		.with_session_keys(collator_session_key.session_keys())
+		.with_safe_xcm_version(XCM_VERSION)
+		.with_para_id(runtime_para_id.into())
+		.with_balances(balances)
+		.with_tracing()
+		.build()
+		.execute_with(|| test())
+}
+
+/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
+pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	runtime_call_encode: Box<
+		dyn Fn(pallet_bridge_grandpa::Call<Runtime, GrandpaPalletInstance>) -> Vec<u8>,
+	>,
+) where
+	Runtime: BasicParachainRuntime + pallet_bridge_grandpa::Config<GrandpaPalletInstance>,
+	GrandpaPalletInstance: 'static,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+{
+	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
+		// check mode before
+		assert_eq!(
+			pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
+			Err(())
+		);
+
+		// encode `initialize` call
+		let initialize_call = runtime_call_encode(pallet_bridge_grandpa::Call::<
+			Runtime,
+			GrandpaPalletInstance,
+		>::initialize {
+			init_data: test_data::initialization_data::<Runtime, GrandpaPalletInstance>(12345),
+		});
+
+		// overestimate - check weight for `pallet_bridge_grandpa::Pallet::initialize()` call
+		let require_weight_at_most =
+			<Runtime as frame_system::Config>::DbWeight::get().reads_writes(7, 7);
+
+		// execute XCM with Transacts to `initialize bridge` as governance does
+		assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
+			initialize_call,
+			require_weight_at_most
+		)
+		.ensure_complete());
+
+		// check mode after
+		assert_eq!(
+			pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
+			Ok(bp_runtime::BasicOperatingMode::Normal)
+		);
+	})
+}
+
+/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
+/// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
+/// For SystemParachains we expect unpaid execution.
+pub fn handle_export_message_from_system_parachain_to_outbound_queue_works<
+	Runtime,
+	XcmConfig,
+	MessagesPalletInstance,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	sibling_parachain_id: u32,
+	unwrap_pallet_bridge_messages_event: Box<
+		dyn Fn(Vec<u8>) -> Option<pallet_bridge_messages::Event<Runtime, MessagesPalletInstance>>,
+	>,
+	export_message_instruction: fn() -> Instruction<XcmConfig::RuntimeCall>,
+	expected_lane_id: LaneId,
+	existential_deposit: Option<MultiAsset>,
+	maybe_paid_export_message: Option<MultiAsset>,
+	prepare_configuration: impl Fn(),
+) where
+	Runtime: BasicParachainRuntime + pallet_bridge_messages::Config<MessagesPalletInstance>,
+	XcmConfig: xcm_executor::Config,
+	MessagesPalletInstance: 'static,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+{
+	assert_ne!(runtime_para_id, sibling_parachain_id);
+	let sibling_parachain_location = MultiLocation::new(1, Parachain(sibling_parachain_id));
+
+	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
+		prepare_configuration();
+
+		// check queue before
+		assert_eq!(
+			pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
+				expected_lane_id
+			),
+			Err(())
+		);
+
+		// prepare `ExportMessage`
+		let xcm = if let Some(fee) = maybe_paid_export_message {
+			// deposit ED to origin (if needed)
+			if let Some(ed) = existential_deposit {
+				XcmConfig::AssetTransactor::deposit_asset(
+					&ed,
+					&sibling_parachain_location,
+					Some(&XcmContext::with_message_id([0; 32])),
+				)
+				.expect("deposited ed");
+			}
+			// deposit fee to origin
+			XcmConfig::AssetTransactor::deposit_asset(
+				&fee,
+				&sibling_parachain_location,
+				Some(&XcmContext::with_message_id([0; 32])),
+			)
+			.expect("deposited fee");
+
+			Xcm(vec![
+				WithdrawAsset(MultiAssets::from(vec![fee.clone()])),
+				BuyExecution { fees: fee, weight_limit: Unlimited },
+				export_message_instruction(),
+			])
+		} else {
+			Xcm(vec![
+				UnpaidExecution { weight_limit: Unlimited, check_origin: None },
+				export_message_instruction(),
+			])
+		};
+
+		// execute XCM
+		let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
+		assert_ok!(XcmExecutor::<XcmConfig>::execute_xcm(
+			sibling_parachain_location,
+			xcm,
+			hash,
+			RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
+		)
+		.ensure_complete());
+
+		// check queue after
+		assert_eq!(
+			pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
+				expected_lane_id
+			),
+			Ok(OutboundLaneData {
+				oldest_unpruned_nonce: 1,
+				latest_received_nonce: 0,
+				latest_generated_nonce: 1,
+			})
+		);
+
+		// check events
+		let mut events = <frame_system::Pallet<Runtime>>::events()
+			.into_iter()
+			.filter_map(|e| unwrap_pallet_bridge_messages_event(e.event.encode()));
+		assert!(events.any(|e| matches!(e, pallet_bridge_messages::Event::MessageAccepted { .. })));
+	})
+}
+
+/// Test-case makes sure that Runtime can route XCM messages received in inbound queue,
+/// We just test here `MessageDispatch` configuration.
+/// We expect that runtime can route messages:
+///     1. to Parent (relay chain)
+///     2. to Sibling parachain
+pub fn message_dispatch_routing_works<
+	Runtime,
+	AllPalletsWithoutSystem,
+	XcmConfig,
+	HrmpChannelOpener,
+	MessagesPalletInstance,
+	RuntimeNetwork,
+	BridgedNetwork,
+	NetworkDistanceAsParentCount,
+>(
+	collator_session_key: CollatorSessionKeys<Runtime>,
+	runtime_para_id: u32,
+	sibling_parachain_id: u32,
+	unwrap_cumulus_pallet_parachain_system_event: Box<
+		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_parachain_system::Event<Runtime>>,
+	>,
+	unwrap_cumulus_pallet_xcmp_queue_event: Box<
+		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
+	>,
+	expected_lane_id: LaneId,
+	prepare_configuration: impl Fn(),
+) where
+	Runtime: BasicParachainRuntime
+		+ cumulus_pallet_xcmp_queue::Config
+		+ pallet_bridge_messages::Config<MessagesPalletInstance, InboundPayload = XcmAsPlainPayload>,
+	AllPalletsWithoutSystem:
+		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
+	<Runtime as frame_system::Config>::AccountId:
+		Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
+	XcmConfig: xcm_executor::Config,
+	MessagesPalletInstance: 'static,
+	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
+	<Runtime as frame_system::Config>::AccountId: From<AccountId32>,
+	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
+		Call = cumulus_pallet_parachain_system::Call<Runtime>,
+	>,
+	RuntimeNetwork: Get<NetworkId>,
+	BridgedNetwork: Get<NetworkId>,
+	NetworkDistanceAsParentCount: Get<u8>,
+{
+	struct NetworkWithParentCount<N, C>(core::marker::PhantomData<(N, C)>);
+	impl<N: Get<NetworkId>, C: Get<u8>> Get<MultiLocation> for NetworkWithParentCount<N, C> {
+		fn get() -> MultiLocation {
+			MultiLocation { parents: C::get(), interior: X1(GlobalConsensus(N::get())) }
+		}
+	}
+
+	assert_ne!(runtime_para_id, sibling_parachain_id);
+
+	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
+		prepare_configuration();
+
+		let mut alice = [0u8; 32];
+		alice[0] = 1;
+
+		let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
+			2,
+			AccountId::from(alice).into(),
+		);
+		// 1. this message is sent from other global consensus with destination of this Runtime
+		//    relay chain (UMP)
+		let bridging_message = test_data::simulate_message_exporter_on_bridged_chain::<
+			BridgedNetwork,
+			NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
+			AlwaysLatest,
+		>((RuntimeNetwork::get(), Here));
+		let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
+			test_data::dispatch_message(expected_lane_id, 1, bridging_message)
+		);
+		assert_eq!(
+			format!("{:?}", result.dispatch_level_result),
+			format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
+		);
+
+		// check events - UpwardMessageSent
+		let mut events = <frame_system::Pallet<Runtime>>::events()
+			.into_iter()
+			.filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode()));
+		assert!(events.any(|e| matches!(
+			e,
+			cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
+		)));
+
+		// 2. this message is sent from other global consensus with destination of this Runtime
+		//    sibling parachain (HRMP)
+		let bridging_message = test_data::simulate_message_exporter_on_bridged_chain::<
+			BridgedNetwork,
+			NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
+			AlwaysLatest,
+		>((RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))));
+
+		// 2.1. WITHOUT opened hrmp channel -> RoutingError
+		let result =
+			<<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
+				DispatchMessage {
+					key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
+					data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
+				}
+			);
+		assert_eq!(
+			format!("{:?}", result.dispatch_level_result),
+			format!(
+				"{:?}",
+				XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))
+			)
+		);
+
+		// check events - no XcmpMessageSent
+		assert_eq!(
+			<frame_system::Pallet<Runtime>>::events()
+				.into_iter()
+				.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
+				.count(),
+			0
+		);
+
+		// 2.1. WITH hrmp channel -> Ok
+		mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
+			runtime_para_id.into(),
+			sibling_parachain_id.into(),
+			included_head,
+			&alice,
+		);
+		let result = <<Runtime as pallet_bridge_messages::Config<MessagesPalletInstance>>::MessageDispatch>::dispatch(
+			DispatchMessage {
+				key: MessageKey { lane_id: expected_lane_id, nonce: 1 },
+				data: DispatchMessageData { payload: Ok(bridging_message) },
+			}
+		);
+		assert_eq!(
+			format!("{:?}", result.dispatch_level_result),
+			format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
+		);
+
+		// check events - XcmpMessageSent
+		let mut events = <frame_system::Pallet<Runtime>>::events()
+			.into_iter()
+			.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()));
+		assert!(
+			events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
+		);
+	})
+}
+
+/// Estimates XCM execution fee for paid `ExportMessage` processing.
+pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer<
+	Runtime,
+	XcmConfig,
+	WeightToFee,
+>() -> u128
+where
+	Runtime: frame_system::Config + pallet_balances::Config,
+	XcmConfig: xcm_executor::Config,
+	WeightToFee: frame_support::weights::WeightToFee<Balance = BalanceOf<Runtime>>,
+	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
+{
+	// data here are not relevant for weighing
+	let mut xcm = Xcm(vec![
+		WithdrawAsset(MultiAssets::from(vec![MultiAsset {
+			id: Concrete(MultiLocation { parents: 1, interior: Here }),
+			fun: Fungible(34333299),
+		}])),
+		BuyExecution {
+			fees: MultiAsset {
+				id: Concrete(MultiLocation { parents: 1, interior: Here }),
+				fun: Fungible(34333299),
+			},
+			weight_limit: Unlimited,
+		},
+		ExportMessage {
+			network: Polkadot,
+			destination: X1(Parachain(1000)),
+			xcm: Xcm(vec![
+				ReserveAssetDeposited(MultiAssets::from(vec![MultiAsset {
+					id: Concrete(MultiLocation {
+						parents: 2,
+						interior: X1(GlobalConsensus(Kusama)),
+					}),
+					fun: Fungible(1000000000000),
+				}])),
+				ClearOrigin,
+				BuyExecution {
+					fees: MultiAsset {
+						id: Concrete(MultiLocation {
+							parents: 2,
+							interior: X1(GlobalConsensus(Kusama)),
+						}),
+						fun: Fungible(1000000000000),
+					},
+					weight_limit: Unlimited,
+				},
+				DepositAsset {
+					assets: Wild(AllCounted(1)),
+					beneficiary: MultiLocation {
+						parents: 0,
+						interior: X1(xcm::latest::prelude::AccountId32 {
+							network: None,
+							id: [
+								212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159,
+								214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165,
+								109, 162, 125,
+							],
+						}),
+					},
+				},
+				SetTopic([
+					116, 82, 194, 132, 171, 114, 217, 165, 23, 37, 161, 177, 165, 179, 247, 114,
+					137, 101, 147, 70, 28, 157, 168, 32, 154, 63, 74, 228, 152, 180, 5, 63,
+				]),
+			]),
+		},
+		DepositAsset {
+			assets: Wild(All),
+			beneficiary: MultiLocation { parents: 1, interior: X1(Parachain(1000)) },
+		},
+		SetTopic([
+			36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219,
+			157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122,
+		]),
+	]);
+
+	// get weight
+	let weight = XcmConfig::Weigher::weight(&mut xcm);
+	assert_ok!(weight);
+	let weight = weight.unwrap();
+	// check if sane
+	let max_expected = Runtime::BlockWeights::get().max_block / 10;
+	assert!(
+		weight.all_lte(max_expected),
+		"calculated weight: {:?}, max_expected: {:?}",
+		weight,
+		max_expected
+	);
+
+	// check fee, should not be 0
+	let estimated_fee = WeightToFee::weight_to_fee(&weight);
+	assert!(estimated_fee > BalanceOf::<Runtime>::zero());
+
+	sp_tracing::try_init_simple();
+	log::error!(
+		target: "bridges::estimate",
+		"Estimate fee: {:?} for `ExportMessage` for runtime: {:?}",
+		estimated_fee,
+		Runtime::Version::get(),
+	);
+
+	estimated_fee.into()
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
new file mode 100644
index 00000000000..017ec0fd540
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs
@@ -0,0 +1,244 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Generating test data for bridges with remote GRANDPA chains.
+
+use crate::test_data::prepare_inbound_xcm;
+
+use bp_messages::{
+	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId, MessageNonce,
+	UnrewardedRelayersState,
+};
+use bp_runtime::{AccountIdOf, BlockNumberOf, HeaderOf, StorageProofSize, UnderlyingChainOf};
+use bp_test_utils::make_default_justification;
+use bridge_runtime_common::{
+	messages::{
+		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
+		BridgedChain as MessageBridgedChain, MessageBridge, ThisChain as MessageThisChain,
+	},
+	messages_generation::{
+		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
+		prepare_messages_storage_proof,
+	},
+	messages_xcm_extension::XcmAsPlainPayload,
+};
+use codec::Encode;
+use pallet_bridge_grandpa::{BridgedChain, BridgedHeader};
+use sp_runtime::traits::Header as HeaderT;
+use xcm::latest::prelude::*;
+
+use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
+use bp_messages::{DeliveredMessages, InboundLaneData, UnrewardedRelayer};
+use bp_runtime::HashOf;
+use sp_runtime::DigestItem;
+
+/// Prepare a batch call with bridged GRANDPA finality and message proof.
+pub fn make_complex_relayer_delivery_batch<Runtime, GPI, MPI>(
+	bridged_header: BridgedHeader<Runtime, GPI>,
+	bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
+	message_proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
+	relayer_id_at_bridged_chain: AccountIdOf<BridgedChain<Runtime, GPI>>,
+) -> pallet_utility::Call<Runtime>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_messages::Config<
+			MPI,
+			InboundPayload = XcmAsPlainPayload,
+			InboundRelayer = AccountIdOf<BridgedChain<Runtime, GPI>>,
+		> + pallet_utility::Config,
+	GPI: 'static,
+	MPI: 'static,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain: SourceHeaderChain<
+		MessagesProof = FromBridgedChainMessagesProof<HashOf<BridgedChain<Runtime, GPI>>>,
+	>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+		finality_target: Box::new(bridged_header),
+		justification: bridged_justification,
+	};
+	let submit_message = pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+		relayer_id_at_bridged_chain,
+		proof: message_proof,
+		messages_count: 1,
+		dispatch_weight: Weight::from_parts(1000000000, 0),
+	};
+	pallet_utility::Call::<Runtime>::batch_all {
+		calls: vec![submit_grandpa.into(), submit_message.into()],
+	}
+}
+
+/// Prepare a batch call with bridged GRANDPA finality and message delivery proof.
+pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, MPI>(
+	bridged_header: BridgedHeader<Runtime, GPI>,
+	bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
+	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<
+		HashOf<BridgedChain<Runtime, GPI>>,
+	>,
+	relayers_state: UnrewardedRelayersState,
+) -> pallet_utility::Call<Runtime>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	MPI: 'static,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain: TargetHeaderChain<
+		XcmAsPlainPayload,
+		Runtime::AccountId,
+		MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<
+			HashOf<BridgedChain<Runtime, GPI>>,
+		>,
+	>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+		finality_target: Box::new(bridged_header),
+		justification: bridged_justification,
+	};
+	let submit_message_delivery_proof =
+		pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
+			proof: message_delivery_proof,
+			relayers_state,
+		};
+	pallet_utility::Call::<Runtime>::batch_all {
+		calls: vec![submit_grandpa.into(), submit_message_delivery_proof.into()],
+	}
+}
+
+/// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain.
+pub fn make_complex_relayer_delivery_proofs<MB, InnerXcmRuntimeCall>(
+	lane_id: LaneId,
+	xcm_message: Xcm<InnerXcmRuntimeCall>,
+	message_nonce: MessageNonce,
+	message_destination: Junctions,
+	header_number: BlockNumberOf<MessageBridgedChain<MB>>,
+) -> (
+	HeaderOf<MessageBridgedChain<MB>>,
+	GrandpaJustification<HeaderOf<MessageBridgedChain<MB>>>,
+	FromBridgedChainMessagesProof<HashOf<MessageBridgedChain<MB>>>,
+)
+where
+	MB: MessageBridge,
+	MessageBridgedChain<MB>: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+{
+	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
+	let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
+	// prepare para storage proof containing message
+	let (state_root, storage_proof) = prepare_messages_storage_proof::<MB>(
+		lane_id,
+		message_nonce..=message_nonce,
+		None,
+		message_size,
+		message_payload,
+		encode_all_messages,
+		encode_lane_data,
+	);
+
+	let (header, justification) = make_complex_bridged_grandpa_header_proof::<
+		MessageBridgedChain<MB>,
+	>(state_root, header_number);
+
+	let message_proof = FromBridgedChainMessagesProof {
+		bridged_header_hash: header.hash(),
+		storage_proof,
+		lane: lane_id,
+		nonces_start: message_nonce,
+		nonces_end: message_nonce,
+	};
+
+	(header, justification, message_proof)
+}
+
+/// Prepare storage proofs of message confirmations, stored at the (bridged) target GRANDPA chain.
+pub fn make_complex_relayer_confirmation_proofs<MB, InnerXcmRuntimeCall>(
+	lane_id: LaneId,
+	header_number: BlockNumberOf<MessageBridgedChain<MB>>,
+	relayer_id_at_this_chain: AccountIdOf<MessageThisChain<MB>>,
+	relayers_state: UnrewardedRelayersState,
+) -> (
+	HeaderOf<MessageBridgedChain<MB>>,
+	GrandpaJustification<HeaderOf<MessageBridgedChain<MB>>>,
+	FromBridgedChainMessagesDeliveryProof<HashOf<MessageBridgedChain<MB>>>,
+)
+where
+	MB: MessageBridge,
+	MessageBridgedChain<MB>: Send + Sync + 'static,
+	MessageThisChain<MB>: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: ChainWithGrandpa,
+{
+	// prepare storage proof containing message delivery proof
+	let (state_root, storage_proof) = prepare_message_delivery_storage_proof::<MB>(
+		lane_id,
+		InboundLaneData {
+			relayers: vec![
+				UnrewardedRelayer {
+					relayer: relayer_id_at_this_chain,
+					messages: DeliveredMessages::new(1)
+				};
+				relayers_state.unrewarded_relayer_entries as usize
+			]
+			.into(),
+			last_confirmed_nonce: 1,
+		},
+		StorageProofSize::Minimal(0),
+	);
+
+	let (header, justification) =
+		make_complex_bridged_grandpa_header_proof::<MB::BridgedChain>(state_root, header_number);
+
+	let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
+		bridged_header_hash: header.hash(),
+		storage_proof,
+		lane: lane_id,
+	};
+
+	(header, justification, message_delivery_proof)
+}
+
+/// Make bridged GRANDPA chain header with given state root.
+pub fn make_complex_bridged_grandpa_header_proof<BridgedChain>(
+	state_root: HashOf<BridgedChain>,
+	header_number: BlockNumberOf<BridgedChain>,
+) -> (HeaderOf<BridgedChain>, GrandpaJustification<HeaderOf<BridgedChain>>)
+where
+	BridgedChain: ChainWithGrandpa,
+{
+	let mut header = bp_test_utils::test_header_with_root::<HeaderOf<BridgedChain>>(
+		header_number.into(),
+		state_root.into(),
+	);
+
+	// to compute proper cost of GRANDPA call, let's add some dummy bytes to header, so that the
+	// `submit_finality_proof` call size would be close to maximal expected (and refundable)
+	let extra_bytes_required = maximal_expected_submit_finality_proof_call_size::<BridgedChain>()
+		.saturating_sub(header.encoded_size());
+	header.digest_mut().push(DigestItem::Other(vec![42; extra_bytes_required]));
+
+	let justification = make_default_justification(&header);
+	(header, justification)
+}
+
+/// Maximal expected `submit_finality_proof` call size.
+pub fn maximal_expected_submit_finality_proof_call_size<BridgedChain: ChainWithGrandpa>() -> usize {
+	bp_header_chain::max_expected_submit_finality_proof_arguments_size::<BridgedChain>(
+		false,
+		BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1,
+	) as usize
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
new file mode 100644
index 00000000000..7ac10aa9e73
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs
@@ -0,0 +1,326 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Generating test data for bridges with remote parachains.
+
+use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepare_inbound_xcm};
+
+use bp_messages::{
+	source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId,
+	UnrewardedRelayersState, Weight,
+};
+use bp_runtime::{BlockNumberOf, HeaderOf, Parachain, StorageProofSize, UnderlyingChainOf};
+use bp_test_utils::prepare_parachain_heads_proof;
+use bridge_runtime_common::{
+	messages::{
+		source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
+		BridgedChain as MessageBridgedChain, MessageBridge,
+	},
+	messages_generation::{
+		encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
+		prepare_messages_storage_proof,
+	},
+	messages_xcm_extension::XcmAsPlainPayload,
+};
+use codec::Encode;
+use pallet_bridge_grandpa::BridgedHeader;
+use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber};
+use sp_runtime::{traits::Header as HeaderT, AccountId32};
+use xcm::latest::prelude::*;
+
+use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
+use bp_messages::{DeliveredMessages, InboundLaneData, MessageNonce, UnrewardedRelayer};
+use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
+use sp_runtime::SaturatedConversion;
+
+/// Prepare a batch call with relay finality proof, parachain head proof and message proof.
+pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI>(
+	relay_chain_header: BridgedHeader<Runtime, GPI>,
+	grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
+	parachain_heads: Vec<(ParaId, ParaHash)>,
+	para_heads_proof: ParaHeadsProof,
+	message_proof: FromBridgedChainMessagesProof<ParaHash>,
+	relayer_id_at_bridged_chain: AccountId32,
+) -> pallet_utility::Call<Runtime> where
+	Runtime:pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	ParaHash: From<<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash>,
+	<<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash: From<ParaHash>,
+	<<Runtime as pallet_bridge_messages::Config<MPI>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof:
+		From<FromBridgedChainMessagesProof<ParaHash>>,
+	<Runtime as pallet_bridge_messages::Config<MPI>>::InboundRelayer: From<AccountId32>,
+	<Runtime as pallet_utility::Config>::RuntimeCall:
+		From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	let relay_chain_header_hash = relay_chain_header.hash();
+	let relay_chain_header_number = *relay_chain_header.number();
+	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+		finality_target: Box::new(relay_chain_header),
+		justification: grandpa_justification,
+	};
+	let submit_para_head = pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
+		at_relay_block: (
+			relay_chain_header_number.saturated_into(),
+			relay_chain_header_hash.into(),
+		),
+		parachains: parachain_heads,
+		parachain_heads_proof: para_heads_proof,
+	};
+	let submit_message = pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
+		relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
+		proof: message_proof.into(),
+		messages_count: 1,
+		dispatch_weight: Weight::from_parts(1000000000, 0),
+	};
+	pallet_utility::Call::<Runtime>::batch_all {
+		calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()],
+	}
+}
+
+/// Prepare a batch call with relay finality proof, parachain head proof and message delivery
+/// proof.
+pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, PPI, MPI>(
+	relay_chain_header: BridgedHeader<Runtime, GPI>,
+	grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
+	parachain_heads: Vec<(ParaId, ParaHash)>,
+	para_heads_proof: ParaHeadsProof,
+	message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash>,
+	relayers_state: UnrewardedRelayersState,
+) -> pallet_utility::Call<Runtime>
+where
+	Runtime: pallet_bridge_grandpa::Config<GPI>
+		+ pallet_bridge_parachains::Config<PPI>
+		+ pallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+		+ pallet_utility::Config,
+	GPI: 'static,
+	PPI: 'static,
+	MPI: 'static,
+	<Runtime as pallet_bridge_grandpa::Config<GPI>>::BridgedChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
+	<<Runtime as pallet_bridge_messages::Config<MPI>>::TargetHeaderChain as TargetHeaderChain<
+		XcmAsPlainPayload,
+		Runtime::AccountId,
+	>>::MessagesDeliveryProof: From<FromBridgedChainMessagesDeliveryProof<ParaHash>>,
+	<Runtime as pallet_utility::Config>::RuntimeCall: From<pallet_bridge_grandpa::Call<Runtime, GPI>>
+		+ From<pallet_bridge_parachains::Call<Runtime, PPI>>
+		+ From<pallet_bridge_messages::Call<Runtime, MPI>>,
+{
+	let relay_chain_header_hash = relay_chain_header.hash();
+	let relay_chain_header_number = *relay_chain_header.number();
+	let submit_grandpa = pallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
+		finality_target: Box::new(relay_chain_header),
+		justification: grandpa_justification,
+	};
+	let submit_para_head = pallet_bridge_parachains::Call::<Runtime, PPI>::submit_parachain_heads {
+		at_relay_block: (
+			relay_chain_header_number.saturated_into(),
+			relay_chain_header_hash.into(),
+		),
+		parachains: parachain_heads,
+		parachain_heads_proof: para_heads_proof,
+	};
+	let submit_message_delivery_proof =
+		pallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
+			proof: message_delivery_proof.into(),
+			relayers_state,
+		};
+	pallet_utility::Call::<Runtime>::batch_all {
+		calls: vec![
+			submit_grandpa.into(),
+			submit_para_head.into(),
+			submit_message_delivery_proof.into(),
+		],
+	}
+}
+
+/// Prepare storage proofs of messages, stored at the source chain.
+pub fn make_complex_relayer_delivery_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
+	lane_id: LaneId,
+	xcm_message: Xcm<InnerXcmRuntimeCall>,
+	message_nonce: MessageNonce,
+	message_destination: Junctions,
+	para_header_number: u32,
+	relay_header_number: u32,
+	bridged_para_id: u32,
+) -> (
+	HeaderOf<BridgedRelayChain>,
+	GrandpaJustification<HeaderOf<BridgedRelayChain>>,
+	ParaHead,
+	Vec<(ParaId, ParaHash)>,
+	ParaHeadsProof,
+	FromBridgedChainMessagesProof<ParaHash>,
+)
+where
+	BridgedRelayChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+{
+	let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
+	let message_size = StorageProofSize::Minimal(message_payload.len() as u32);
+	// prepare para storage proof containing message
+	let (para_state_root, para_storage_proof) = prepare_messages_storage_proof::<MB>(
+		lane_id,
+		message_nonce..=message_nonce,
+		None,
+		message_size,
+		message_payload,
+		encode_all_messages,
+		encode_lane_data,
+	);
+
+	let (relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof) =
+		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, MB>(
+			para_state_root,
+			para_header_number,
+			relay_header_number,
+			bridged_para_id,
+		);
+
+	let message_proof = FromBridgedChainMessagesProof {
+		bridged_header_hash: bridged_para_head.hash(),
+		storage_proof: para_storage_proof,
+		lane: lane_id,
+		nonces_start: message_nonce,
+		nonces_end: message_nonce,
+	};
+
+	(
+		relay_chain_header,
+		justification,
+		bridged_para_head,
+		parachain_heads,
+		para_heads_proof,
+		message_proof,
+	)
+}
+
+/// Prepare storage proofs of message confirmations, stored at the target parachain.
+pub fn make_complex_relayer_confirmation_proofs<BridgedRelayChain, MB, InnerXcmRuntimeCall>(
+	lane_id: LaneId,
+	para_header_number: u32,
+	relay_header_number: u32,
+	bridged_para_id: u32,
+	relayer_id_at_this_chain: AccountId32,
+	relayers_state: UnrewardedRelayersState,
+) -> (
+	HeaderOf<BridgedRelayChain>,
+	GrandpaJustification<HeaderOf<BridgedRelayChain>>,
+	ParaHead,
+	Vec<(ParaId, ParaHash)>,
+	ParaHeadsProof,
+	FromBridgedChainMessagesDeliveryProof<ParaHash>,
+)
+where
+	BridgedRelayChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	<<MB as MessageBridge>::ThisChain as bp_runtime::Chain>::AccountId: From<AccountId32>,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+{
+	// prepare para storage proof containing message delivery proof
+	let (para_state_root, para_storage_proof) = prepare_message_delivery_storage_proof::<MB>(
+		lane_id,
+		InboundLaneData {
+			relayers: vec![
+				UnrewardedRelayer {
+					relayer: relayer_id_at_this_chain.into(),
+					messages: DeliveredMessages::new(1)
+				};
+				relayers_state.unrewarded_relayer_entries as usize
+			]
+			.into(),
+			last_confirmed_nonce: 1,
+		},
+		StorageProofSize::Minimal(0),
+	);
+
+	let (relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof) =
+		make_complex_bridged_parachain_heads_proof::<BridgedRelayChain, MB>(
+			para_state_root,
+			para_header_number,
+			relay_header_number,
+			bridged_para_id,
+		);
+
+	let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
+		bridged_header_hash: bridged_para_head.hash(),
+		storage_proof: para_storage_proof,
+		lane: lane_id,
+	};
+
+	(
+		relay_chain_header,
+		justification,
+		bridged_para_head,
+		parachain_heads,
+		para_heads_proof,
+		message_delivery_proof,
+	)
+}
+
+/// Make bridged parachain header with given state root and relay header that is finalizing it.
+pub fn make_complex_bridged_parachain_heads_proof<BridgedRelayChain, MB>(
+	para_state_root: ParaHash,
+	para_header_number: u32,
+	relay_header_number: BlockNumberOf<BridgedRelayChain>,
+	bridged_para_id: u32,
+) -> (
+	HeaderOf<BridgedRelayChain>,
+	GrandpaJustification<HeaderOf<BridgedRelayChain>>,
+	ParaHead,
+	Vec<(ParaId, ParaHash)>,
+	ParaHeadsProof,
+)
+where
+	BridgedRelayChain:
+		bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
+	MB: MessageBridge,
+	<MB as MessageBridge>::BridgedChain: Send + Sync + 'static,
+	<MB as MessageBridge>::ThisChain: Send + Sync + 'static,
+	UnderlyingChainOf<MessageBridgedChain<MB>>: bp_runtime::Chain<Hash = ParaHash> + Parachain,
+{
+	let bridged_para_head = ParaHead(
+		bp_test_utils::test_header_with_root::<HeaderOf<MB::BridgedChain>>(
+			para_header_number.into(),
+			para_state_root,
+		)
+		.encode(),
+	);
+	let (relay_state_root, para_heads_proof, parachain_heads) =
+		prepare_parachain_heads_proof::<HeaderOf<MB::BridgedChain>>(vec![(
+			bridged_para_id,
+			bridged_para_head.clone(),
+		)]);
+	assert_eq!(bridged_para_head.hash(), parachain_heads[0].1);
+
+	let (relay_chain_header, justification) = make_complex_bridged_grandpa_header_proof::<
+		BridgedRelayChain,
+	>(relay_state_root, relay_header_number);
+
+	(relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof)
+}
diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs
new file mode 100644
index 00000000000..f905d21b187
--- /dev/null
+++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs
@@ -0,0 +1,144 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Generating test data, used by all tests.
+
+pub mod from_grandpa_chain;
+pub mod from_parachain;
+
+use bp_messages::{
+	target_chain::{DispatchMessage, DispatchMessageData},
+	LaneId, MessageKey,
+};
+use codec::Encode;
+use frame_support::traits::Get;
+use pallet_bridge_grandpa::BridgedHeader;
+use xcm::latest::prelude::*;
+
+use bp_messages::MessageNonce;
+use bp_runtime::BasicOperatingMode;
+use bp_test_utils::authority_list;
+use xcm::GetVersion;
+use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
+use xcm_executor::traits::{validate_export, ExportXcm};
+
+pub fn prepare_inbound_xcm<InnerXcmRuntimeCall>(
+	xcm_message: Xcm<InnerXcmRuntimeCall>,
+	destination: InteriorMultiLocation,
+) -> Vec<u8> {
+	let location = xcm::VersionedInteriorMultiLocation::V3(destination);
+	let xcm = xcm::VersionedXcm::<InnerXcmRuntimeCall>::V3(xcm_message);
+	// this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor
+	// or public fields, so just tuple
+	// (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed
+	// to the storage)
+	(location, xcm).encode().encode()
+}
+
+/// Helper that creates InitializationData mock data, that can be used to initialize bridge
+/// GRANDPA pallet
+pub fn initialization_data<
+	Runtime: pallet_bridge_grandpa::Config<GrandpaPalletInstance>,
+	GrandpaPalletInstance: 'static,
+>(
+	block_number: u32,
+) -> bp_header_chain::InitializationData<BridgedHeader<Runtime, GrandpaPalletInstance>> {
+	bp_header_chain::InitializationData {
+		header: Box::new(bp_test_utils::test_header(block_number.into())),
+		authority_list: authority_list(),
+		set_id: 1,
+		operating_mode: BasicOperatingMode::Normal,
+	}
+}
+
+/// Dummy xcm
+pub(crate) fn dummy_xcm() -> Xcm<()> {
+	vec![Trap(42)].into()
+}
+
+pub(crate) fn dispatch_message(
+	lane_id: LaneId,
+	nonce: MessageNonce,
+	payload: Vec<u8>,
+) -> DispatchMessage<Vec<u8>> {
+	DispatchMessage {
+		key: MessageKey { lane_id, nonce },
+		data: DispatchMessageData { payload: Ok(payload) },
+	}
+}
+
+/// Macro used for simulate_export_message and capturing bytes
+macro_rules! grab_haul_blob (
+	($name:ident, $grabbed_payload:ident) => {
+		std::thread_local! {
+			static $grabbed_payload: std::cell::RefCell<Option<Vec<u8>>> = std::cell::RefCell::new(None);
+		}
+
+		struct $name;
+		impl HaulBlob for $name {
+			fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError>{
+				$grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob));
+				Ok(())
+			}
+		}
+	}
+);
+
+/// Simulates `HaulBlobExporter` and all its wrapping and captures generated plain bytes,
+/// which are transferred over bridge.
+pub(crate) fn simulate_message_exporter_on_bridged_chain<
+	SourceNetwork: Get<NetworkId>,
+	DestinationNetwork: Get<MultiLocation>,
+	DestinationVersion: GetVersion,
+>(
+	(destination_network, destination_junctions): (NetworkId, Junctions),
+) -> Vec<u8> {
+	grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD);
+
+	// lets pretend that some parachain on bridged chain exported the message
+	let universal_source_on_bridged_chain =
+		X2(GlobalConsensus(SourceNetwork::get()), Parachain(5678));
+	let channel = 1_u32;
+
+	// simulate XCM message export
+	let (ticket, fee) = validate_export::<
+		HaulBlobExporter<GrabbingHaulBlob, DestinationNetwork, DestinationVersion, ()>,
+	>(
+		destination_network,
+		channel,
+		universal_source_on_bridged_chain,
+		destination_junctions,
+		dummy_xcm(),
+	)
+	.expect("validate_export to pass");
+	log::info!(
+		target: "simulate_message_exporter_on_bridged_chain",
+		"HaulBlobExporter::validate fee: {:?}",
+		fee
+	);
+	let xcm_hash =
+		HaulBlobExporter::<GrabbingHaulBlob, DestinationNetwork, DestinationVersion, ()>::deliver(
+			ticket,
+		)
+		.expect("deliver to pass");
+	log::info!(
+		target: "simulate_message_exporter_on_bridged_chain",
+		"HaulBlobExporter::deliver xcm_hash: {:?}",
+		xcm_hash
+	);
+
+	GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("Encoded message should be here"))
+}
diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs
index a8ae63b250a..ca1e1633d4f 100644
--- a/cumulus/parachains/runtimes/test-utils/src/lib.rs
+++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs
@@ -114,14 +114,34 @@ impl<Runtime: frame_system::Config + pallet_balances::Config + pallet_session::C
 	}
 }
 
-// Basic builder based on balances, collators and pallet_sessopm
-pub struct ExtBuilder<
-	Runtime: frame_system::Config
+/// A set of traits for a minimal parachain runtime, that may be used in conjunction with the
+/// `ExtBuilder` and the `RuntimeHelper`.
+pub trait BasicParachainRuntime:
+	frame_system::Config
+	+ pallet_balances::Config
+	+ pallet_session::Config
+	+ pallet_xcm::Config
+	+ parachain_info::Config
+	+ pallet_collator_selection::Config
+	+ cumulus_pallet_parachain_system::Config
+{
+}
+
+impl<T> BasicParachainRuntime for T
+where
+	T: frame_system::Config
 		+ pallet_balances::Config
 		+ pallet_session::Config
 		+ pallet_xcm::Config
-		+ parachain_info::Config,
-> {
+		+ parachain_info::Config
+		+ pallet_collator_selection::Config
+		+ cumulus_pallet_parachain_system::Config,
+	ValidatorIdOf<T>: From<AccountIdOf<T>>,
+{
+}
+
+/// Basic builder based on balances, collators and pallet_session.
+pub struct ExtBuilder<Runtime: BasicParachainRuntime> {
 	// endowed accounts with balances
 	balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
 	// collators to test block prod
@@ -135,14 +155,7 @@ pub struct ExtBuilder<
 	_runtime: PhantomData<Runtime>,
 }
 
-impl<
-		Runtime: frame_system::Config
-			+ pallet_balances::Config
-			+ pallet_session::Config
-			+ pallet_xcm::Config
-			+ parachain_info::Config,
-	> Default for ExtBuilder<Runtime>
-{
+impl<Runtime: BasicParachainRuntime> Default for ExtBuilder<Runtime> {
 	fn default() -> ExtBuilder<Runtime> {
 		ExtBuilder {
 			balances: vec![],
@@ -155,14 +168,7 @@ impl<
 	}
 }
 
-impl<
-		Runtime: frame_system::Config
-			+ pallet_balances::Config
-			+ pallet_session::Config
-			+ pallet_xcm::Config
-			+ parachain_info::Config,
-	> ExtBuilder<Runtime>
-{
+impl<Runtime: BasicParachainRuntime> ExtBuilder<Runtime> {
 	pub fn with_balances(
 		mut self,
 		balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
@@ -198,12 +204,7 @@ impl<
 		self
 	}
 
-	pub fn build(self) -> sp_io::TestExternalities
-	where
-		Runtime:
-			pallet_collator_selection::Config + pallet_balances::Config + pallet_session::Config,
-		ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
-	{
+	pub fn build(self) -> sp_io::TestExternalities {
 		let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
 
 		pallet_xcm::GenesisConfig::<Runtime> {
-- 
GitLab