Skip to main content

bridge_hub_test_utils/test_cases/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
18//!
19//! This file contains tests, suitable for all bridge runtimes. See `from_parachain` and
20//! `from_grandpa_chain` submodules for tests, that are specific to the bridged chain type.
21
22pub mod from_grandpa_chain;
23pub mod from_parachain;
24
25pub(crate) mod helpers;
26
27use crate::{test_cases::bridges_prelude::*, test_data};
28
29use asset_test_utils::BasicParachainRuntime;
30use bp_messages::{
31	target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
32	LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData,
33};
34use bp_runtime::BasicOperatingMode;
35use codec::Encode;
36use frame_support::{
37	assert_ok,
38	dispatch::GetDispatchInfo,
39	traits::{fungible::Mutate, Contains, Get, OnFinalize, OnInitialize, OriginTrait},
40};
41use frame_system::pallet_prelude::BlockNumberFor;
42use parachains_common::AccountId;
43use parachains_runtimes_test_utils::{
44	mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
45	GovernanceOrigin, RuntimeCallOf, RuntimeOriginOf, SlotDurations, XcmReceivedFrom,
46};
47use sp_runtime::{traits::Zero, AccountId32};
48use xcm::{latest::prelude::*, AlwaysLatest};
49use xcm_builder::DispatchBlobError;
50use xcm_executor::{
51	traits::{ConvertLocation, WeightBounds},
52	XcmExecutor,
53};
54
55/// Common bridges exports.
56pub(crate) mod bridges_prelude {
57	pub use bp_parachains::{RelayBlockHash, RelayBlockNumber};
58	pub use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
59	pub use pallet_bridge_messages::{
60		Call as BridgeMessagesCall, Config as BridgeMessagesConfig, LanesManagerError,
61	};
62	pub use pallet_bridge_parachains::{
63		Call as BridgeParachainsCall, Config as BridgeParachainsConfig,
64	};
65}
66
67// Re-export test-case
68pub use for_pallet_xcm_bridge_hub::open_and_close_bridge_works;
69
70// Re-export test_case from assets
71pub use asset_test_utils::include_teleports_for_native_asset_works;
72use pallet_bridge_messages::LaneIdOf;
73
74pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
75	parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
76
77// Re-export test_case from `parachains-runtimes-test-utils`
78pub use parachains_runtimes_test_utils::test_cases::{
79	change_storage_constant_by_governance_works, set_storage_keys_by_governance_works,
80};
81
82/// Prepare default runtime storage and run test within this context.
83pub fn run_test<Runtime, T>(
84	collator_session_key: CollatorSessionKeys<Runtime>,
85	runtime_para_id: u32,
86	balances: Vec<(Runtime::AccountId, Runtime::Balance)>,
87	test: impl FnOnce() -> T,
88) -> T
89where
90	Runtime: BasicParachainRuntime,
91{
92	ExtBuilder::<Runtime>::default()
93		.with_collators(collator_session_key.collators())
94		.with_session_keys(collator_session_key.session_keys())
95		.with_safe_xcm_version(XCM_VERSION)
96		.with_para_id(runtime_para_id.into())
97		.with_balances(balances)
98		.with_tracing()
99		.build()
100		.execute_with(|| test())
101}
102
103/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
104pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
105	collator_session_key: CollatorSessionKeys<Runtime>,
106	runtime_para_id: u32,
107	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
108) where
109	Runtime: BasicParachainRuntime + BridgeGrandpaConfig<GrandpaPalletInstance>,
110	GrandpaPalletInstance: 'static,
111	RuntimeCallOf<Runtime>:
112		GetDispatchInfo + From<BridgeGrandpaCall<Runtime, GrandpaPalletInstance>>,
113{
114	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
115		// check mode before
116		assert_eq!(
117			pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
118			Err(())
119		);
120
121		// prepare the `initialize` call
122		let initialize_call = RuntimeCallOf::<Runtime>::from(BridgeGrandpaCall::<
123			Runtime,
124			GrandpaPalletInstance,
125		>::initialize {
126			init_data: test_data::initialization_data::<Runtime, GrandpaPalletInstance>(12345),
127		});
128
129		// execute XCM with Transacts to `initialize bridge` as governance does
130		assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
131			initialize_call,
132			governance_origin
133		));
134
135		// check mode after
136		assert_eq!(
137			pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
138			Ok(BasicOperatingMode::Normal)
139		);
140	})
141}
142
143/// Test-case makes sure that `Runtime` can change bridge GRANDPA pallet operating mode via
144/// governance-like call.
145pub fn change_bridge_grandpa_pallet_mode_by_governance_works<Runtime, GrandpaPalletInstance>(
146	collator_session_key: CollatorSessionKeys<Runtime>,
147	runtime_para_id: u32,
148	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
149) where
150	Runtime: BasicParachainRuntime + BridgeGrandpaConfig<GrandpaPalletInstance>,
151	GrandpaPalletInstance: 'static,
152	RuntimeCallOf<Runtime>:
153		GetDispatchInfo + From<BridgeGrandpaCall<Runtime, GrandpaPalletInstance>>,
154{
155	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
156		let dispatch_set_operating_mode_call = |old_mode, new_mode| {
157			// check old mode
158			assert_eq!(
159				pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::get(),
160				old_mode,
161			);
162
163			// prepare the `set_operating_mode` call
164			let set_operating_mode_call = <Runtime as frame_system::Config>::RuntimeCall::from(
165				pallet_bridge_grandpa::Call::<Runtime, GrandpaPalletInstance>::set_operating_mode {
166					operating_mode: new_mode,
167				},
168			);
169
170			// execute XCM with Transacts to `initialize bridge` as governance does
171			assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
172				set_operating_mode_call,
173				governance_origin.clone()
174			));
175
176			// check mode after
177			assert_eq!(
178				pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
179				Ok(new_mode)
180			);
181		};
182
183		// check mode before
184		assert_eq!(
185			pallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
186			Err(())
187		);
188
189		dispatch_set_operating_mode_call(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
190		dispatch_set_operating_mode_call(BasicOperatingMode::Halted, BasicOperatingMode::Normal);
191	});
192}
193
194/// Test-case makes sure that `Runtime` can change bridge parachains pallet operating mode via
195/// governance-like call.
196pub fn change_bridge_parachains_pallet_mode_by_governance_works<Runtime, ParachainsPalletInstance>(
197	collator_session_key: CollatorSessionKeys<Runtime>,
198	runtime_para_id: u32,
199	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
200) where
201	Runtime: BasicParachainRuntime + BridgeParachainsConfig<ParachainsPalletInstance>,
202	ParachainsPalletInstance: 'static,
203	RuntimeCallOf<Runtime>:
204		GetDispatchInfo + From<BridgeParachainsCall<Runtime, ParachainsPalletInstance>>,
205{
206	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
207		let dispatch_set_operating_mode_call = |old_mode, new_mode| {
208			// check old mode
209			assert_eq!(
210				pallet_bridge_parachains::PalletOperatingMode::<Runtime, ParachainsPalletInstance>::get(),
211				old_mode,
212			);
213
214			// prepare the `set_operating_mode` call
215			let set_operating_mode_call =
216				RuntimeCallOf::<Runtime>::from(pallet_bridge_parachains::Call::<
217					Runtime,
218					ParachainsPalletInstance,
219				>::set_operating_mode {
220					operating_mode: new_mode,
221				});
222
223			// execute XCM with Transacts to `initialize bridge` as governance does
224			assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
225				set_operating_mode_call,
226				governance_origin.clone()
227			));
228
229			// check mode after
230			assert_eq!(
231				pallet_bridge_parachains::PalletOperatingMode::<Runtime, ParachainsPalletInstance>::try_get(),
232				Ok(new_mode)
233			);
234		};
235
236		// check mode before
237		assert_eq!(
238			pallet_bridge_parachains::PalletOperatingMode::<Runtime, ParachainsPalletInstance>::try_get(),
239			Err(())
240		);
241
242		dispatch_set_operating_mode_call(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
243		dispatch_set_operating_mode_call(BasicOperatingMode::Halted, BasicOperatingMode::Normal);
244	});
245}
246
247/// Test-case makes sure that `Runtime` can change bridge messaging pallet operating mode via
248/// governance-like call.
249pub fn change_bridge_messages_pallet_mode_by_governance_works<Runtime, MessagesPalletInstance>(
250	collator_session_key: CollatorSessionKeys<Runtime>,
251	runtime_para_id: u32,
252	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
253) where
254	Runtime: BasicParachainRuntime + BridgeMessagesConfig<MessagesPalletInstance>,
255	MessagesPalletInstance: 'static,
256	RuntimeCallOf<Runtime>:
257		GetDispatchInfo + From<BridgeMessagesCall<Runtime, MessagesPalletInstance>>,
258{
259	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
260		let dispatch_set_operating_mode_call = |old_mode, new_mode| {
261			// check old mode
262			assert_eq!(
263				pallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::get(
264				),
265				old_mode,
266			);
267
268			// encode `set_operating_mode` call
269			let set_operating_mode_call = RuntimeCallOf::<Runtime>::from(BridgeMessagesCall::<
270				Runtime,
271				MessagesPalletInstance,
272			>::set_operating_mode {
273				operating_mode: new_mode,
274			});
275
276			// execute XCM with Transacts to `initialize bridge` as governance does
277			assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
278				set_operating_mode_call,
279				governance_origin.clone()
280			));
281
282			// check mode after
283			assert_eq!(
284				pallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::try_get(),
285				Ok(new_mode)
286			);
287		};
288
289		// check mode before
290		assert_eq!(
291			pallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::try_get(
292			),
293			Err(())
294		);
295
296		dispatch_set_operating_mode_call(
297			MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
298			MessagesOperatingMode::RejectingOutboundMessages,
299		);
300		dispatch_set_operating_mode_call(
301			MessagesOperatingMode::RejectingOutboundMessages,
302			MessagesOperatingMode::Basic(BasicOperatingMode::Halted),
303		);
304		dispatch_set_operating_mode_call(
305			MessagesOperatingMode::Basic(BasicOperatingMode::Halted),
306			MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
307		);
308	});
309}
310
311/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
312/// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
313/// For SystemParachains we expect unpaid execution.
314pub fn handle_export_message_from_system_parachain_to_outbound_queue_works<
315	Runtime,
316	XcmConfig,
317	MessagesPalletInstance,
318	LocationToAccountId,
319>(
320	collator_session_key: CollatorSessionKeys<Runtime>,
321	runtime_para_id: u32,
322	sibling_parachain_id: u32,
323	unwrap_pallet_bridge_messages_event: Box<
324		dyn Fn(Vec<u8>) -> Option<pallet_bridge_messages::Event<Runtime, MessagesPalletInstance>>,
325	>,
326	export_message_instruction: fn() -> Instruction<XcmConfig::RuntimeCall>,
327	_existential_deposit: Option<Asset>,
328	maybe_paid_export_message: Option<Asset>,
329	prepare_configuration: impl Fn() -> LaneIdOf<Runtime, MessagesPalletInstance>,
330) where
331	Runtime: BasicParachainRuntime + BridgeMessagesConfig<MessagesPalletInstance>,
332	XcmConfig: xcm_executor::Config,
333	MessagesPalletInstance: 'static,
334	LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
335{
336	assert_ne!(runtime_para_id, sibling_parachain_id);
337	let sibling_parachain_location = Location::new(1, [Parachain(sibling_parachain_id)]);
338
339	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
340		let expected_lane_id = prepare_configuration();
341
342		// check queue before
343		assert_eq!(
344			pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
345				expected_lane_id
346			),
347			Ok(OutboundLaneData {
348				state: LaneState::Opened,
349				oldest_unpruned_nonce: 1,
350				latest_received_nonce: 0,
351				latest_generated_nonce: 0
352			})
353		);
354
355		// prepare `ExportMessage`
356		let xcm = if let Some(fee) = maybe_paid_export_message {
357			// Pre-fund the sibling parachain's sovereign account with the fee
358			// We need to convert the location to an account and mint funds
359			let sibling_account =
360				LocationToAccountId::convert_location(&sibling_parachain_location)
361					.expect("valid location conversion");
362
363			// Extract the amount from the fee asset
364			let fee_amount = if let Fungibility::Fungible(amount) = fee.fun {
365				amount
366			} else {
367				panic!("Expected fungible asset for fee");
368			};
369
370			// Mint the fee amount to the sibling account using the runtime's Balances pallet
371			let balance_amount: BalanceOf<Runtime> = fee_amount
372				.try_into()
373				.unwrap_or_else(|_| panic!("Failed to convert fee amount to balance"));
374			<pallet_balances::Pallet<Runtime>>::mint_into(&sibling_account, balance_amount)
375				.expect("minting should succeed");
376
377			Xcm(vec![
378				WithdrawAsset(Assets::from(vec![fee.clone()])),
379				BuyExecution { fees: fee, weight_limit: Unlimited },
380				export_message_instruction(),
381			])
382		} else {
383			Xcm(vec![
384				UnpaidExecution { weight_limit: Unlimited, check_origin: None },
385				export_message_instruction(),
386			])
387		};
388
389		// execute XCM
390		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
391		assert_ok!(XcmExecutor::<XcmConfig>::prepare_and_execute(
392			sibling_parachain_location,
393			xcm,
394			&mut hash,
395			RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
396			Weight::zero(),
397		)
398		.ensure_complete());
399
400		// check queue after
401		assert_eq!(
402			pallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
403				expected_lane_id
404			),
405			Ok(OutboundLaneData {
406				state: LaneState::Opened,
407				oldest_unpruned_nonce: 1,
408				latest_received_nonce: 0,
409				latest_generated_nonce: 1,
410			})
411		);
412
413		// check events
414		let mut events = <frame_system::Pallet<Runtime>>::events()
415			.into_iter()
416			.filter_map(|e| unwrap_pallet_bridge_messages_event(e.event.encode()));
417		assert!(events.any(|e| matches!(e, pallet_bridge_messages::Event::MessageAccepted { .. })));
418	})
419}
420
421/// Test-case makes sure that Runtime can route XCM messages received in inbound queue,
422/// We just test here `MessageDispatch` configuration.
423/// We expect that runtime can route messages:
424///     1. to Parent (relay chain)
425///     2. to Sibling parachain
426pub fn message_dispatch_routing_works<
427	Runtime,
428	AllPalletsWithoutSystem,
429	XcmConfig,
430	HrmpChannelOpener,
431	MessagesPalletInstance,
432	RuntimeNetwork,
433	BridgedNetwork,
434	NetworkDistanceAsParentCount,
435>(
436	collator_session_key: CollatorSessionKeys<Runtime>,
437	slot_durations: SlotDurations,
438	runtime_para_id: u32,
439	sibling_parachain_id: u32,
440	unwrap_cumulus_pallet_parachain_system_event: Box<
441		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_parachain_system::Event<Runtime>>,
442	>,
443	unwrap_cumulus_pallet_xcmp_queue_event: Box<
444		dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
445	>,
446	prepare_configuration: impl Fn(),
447) where
448	Runtime: BasicParachainRuntime
449		+ cumulus_pallet_xcmp_queue::Config
450		+ BridgeMessagesConfig<MessagesPalletInstance, InboundPayload = test_data::XcmAsPlainPayload>,
451	AllPalletsWithoutSystem:
452		OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
453	AccountIdOf<Runtime>: From<AccountId32>
454		+ Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
455	XcmConfig: xcm_executor::Config,
456	MessagesPalletInstance: 'static,
457	HrmpChannelOpener: frame_support::inherent::ProvideInherent<
458		Call = cumulus_pallet_parachain_system::Call<Runtime>,
459	>,
460	RuntimeNetwork: Get<NetworkId>,
461	BridgedNetwork: Get<NetworkId>,
462	NetworkDistanceAsParentCount: Get<u8>,
463{
464	struct NetworkWithParentCount<N, C>(core::marker::PhantomData<(N, C)>);
465	impl<N: Get<NetworkId>, C: Get<u8>> Get<Location> for NetworkWithParentCount<N, C> {
466		fn get() -> Location {
467			Location::new(C::get(), [GlobalConsensus(N::get())])
468		}
469	}
470	assert_ne!(runtime_para_id, sibling_parachain_id);
471
472	#[derive(Debug)]
473	enum XcmBlobMessageDispatchResult {
474		Dispatched,
475		#[allow(dead_code)]
476		NotDispatched(Option<DispatchBlobError>),
477	}
478
479	run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
480		prepare_configuration();
481
482		let dummy_lane_id = LaneIdOf::<Runtime, MessagesPalletInstance>::default();
483		let mut alice = [0u8; 32];
484		alice[0] = 1;
485
486		let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
487			2,
488			AccountId::from(alice).into(),
489		);
490		// 1. this message is sent from other global consensus with destination of this Runtime
491		//    relay chain (UMP)
492		let bridging_message = test_data::simulate_message_exporter_on_bridged_chain::<
493			BridgedNetwork,
494			NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
495			AlwaysLatest,
496		>((RuntimeNetwork::get(), Here));
497		let result =
498			<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
499				test_data::dispatch_message(dummy_lane_id, 1, bridging_message),
500			);
501		assert_eq!(
502			format!("{:?}", result.dispatch_level_result),
503			format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
504		);
505
506		// check events - UpwardMessageSent
507		let mut events = <frame_system::Pallet<Runtime>>::events()
508			.into_iter()
509			.filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode()));
510		assert!(events.any(|e| matches!(
511			e,
512			cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
513		)));
514
515		// 2. this message is sent from other global consensus with destination of this Runtime
516		//    sibling parachain (HRMP)
517		let bridging_message =
518			test_data::simulate_message_exporter_on_bridged_chain::<
519				BridgedNetwork,
520				NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
521				AlwaysLatest,
522			>((RuntimeNetwork::get(), [Parachain(sibling_parachain_id)].into()));
523
524		// 2.1. WITHOUT opened hrmp channel -> RoutingError
525		let result =
526			<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
527				DispatchMessage {
528					key: MessageKey { lane_id: dummy_lane_id, nonce: 1 },
529					data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
530				},
531			);
532		assert_eq!(
533			format!("{:?}", result.dispatch_level_result),
534			format!(
535				"{:?}",
536				XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))
537			)
538		);
539
540		// check events - no XcmpMessageSent
541		assert_eq!(
542			<frame_system::Pallet<Runtime>>::events()
543				.into_iter()
544				.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
545				.count(),
546			0
547		);
548
549		// 2.1. WITH hrmp channel -> Ok
550		mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
551			runtime_para_id.into(),
552			sibling_parachain_id.into(),
553			included_head,
554			&alice,
555			&slot_durations,
556		);
557		let result =
558			<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
559				DispatchMessage {
560					key: MessageKey { lane_id: dummy_lane_id, nonce: 1 },
561					data: DispatchMessageData { payload: Ok(bridging_message) },
562				},
563			);
564		assert_eq!(
565			format!("{:?}", result.dispatch_level_result),
566			format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
567		);
568
569		// check events - XcmpMessageSent
570		let mut events = <frame_system::Pallet<Runtime>>::events()
571			.into_iter()
572			.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()));
573		assert!(
574			events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
575		);
576	})
577}
578
579/// Estimates XCM execution fee for paid `ExportMessage` processing.
580pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer<
581	Runtime,
582	XcmConfig,
583	WeightToFee,
584>() -> u128
585where
586	Runtime: frame_system::Config + pallet_balances::Config,
587	XcmConfig: xcm_executor::Config,
588	WeightToFee: frame_support::weights::WeightToFee<Balance = BalanceOf<Runtime>>,
589	<WeightToFee as frame_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
590{
591	// data here are not relevant for weighing
592	let mut xcm = Xcm(vec![
593		WithdrawAsset(Assets::from(vec![Asset {
594			id: AssetId(Location::new(1, [])),
595			fun: Fungible(34333299),
596		}])),
597		BuyExecution {
598			fees: Asset { id: AssetId(Location::new(1, [])), fun: Fungible(34333299) },
599			weight_limit: Unlimited,
600		},
601		SetAppendix(Xcm(vec![DepositAsset {
602			assets: Wild(AllCounted(1)),
603			beneficiary: Location::new(1, [Parachain(1000)]),
604		}])),
605		ExportMessage {
606			network: Polkadot,
607			destination: [Parachain(1000)].into(),
608			xcm: Xcm(vec![
609				ReserveAssetDeposited(Assets::from(vec![Asset {
610					id: AssetId(Location::new(2, [GlobalConsensus(Kusama)])),
611					fun: Fungible(1000000000000),
612				}])),
613				ClearOrigin,
614				BuyExecution {
615					fees: Asset {
616						id: AssetId(Location::new(2, [GlobalConsensus(Kusama)])),
617						fun: Fungible(1000000000000),
618					},
619					weight_limit: Unlimited,
620				},
621				DepositAsset {
622					assets: Wild(AllCounted(1)),
623					beneficiary: Location::new(
624						0,
625						[xcm::latest::prelude::AccountId32 {
626							network: None,
627							id: [
628								212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159,
629								214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165,
630								109, 162, 125,
631							],
632						}],
633					),
634				},
635				SetTopic([
636					116, 82, 194, 132, 171, 114, 217, 165, 23, 37, 161, 177, 165, 179, 247, 114,
637					137, 101, 147, 70, 28, 157, 168, 32, 154, 63, 74, 228, 152, 180, 5, 63,
638				]),
639			]),
640		},
641		SetTopic([
642			36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219,
643			157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122,
644		]),
645	]);
646
647	// get weight
648	let weight = XcmConfig::Weigher::weight(&mut xcm, Weight::MAX);
649	assert_ok!(weight);
650	let weight = weight.unwrap();
651	// check if sane
652	let max_expected = Runtime::BlockWeights::get().max_block / 10;
653	assert!(
654		weight.all_lte(max_expected),
655		"calculated weight: {:?}, max_expected: {:?}",
656		weight,
657		max_expected
658	);
659
660	// check fee, should not be 0
661	let estimated_fee = WeightToFee::weight_to_fee(&weight);
662	assert!(estimated_fee > BalanceOf::<Runtime>::zero());
663
664	estimated_fee.into()
665}
666
667pub(crate) mod for_pallet_xcm_bridge_hub {
668	use super::*;
669	use crate::test_cases::helpers::for_pallet_xcm_bridge_hub::{
670		close_bridge, ensure_opened_bridge, open_bridge_with_extrinsic,
671	};
672	pub(crate) use pallet_xcm_bridge_hub::{
673		Bridge, BridgeState, Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig,
674		LanesManagerOf,
675	};
676
677	/// Test-case makes sure that `Runtime` can open/close bridges.
678	pub fn open_and_close_bridge_works<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(
679		collator_session_key: CollatorSessionKeys<Runtime>,
680		runtime_para_id: u32,
681		expected_source: Location,
682		destination: InteriorLocation,
683		origin_with_origin_kind: (Location, OriginKind),
684		is_paid_xcm_execution: bool,
685	) where
686		Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig<XcmOverBridgePalletInstance>,
687		XcmOverBridgePalletInstance: 'static,
688		<Runtime as frame_system::Config>::RuntimeCall: GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
689		<Runtime as pallet_balances::Config>::Balance: From<<<Runtime as pallet_bridge_messages::Config<<Runtime as pallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>,
690		<Runtime as pallet_balances::Config>::Balance: From<u128>,
691		<<Runtime as pallet_bridge_messages::Config<<Runtime as pallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<<Runtime as frame_system::Config>::AccountId>,
692		LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
693		TokenLocation: Get<Location>,
694	{
695		run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
696			// construct expected bridge configuration
697			let locations = pallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::bridge_locations(
698				expected_source.clone().into(),
699				destination.clone().into(),
700			).expect("valid bridge locations");
701			let expected_lane_id =
702				locations.calculate_lane_id(xcm::latest::VERSION).expect("valid laneId");
703			let lanes_manager = LanesManagerOf::<Runtime, XcmOverBridgePalletInstance>::new();
704
705			let expected_deposit = if <Runtime as pallet_xcm_bridge_hub::Config<
706				XcmOverBridgePalletInstance,
707			>>::AllowWithoutBridgeDeposit::contains(
708				locations.bridge_origin_relative_location()
709			) {
710				Zero::zero()
711			} else {
712				<Runtime as pallet_xcm_bridge_hub::Config<
713					XcmOverBridgePalletInstance,
714				>>::BridgeDeposit::get()
715			};
716
717			// check bridge/lane DOES not exist
718			assert_eq!(
719				pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
720					locations.bridge_id()
721				),
722				None
723			);
724			assert_eq!(
725				lanes_manager.active_inbound_lane(expected_lane_id).map(drop),
726				Err(LanesManagerError::UnknownInboundLane)
727			);
728			assert_eq!(
729				lanes_manager.active_outbound_lane(expected_lane_id).map(drop),
730				Err(LanesManagerError::UnknownOutboundLane)
731			);
732
733			// open bridge with Transact call
734			assert_eq!(
735				ensure_opened_bridge::<
736					Runtime,
737					XcmOverBridgePalletInstance,
738					LocationToAccountId,
739					TokenLocation,
740				>(
741					expected_source.clone(),
742					destination.clone(),
743					is_paid_xcm_execution,
744					|locations, maybe_paid_execution| open_bridge_with_extrinsic::<
745						Runtime,
746						XcmOverBridgePalletInstance,
747					>(
748						origin_with_origin_kind.clone(),
749						locations.bridge_destination_universal_location().clone(),
750						maybe_paid_execution
751					)
752				)
753				.0
754				.bridge_id(),
755				locations.bridge_id()
756			);
757
758			// check bridge/lane DOES exist
759			assert_eq!(
760				pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
761					locations.bridge_id()
762				),
763				Some(Bridge {
764					bridge_origin_relative_location: Box::new(expected_source.clone().into()),
765					bridge_origin_universal_location: Box::new(
766						locations.bridge_origin_universal_location().clone().into()
767					),
768					bridge_destination_universal_location: Box::new(
769						locations.bridge_destination_universal_location().clone().into()
770					),
771					state: BridgeState::Opened,
772					bridge_owner_account: LocationToAccountId::convert_location(&expected_source)
773						.expect("valid location")
774						.into(),
775					deposit: expected_deposit,
776					lane_id: expected_lane_id,
777				})
778			);
779			assert_eq!(
780				lanes_manager.active_inbound_lane(expected_lane_id).map(|lane| lane.state()),
781				Ok(LaneState::Opened)
782			);
783			assert_eq!(
784				lanes_manager.active_outbound_lane(expected_lane_id).map(|lane| lane.state()),
785				Ok(LaneState::Opened)
786			);
787
788			// close bridge with Transact call
789			close_bridge::<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(
790				expected_source,
791				destination,
792				origin_with_origin_kind,
793				is_paid_xcm_execution,
794			);
795
796			// check bridge/lane DOES not exist
797			assert_eq!(
798				pallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
799					locations.bridge_id()
800				),
801				None
802			);
803			assert_eq!(
804				lanes_manager.active_inbound_lane(expected_lane_id).map(drop),
805				Err(LanesManagerError::UnknownInboundLane)
806			);
807			assert_eq!(
808				lanes_manager.active_outbound_lane(expected_lane_id).map(drop),
809				Err(LanesManagerError::UnknownOutboundLane)
810			);
811		});
812	}
813}