referrerpolicy=no-referrer-when-downgrade

pallet_xcm_bridge_hub/
exporter.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as XCM message
18//! exporter at the sending bridge hub. Internally, it just enqueues outbound blob
19//! in the messages pallet queue.
20//!
21//! This code is executed at the source bridge hub.
22
23use crate::{Config, Pallet, LOG_TARGET};
24
25use crate::{BridgeOf, Bridges};
26
27use bp_messages::{
28	source_chain::{MessagesBridge, OnMessagesDelivered},
29	MessageNonce,
30};
31use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload};
32use frame_support::{ensure, traits::Get};
33use pallet_bridge_messages::{
34	Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet,
35};
36use xcm::prelude::*;
37use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
38use xcm_executor::traits::ExportXcm;
39
40/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we
41/// suspend a bridge.
42const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192;
43
44/// After we have suspended the bridge, we wait until number of messages in the outbound bridge
45/// queue drops to this count, before sending resuming the bridge.
46const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024;
47
48/// An easy way to access `HaulBlobExporter`.
49pub type PalletAsHaulBlobExporter<T, I> = HaulBlobExporter<
50	DummyHaulBlob,
51	<T as Config<I>>::BridgedNetwork,
52	<T as Config<I>>::DestinationVersion,
53	<T as Config<I>>::MessageExportPrice,
54>;
55/// An easy way to access associated messages pallet.
56type MessagesPallet<T, I> = BridgeMessagesPallet<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
57
58impl<T: Config<I>, I: 'static> ExportXcm for Pallet<T, I>
59where
60	T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, OutboundPayload = XcmAsPlainPayload>,
61{
62	type Ticket = (
63		BridgeId,
64		BridgeOf<T, I>,
65		<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload, T::LaneId>>::SendMessageArgs,
66		XcmHash,
67	);
68
69	fn validate(
70		network: NetworkId,
71		channel: u32,
72		universal_source: &mut Option<InteriorLocation>,
73		destination: &mut Option<InteriorLocation>,
74		message: &mut Option<Xcm<()>>,
75	) -> Result<(Self::Ticket, Assets), SendError> {
76		tracing::trace!(
77			target: LOG_TARGET,
78			?network,
79			?channel,
80			?universal_source,
81			?destination,
82			"Validate for network"
83		);
84
85		// `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so
86		// let's save them before
87		let bridge_origin_universal_location =
88			universal_source.clone().ok_or(SendError::MissingArgument)?;
89		// Note: watch out this is `ExportMessage::destination`, which is relative to the `network`,
90		// which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with
91		// `Self::bridge_locations` which requires **universal** location for destination.
92		let bridge_destination_universal_location = {
93			let dest = destination.clone().ok_or(SendError::MissingArgument)?;
94			match dest.global_consensus() {
95				Ok(dest_network) => {
96					tracing::trace!(
97						target: LOG_TARGET,
98						?dest,
99						?dest_network,
100						?network,
101						"Destination is already universal, checking if matches: {:?}",
102						dest_network == network
103					);
104					ensure!(dest_network == network, SendError::NotApplicable);
105					// ok, `dest` looks like a universal location, so let's use it
106					dest
107				},
108				Err(_) => {
109					// `dest` is not a universal location, so we need to prepend it with
110					// `GlobalConsensus`.
111					dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| {
112						tracing::error!(
113							target: LOG_TARGET, error=?error_data,
114							"Destination is not a universal and prepending failed!"
115						);
116						SendError::NotApplicable
117					})?
118				},
119			}
120		};
121
122		// prepare the origin relative location
123		let bridge_origin_relative_location =
124			bridge_origin_universal_location.relative_to(&T::UniversalLocation::get());
125
126		// then we are able to compute the `BridgeId` and find `LaneId` used to send messages
127		let locations = Self::bridge_locations(
128			bridge_origin_relative_location,
129			bridge_destination_universal_location.into(),
130		)
131		.map_err(|e| {
132			tracing::error!(
133				target: LOG_TARGET, error=?e,
134				"Validate `bridge_locations` with error"
135			);
136			SendError::NotApplicable
137		})?;
138		let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| {
139			tracing::error!(
140				target: LOG_TARGET,
141				bridge_origin_relative_location=?locations.bridge_origin_relative_location(),
142				bridge_destination_universal_location=?locations.bridge_destination_universal_location(),
143				"No opened bridge for requested"
144			);
145			SendError::NotApplicable
146		})?;
147
148		// check if we are able to route the message. We use existing `HaulBlobExporter` for that.
149		// It will make all required changes and will encode message properly, so that the
150		// `DispatchBlob` at the bridged bridge hub will be able to decode it
151		let ((blob, id), price) = PalletAsHaulBlobExporter::<T, I>::validate(
152			network,
153			channel,
154			universal_source,
155			destination,
156			message,
157		)?;
158
159		let bridge_message = MessagesPallet::<T, I>::validate_message(bridge.lane_id, &blob)
160			.map_err(|e| {
161				match e {
162					Error::LanesManager(ref ei) =>
163						tracing::error!(target: LOG_TARGET, error=?ei, "LanesManager"),
164					Error::MessageRejectedByPallet(ref ei) =>
165						tracing::error!(target: LOG_TARGET, error=?ei, "MessageRejectedByPallet"),
166					Error::ReceptionConfirmation(ref ei) =>
167						tracing::error!(target: LOG_TARGET, error=?ei, "ReceptionConfirmation"),
168					_ => (),
169				};
170
171				tracing::error!(
172					target: LOG_TARGET,
173					error=?e,
174					topic_id=?id,
175					bridge_id=?locations,
176					lane_id=?bridge.lane_id,
177					"XCM message cannot be exported"
178				);
179				SendError::Transport("BridgeValidateError")
180			})?;
181
182		Ok(((*locations.bridge_id(), bridge, bridge_message, id), price))
183	}
184
185	fn deliver(
186		(bridge_id, bridge, bridge_message, id): Self::Ticket,
187	) -> Result<XcmHash, SendError> {
188		let artifacts = MessagesPallet::<T, I>::send_message(bridge_message);
189
190		tracing::info!(
191			target: LOG_TARGET,
192			topic_id=?id,
193			bridge_id=?bridge_id,
194			lane_id=?bridge.lane_id,
195			nonce=%artifacts.nonce,
196			"XCM message has been enqueued"
197		);
198
199		// maybe we need switch to congested state
200		Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages);
201
202		Ok(id)
203	}
204}
205
206impl<T: Config<I>, I: 'static> OnMessagesDelivered<T::LaneId> for Pallet<T, I> {
207	fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
208		Self::on_bridge_messages_delivered(lane_id, enqueued_messages);
209	}
210}
211
212impl<T: Config<I>, I: 'static> Pallet<T, I> {
213	/// Called when new message is pushed onto outbound bridge queue.
214	fn on_bridge_message_enqueued(
215		bridge_id: BridgeId,
216		bridge: BridgeOf<T, I>,
217		enqueued_messages: MessageNonce,
218	) {
219		// if the bridge queue is not congested, we don't want to do anything
220		let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD;
221		if !is_congested {
222			return
223		}
224
225		// TODO: https://github.com/paritytech/parity-bridges-common/issues/2006 we either need fishermens
226		// to watch this rule violation (suspended, but keep sending new messages), or we need a
227		// hard limit for that like other XCM queues have
228
229		// check if the lane is already suspended. If it is, do nothing. We still accept new
230		// messages to the suspended bridge, hoping that it'll be actually resumed soon
231		if bridge.state == BridgeState::Suspended {
232			return
233		}
234
235		// else - suspend the bridge
236		let result_bridge_origin_relative_location =
237			(*bridge.bridge_origin_relative_location).clone().try_into();
238		let bridge_origin_relative_location = match &result_bridge_origin_relative_location {
239			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
240			Err(_) => {
241				tracing::debug!(
242					target: LOG_TARGET,
243					?bridge_id,
244					origin_location=?bridge.bridge_origin_relative_location,
245					"Failed to convert"
246				);
247
248				return
249			},
250		};
251		let suspend_result =
252			T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id);
253		match suspend_result {
254			Ok(_) => {
255				tracing::debug!(
256					target: LOG_TARGET,
257					?bridge_id,
258					originated_by=?bridge.bridge_origin_relative_location,
259					"Suspended"
260				);
261			},
262			Err(e) => {
263				tracing::debug!(
264					target: LOG_TARGET,
265					error=?e,
266					?bridge_id,
267					originated_by=?bridge.bridge_origin_relative_location,
268					"Failed to suspended"
269				);
270
271				return
272			},
273		}
274
275		// and remember that we have suspended the bridge
276		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
277			bridge.state = BridgeState::Suspended;
278		});
279	}
280
281	/// Must be called whenever we receive a message delivery confirmation.
282	fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
283		// if the bridge queue is still congested, we don't want to do anything
284		let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD;
285		if is_congested {
286			return
287		}
288
289		// if we have not suspended the bridge before (or it is closed), we don't want to do
290		// anything
291		let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) {
292			Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge,
293			_ => {
294				// if there is no bridge or it has been closed, then we don't need to send resume
295				// signal to the local origin - it has closed bridge itself, so it should have
296				// alrady pruned everything else
297				return
298			},
299		};
300
301		// else - resume the bridge
302		let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into();
303		let bridge_origin_relative_location = match bridge_origin_relative_location {
304			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
305			Err(e) => {
306				tracing::debug!(
307					target: LOG_TARGET,
308					error=?e,
309					?bridge_id,
310					?lane_id,
311					"Failed to convert",
312				);
313
314				return
315			},
316		};
317
318		let resume_result =
319			T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id);
320		match resume_result {
321			Ok(_) => {
322				tracing::debug!(
323					target: LOG_TARGET,
324					?bridge_id,
325					?lane_id,
326					originated_by=?bridge_origin_relative_location,
327					"Resumed",
328				);
329			},
330			Err(e) => {
331				tracing::debug!(
332					target: LOG_TARGET,
333					error=?e,
334					?bridge_id,
335					?lane_id,
336					originated_by=?bridge_origin_relative_location,
337					"Failed to resume"
338				);
339
340				return
341			},
342		}
343
344		// and forget that we have previously suspended the bridge
345		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
346			bridge.state = BridgeState::Opened;
347		});
348	}
349}
350
351/// Dummy implementation of the `HaulBlob` trait that is never called.
352///
353/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that
354/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing
355/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged
356/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we
357/// still need this `DummyHaulBlob`.
358pub struct DummyHaulBlob;
359
360impl HaulBlob for DummyHaulBlob {
361	fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> {
362		Err(HaulBlobError::Transport("DummyHaulBlob"))
363	}
364}
365
366#[cfg(test)]
367mod tests {
368	use super::*;
369	use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
370
371	use bp_runtime::RangeInclusiveExt;
372	use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
373	use frame_support::{assert_ok, traits::EnsureOrigin};
374	use pallet_bridge_messages::InboundLaneStorage;
375	use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter};
376	use xcm_executor::traits::{export_xcm, ConvertLocation};
377
378	fn universal_source() -> InteriorLocation {
379		SiblingUniversalLocation::get()
380	}
381
382	fn bridged_relative_destination() -> InteriorLocation {
383		BridgedRelativeDestination::get()
384	}
385
386	fn bridged_universal_destination() -> InteriorLocation {
387		BridgedUniversalDestination::get()
388	}
389
390	fn open_lane(origin: RuntimeOrigin) -> (BridgeLocations, TestLaneIdType) {
391		// open expected outbound lane
392		let with = bridged_asset_hub_universal_location();
393		let locations =
394			XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
395		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
396
397		if !Bridges::<TestRuntime, ()>::contains_key(locations.bridge_id()) {
398			// insert bridge
399			Bridges::<TestRuntime, ()>::insert(
400				locations.bridge_id(),
401				Bridge {
402					bridge_origin_relative_location: Box::new(SiblingLocation::get().into()),
403					bridge_origin_universal_location: Box::new(
404						locations.bridge_origin_universal_location().clone().into(),
405					),
406					bridge_destination_universal_location: Box::new(
407						locations.bridge_destination_universal_location().clone().into(),
408					),
409					state: BridgeState::Opened,
410					bridge_owner_account: LocationToAccountId::convert_location(
411						locations.bridge_origin_relative_location(),
412					)
413					.expect("valid accountId"),
414					deposit: 0,
415					lane_id,
416				},
417			);
418			LaneToBridge::<TestRuntime, ()>::insert(lane_id, locations.bridge_id());
419
420			// create lanes
421			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
422			if lanes_manager.create_inbound_lane(lane_id).is_ok() {
423				assert_eq!(
424					0,
425					lanes_manager
426						.active_inbound_lane(lane_id)
427						.unwrap()
428						.storage()
429						.data()
430						.last_confirmed_nonce
431				);
432			}
433			if lanes_manager.create_outbound_lane(lane_id).is_ok() {
434				assert!(lanes_manager
435					.active_outbound_lane(lane_id)
436					.unwrap()
437					.queued_messages()
438					.is_empty());
439			}
440		}
441		assert_ok!(XcmOverBridge::do_try_state());
442
443		(*locations, lane_id)
444	}
445
446	fn open_lane_and_send_regular_message() -> (BridgeId, TestLaneIdType) {
447		let (locations, lane_id) = open_lane(OpenBridgeOrigin::sibling_parachain_origin());
448
449		// now let's try to enqueue message using our `ExportXcm` implementation
450		export_xcm::<XcmOverBridge>(
451			BridgedRelayNetwork::get(),
452			0,
453			locations.bridge_origin_universal_location().clone(),
454			locations.bridge_destination_universal_location().clone(),
455			vec![Instruction::ClearOrigin].into(),
456		)
457		.unwrap();
458
459		(*locations.bridge_id(), lane_id)
460	}
461
462	#[test]
463	fn exporter_works() {
464		run_test(|| {
465			let (_, lane_id) = open_lane_and_send_regular_message();
466
467			// double check that the message has been pushed to the expected lane
468			// (it should already been checked during `send_message` call)
469			assert!(!LanesManagerOf::<TestRuntime, ()>::new()
470				.active_outbound_lane(lane_id)
471				.unwrap()
472				.queued_messages()
473				.is_empty());
474		});
475	}
476
477	#[test]
478	fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() {
479		run_test(|| {
480			let (bridge_id, _) = open_lane_and_send_regular_message();
481			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
482			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
483		});
484	}
485
486	#[test]
487	fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() {
488		run_test(|| {
489			let (bridge_id, _) = open_lane_and_send_regular_message();
490			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
491				bridge.state = BridgeState::Suspended;
492			});
493			for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
494				open_lane_and_send_regular_message();
495			}
496
497			open_lane_and_send_regular_message();
498			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
499		});
500	}
501
502	#[test]
503	fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() {
504		run_test(|| {
505			let (bridge_id, _) = open_lane_and_send_regular_message();
506			for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
507				open_lane_and_send_regular_message();
508			}
509
510			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
511			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
512
513			open_lane_and_send_regular_message();
514			assert!(TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
515			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
516		});
517	}
518
519	#[test]
520	fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() {
521		run_test(|| {
522			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
523			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
524				bridge.state = BridgeState::Suspended;
525			});
526			XcmOverBridge::on_bridge_messages_delivered(
527				lane_id,
528				OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1,
529			);
530
531			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
532			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
533		});
534	}
535
536	#[test]
537	fn bridge_is_not_resumed_if_it_was_not_suspended_before() {
538		run_test(|| {
539			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
540			XcmOverBridge::on_bridge_messages_delivered(
541				lane_id,
542				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
543			);
544
545			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
546			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
547		});
548	}
549
550	#[test]
551	fn bridge_is_resumed_when_enough_messages_are_delivered() {
552		run_test(|| {
553			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
554			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
555				bridge.state = BridgeState::Suspended;
556			});
557			XcmOverBridge::on_bridge_messages_delivered(
558				lane_id,
559				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
560			);
561
562			assert!(TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
563			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
564		});
565	}
566
567	#[test]
568	fn export_fails_if_argument_is_missing() {
569		run_test(|| {
570			assert_eq!(
571				XcmOverBridge::validate(
572					BridgedRelayNetwork::get(),
573					0,
574					&mut None,
575					&mut Some(bridged_relative_destination()),
576					&mut Some(Vec::new().into()),
577				),
578				Err(SendError::MissingArgument),
579			);
580
581			assert_eq!(
582				XcmOverBridge::validate(
583					BridgedRelayNetwork::get(),
584					0,
585					&mut Some(universal_source()),
586					&mut None,
587					&mut Some(Vec::new().into()),
588				),
589				Err(SendError::MissingArgument),
590			);
591		})
592	}
593
594	#[test]
595	fn exporter_computes_correct_lane_id() {
596		run_test(|| {
597			assert_ne!(bridged_universal_destination(), bridged_relative_destination());
598
599			let locations = BridgeLocations::bridge_locations(
600				UniversalLocation::get(),
601				SiblingLocation::get(),
602				bridged_universal_destination(),
603				BridgedRelayNetwork::get(),
604			)
605			.unwrap();
606			let expected_bridge_id = locations.bridge_id();
607			let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
608
609			if LanesManagerOf::<TestRuntime, ()>::new()
610				.create_outbound_lane(expected_lane_id)
611				.is_ok()
612			{
613				Bridges::<TestRuntime, ()>::insert(
614					expected_bridge_id,
615					Bridge {
616						bridge_origin_relative_location: Box::new(
617							locations.bridge_origin_relative_location().clone().into(),
618						),
619						bridge_origin_universal_location: Box::new(
620							locations.bridge_origin_universal_location().clone().into(),
621						),
622						bridge_destination_universal_location: Box::new(
623							locations.bridge_destination_universal_location().clone().into(),
624						),
625						state: BridgeState::Opened,
626						bridge_owner_account: [0u8; 32].into(),
627						deposit: 0,
628						lane_id: expected_lane_id,
629					},
630				);
631			}
632
633			let ticket = XcmOverBridge::validate(
634				BridgedRelayNetwork::get(),
635				0,
636				&mut Some(universal_source()),
637				// Note:  The `ExportMessage` expects relative `InteriorLocation` in the
638				// `BridgedRelayNetwork`.
639				&mut Some(bridged_relative_destination()),
640				&mut Some(Vec::new().into()),
641			)
642			.unwrap()
643			.0;
644			assert_eq!(&ticket.0, expected_bridge_id);
645			assert_eq!(ticket.1.lane_id, expected_lane_id);
646		});
647	}
648
649	#[test]
650	fn exporter_is_compatible_with_pallet_xcm_bridge_hub_router() {
651		run_test(|| {
652			// valid routable destination
653			let dest = Location::new(2, BridgedUniversalDestination::get());
654
655			// open bridge
656			let origin = OpenBridgeOrigin::sibling_parachain_origin();
657			let origin_as_location =
658				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
659			let (_, expected_lane_id) = open_lane(origin);
660
661			// check before - no messages
662			assert_eq!(
663				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
664					expected_lane_id
665				)
666				.unwrap()
667				.queued_messages()
668				.saturating_len(),
669				0
670			);
671
672			// send `ExportMessage(message)` by `UnpaidRemoteExporter`.
673			ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location);
674			assert_ok!(send_xcm::<
675				UnpaidRemoteExporter<
676					NetworkExportTable<BridgeTable>,
677					ExecuteXcmOverSendXcm,
678					UniversalLocation,
679				>,
680			>(dest.clone(), Xcm::<()>::default()));
681
682			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
683			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
684			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
685			// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
686			ExecuteXcmOverSendXcm::set_origin_for_execute(SiblingLocation::get());
687			assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
688				dest.clone(),
689				Xcm::<()>::default()
690			));
691
692			// check after - a message ready to be relayed
693			assert_eq!(
694				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
695					expected_lane_id
696				)
697				.unwrap()
698				.queued_messages()
699				.saturating_len(),
700				2
701			);
702		})
703	}
704
705	#[test]
706	fn validate_works() {
707		run_test(|| {
708			let xcm: Xcm<()> = vec![ClearOrigin].into();
709
710			// check that router does not consume when `NotApplicable`
711			let mut xcm_wrapper = Some(xcm.clone());
712			let mut universal_source_wrapper = Some(universal_source());
713
714			// wrong `NetworkId`
715			let mut dest_wrapper = Some(bridged_relative_destination());
716			assert_eq!(
717				XcmOverBridge::validate(
718					NetworkId::ByGenesis([0; 32]),
719					0,
720					&mut universal_source_wrapper,
721					&mut dest_wrapper,
722					&mut xcm_wrapper,
723				),
724				Err(SendError::NotApplicable),
725			);
726			// dest and xcm is NOT consumed and untouched
727			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
728			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
729			assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper);
730
731			// dest starts with wrong `NetworkId`
732			let mut invalid_dest_wrapper = Some(
733				[GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(BRIDGED_ASSET_HUB_ID)]
734					.into(),
735			);
736			assert_eq!(
737				XcmOverBridge::validate(
738					BridgedRelayNetwork::get(),
739					0,
740					&mut Some(universal_source()),
741					&mut invalid_dest_wrapper,
742					&mut xcm_wrapper,
743				),
744				Err(SendError::NotApplicable),
745			);
746			// dest and xcm is NOT consumed and untouched
747			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
748			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
749			assert_eq!(
750				&Some(
751					[
752						GlobalConsensus(NetworkId::ByGenesis([0; 32]),),
753						Parachain(BRIDGED_ASSET_HUB_ID)
754					]
755					.into()
756				),
757				&invalid_dest_wrapper
758			);
759
760			// no opened lane for dest
761			let mut dest_without_lane_wrapper =
762				Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into());
763			assert_eq!(
764				XcmOverBridge::validate(
765					BridgedRelayNetwork::get(),
766					0,
767					&mut Some(universal_source()),
768					&mut dest_without_lane_wrapper,
769					&mut xcm_wrapper,
770				),
771				Err(SendError::NotApplicable),
772			);
773			// dest and xcm is NOT consumed and untouched
774			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
775			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
776			assert_eq!(
777				&Some([GlobalConsensus(BridgedRelayNetwork::get(),), Parachain(5679)].into()),
778				&dest_without_lane_wrapper
779			);
780
781			// ok
782			let _ = open_lane(OpenBridgeOrigin::sibling_parachain_origin());
783			let mut dest_wrapper = Some(bridged_relative_destination());
784			assert_ok!(XcmOverBridge::validate(
785				BridgedRelayNetwork::get(),
786				0,
787				&mut Some(universal_source()),
788				&mut dest_wrapper,
789				&mut xcm_wrapper,
790			));
791			// dest and xcm IS consumed
792			assert_eq!(None, xcm_wrapper);
793			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
794			assert_eq!(None, dest_wrapper);
795		});
796	}
797
798	#[test]
799	fn congestion_with_pallet_xcm_bridge_hub_router_works() {
800		run_test(|| {
801			// valid routable destination
802			let dest = Location::new(2, BridgedUniversalDestination::get());
803
804			fn router_bridge_state() -> pallet_xcm_bridge_hub_router::BridgeState {
805				pallet_xcm_bridge_hub_router::Bridge::<
806					TestRuntime,
807					XcmOverBridgeWrappedWithExportMessageRouterInstance,
808				>::get()
809			}
810
811			// open two bridges
812			let origin = OpenBridgeOrigin::sibling_parachain_origin();
813			let origin_as_location =
814				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
815			let (bridge_1, expected_lane_id_1) = open_lane(origin);
816
817			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
818			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
819			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
820
821			// check before
822			// bridges are opened
823			assert_eq!(
824				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
825				BridgeState::Opened
826			);
827
828			// the router is uncongested
829			assert!(!router_bridge_state().is_congested);
830			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
831			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
832
833			// make bridges congested with sending too much messages
834			for _ in 1..(OUTBOUND_LANE_CONGESTED_THRESHOLD + 2) {
835				// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
836				ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
837				assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
838					dest.clone(),
839					Xcm::<()>::default()
840				));
841			}
842
843			// checks after
844			// bridges are suspended
845			assert_eq!(
846				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
847				BridgeState::Suspended,
848			);
849			// the router is congested
850			assert!(router_bridge_state().is_congested);
851			assert!(TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
852			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
853
854			// make bridges uncongested to trigger resume signal
855			XcmOverBridge::on_bridge_messages_delivered(
856				expected_lane_id_1,
857				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
858			);
859
860			// bridge is again opened
861			assert_eq!(
862				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
863				BridgeState::Opened
864			);
865			// the router is uncongested
866			assert!(!router_bridge_state().is_congested);
867			assert!(TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
868		})
869	}
870}