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					},
165					Error::MessageRejectedByPallet(ref ei) => {
166						tracing::error!(target: LOG_TARGET, error=?ei, "MessageRejectedByPallet")
167					},
168					Error::ReceptionConfirmation(ref ei) => {
169						tracing::error!(target: LOG_TARGET, error=?ei, "ReceptionConfirmation")
170					},
171					_ => (),
172				};
173
174				tracing::error!(
175					target: LOG_TARGET,
176					error=?e,
177					topic_id=?id,
178					bridge_id=?locations,
179					lane_id=?bridge.lane_id,
180					"XCM message cannot be exported"
181				);
182				SendError::Transport("BridgeValidateError")
183			})?;
184
185		Ok(((*locations.bridge_id(), bridge, bridge_message, id), price))
186	}
187
188	fn deliver(
189		(bridge_id, bridge, bridge_message, id): Self::Ticket,
190	) -> Result<XcmHash, SendError> {
191		let artifacts = MessagesPallet::<T, I>::send_message(bridge_message);
192
193		tracing::info!(
194			target: LOG_TARGET,
195			topic_id=?id,
196			bridge_id=?bridge_id,
197			lane_id=?bridge.lane_id,
198			nonce=%artifacts.nonce,
199			"XCM message has been enqueued"
200		);
201
202		// maybe we need switch to congested state
203		Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages);
204
205		Ok(id)
206	}
207}
208
209impl<T: Config<I>, I: 'static> OnMessagesDelivered<T::LaneId> for Pallet<T, I> {
210	fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
211		Self::on_bridge_messages_delivered(lane_id, enqueued_messages);
212	}
213}
214
215impl<T: Config<I>, I: 'static> Pallet<T, I> {
216	/// Called when new message is pushed onto outbound bridge queue.
217	fn on_bridge_message_enqueued(
218		bridge_id: BridgeId,
219		bridge: BridgeOf<T, I>,
220		enqueued_messages: MessageNonce,
221	) {
222		// if the bridge queue is not congested, we don't want to do anything
223		let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD;
224		if !is_congested {
225			return;
226		}
227
228		// TODO: https://github.com/paritytech/parity-bridges-common/issues/2006 we either need fishermens
229		// to watch this rule violation (suspended, but keep sending new messages), or we need a
230		// hard limit for that like other XCM queues have
231
232		// check if the lane is already suspended. If it is, do nothing. We still accept new
233		// messages to the suspended bridge, hoping that it'll be actually resumed soon
234		if bridge.state == BridgeState::Suspended {
235			return;
236		}
237
238		// else - suspend the bridge
239		let result_bridge_origin_relative_location =
240			(*bridge.bridge_origin_relative_location).clone().try_into();
241		let bridge_origin_relative_location = match &result_bridge_origin_relative_location {
242			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
243			Err(_) => {
244				tracing::debug!(
245					target: LOG_TARGET,
246					?bridge_id,
247					origin_location=?bridge.bridge_origin_relative_location,
248					"Failed to convert"
249				);
250
251				return;
252			},
253		};
254		let suspend_result =
255			T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id);
256		match suspend_result {
257			Ok(_) => {
258				tracing::debug!(
259					target: LOG_TARGET,
260					?bridge_id,
261					originated_by=?bridge.bridge_origin_relative_location,
262					"Suspended"
263				);
264			},
265			Err(e) => {
266				tracing::debug!(
267					target: LOG_TARGET,
268					error=?e,
269					?bridge_id,
270					originated_by=?bridge.bridge_origin_relative_location,
271					"Failed to suspended"
272				);
273
274				return;
275			},
276		}
277
278		// and remember that we have suspended the bridge
279		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
280			bridge.state = BridgeState::Suspended;
281		});
282	}
283
284	/// Must be called whenever we receive a message delivery confirmation.
285	fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
286		// if the bridge queue is still congested, we don't want to do anything
287		let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD;
288		if is_congested {
289			return;
290		}
291
292		// if we have not suspended the bridge before (or it is closed), we don't want to do
293		// anything
294		let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) {
295			Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge,
296			_ => {
297				// if there is no bridge or it has been closed, then we don't need to send resume
298				// signal to the local origin - it has closed bridge itself, so it should have
299				// alrady pruned everything else
300				return;
301			},
302		};
303
304		// else - resume the bridge
305		let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into();
306		let bridge_origin_relative_location = match bridge_origin_relative_location {
307			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
308			Err(e) => {
309				tracing::debug!(
310					target: LOG_TARGET,
311					error=?e,
312					?bridge_id,
313					?lane_id,
314					"Failed to convert",
315				);
316
317				return;
318			},
319		};
320
321		let resume_result =
322			T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id);
323		match resume_result {
324			Ok(_) => {
325				tracing::debug!(
326					target: LOG_TARGET,
327					?bridge_id,
328					?lane_id,
329					originated_by=?bridge_origin_relative_location,
330					"Resumed",
331				);
332			},
333			Err(e) => {
334				tracing::debug!(
335					target: LOG_TARGET,
336					error=?e,
337					?bridge_id,
338					?lane_id,
339					originated_by=?bridge_origin_relative_location,
340					"Failed to resume"
341				);
342
343				return;
344			},
345		}
346
347		// and forget that we have previously suspended the bridge
348		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
349			bridge.state = BridgeState::Opened;
350		});
351	}
352}
353
354/// Dummy implementation of the `HaulBlob` trait that is never called.
355///
356/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that
357/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing
358/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged
359/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we
360/// still need this `DummyHaulBlob`.
361pub struct DummyHaulBlob;
362
363impl HaulBlob for DummyHaulBlob {
364	fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> {
365		Err(HaulBlobError::Transport("DummyHaulBlob"))
366	}
367}
368
369#[cfg(test)]
370mod tests {
371	use super::*;
372	use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
373
374	use bp_runtime::RangeInclusiveExt;
375	use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
376	use frame_support::{assert_ok, traits::EnsureOrigin};
377	use pallet_bridge_messages::InboundLaneStorage;
378	use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter};
379	use xcm_executor::traits::{export_xcm, ConvertLocation};
380
381	fn universal_source() -> InteriorLocation {
382		SiblingUniversalLocation::get()
383	}
384
385	fn bridged_relative_destination() -> InteriorLocation {
386		BridgedRelativeDestination::get()
387	}
388
389	fn bridged_universal_destination() -> InteriorLocation {
390		BridgedUniversalDestination::get()
391	}
392
393	fn open_lane(origin: RuntimeOrigin) -> (BridgeLocations, TestLaneIdType) {
394		// open expected outbound lane
395		let with = bridged_asset_hub_universal_location();
396		let locations =
397			XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
398		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
399
400		if !Bridges::<TestRuntime, ()>::contains_key(locations.bridge_id()) {
401			// insert bridge
402			Bridges::<TestRuntime, ()>::insert(
403				locations.bridge_id(),
404				Bridge {
405					bridge_origin_relative_location: Box::new(SiblingLocation::get().into()),
406					bridge_origin_universal_location: Box::new(
407						locations.bridge_origin_universal_location().clone().into(),
408					),
409					bridge_destination_universal_location: Box::new(
410						locations.bridge_destination_universal_location().clone().into(),
411					),
412					state: BridgeState::Opened,
413					bridge_owner_account: LocationToAccountId::convert_location(
414						locations.bridge_origin_relative_location(),
415					)
416					.expect("valid accountId"),
417					deposit: 0,
418					lane_id,
419				},
420			);
421			LaneToBridge::<TestRuntime, ()>::insert(lane_id, locations.bridge_id());
422
423			// create lanes
424			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
425			if lanes_manager.create_inbound_lane(lane_id).is_ok() {
426				assert_eq!(
427					0,
428					lanes_manager
429						.active_inbound_lane(lane_id)
430						.unwrap()
431						.storage()
432						.data()
433						.last_confirmed_nonce
434				);
435			}
436			if lanes_manager.create_outbound_lane(lane_id).is_ok() {
437				assert!(lanes_manager
438					.active_outbound_lane(lane_id)
439					.unwrap()
440					.queued_messages()
441					.is_empty());
442			}
443		}
444		assert_ok!(XcmOverBridge::do_try_state());
445
446		(*locations, lane_id)
447	}
448
449	fn open_lane_and_send_regular_message() -> (BridgeId, TestLaneIdType) {
450		let (locations, lane_id) = open_lane(OpenBridgeOrigin::sibling_parachain_origin());
451
452		// now let's try to enqueue message using our `ExportXcm` implementation
453		export_xcm::<XcmOverBridge>(
454			BridgedRelayNetwork::get(),
455			0,
456			locations.bridge_origin_universal_location().clone(),
457			locations.bridge_destination_universal_location().clone(),
458			vec![Instruction::ClearOrigin].into(),
459		)
460		.unwrap();
461
462		(*locations.bridge_id(), lane_id)
463	}
464
465	#[test]
466	fn exporter_works() {
467		run_test(|| {
468			let (_, lane_id) = open_lane_and_send_regular_message();
469
470			// double check that the message has been pushed to the expected lane
471			// (it should already been checked during `send_message` call)
472			assert!(!LanesManagerOf::<TestRuntime, ()>::new()
473				.active_outbound_lane(lane_id)
474				.unwrap()
475				.queued_messages()
476				.is_empty());
477		});
478	}
479
480	#[test]
481	fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() {
482		run_test(|| {
483			let (bridge_id, _) = open_lane_and_send_regular_message();
484			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
485			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
486		});
487	}
488
489	#[test]
490	fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() {
491		run_test(|| {
492			let (bridge_id, _) = open_lane_and_send_regular_message();
493			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
494				bridge.state = BridgeState::Suspended;
495			});
496			for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
497				open_lane_and_send_regular_message();
498			}
499
500			open_lane_and_send_regular_message();
501			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
502		});
503	}
504
505	#[test]
506	fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() {
507		run_test(|| {
508			let (bridge_id, _) = open_lane_and_send_regular_message();
509			for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
510				open_lane_and_send_regular_message();
511			}
512
513			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
514			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
515
516			open_lane_and_send_regular_message();
517			assert!(TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
518			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
519		});
520	}
521
522	#[test]
523	fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() {
524		run_test(|| {
525			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
526			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
527				bridge.state = BridgeState::Suspended;
528			});
529			XcmOverBridge::on_bridge_messages_delivered(
530				lane_id,
531				OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1,
532			);
533
534			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
535			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
536		});
537	}
538
539	#[test]
540	fn bridge_is_not_resumed_if_it_was_not_suspended_before() {
541		run_test(|| {
542			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
543			XcmOverBridge::on_bridge_messages_delivered(
544				lane_id,
545				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
546			);
547
548			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
549			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
550		});
551	}
552
553	#[test]
554	fn bridge_is_resumed_when_enough_messages_are_delivered() {
555		run_test(|| {
556			let (bridge_id, lane_id) = open_lane_and_send_regular_message();
557			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
558				bridge.state = BridgeState::Suspended;
559			});
560			XcmOverBridge::on_bridge_messages_delivered(
561				lane_id,
562				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
563			);
564
565			assert!(TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
566			assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
567		});
568	}
569
570	#[test]
571	fn export_fails_if_argument_is_missing() {
572		run_test(|| {
573			assert_eq!(
574				XcmOverBridge::validate(
575					BridgedRelayNetwork::get(),
576					0,
577					&mut None,
578					&mut Some(bridged_relative_destination()),
579					&mut Some(Vec::new().into()),
580				),
581				Err(SendError::MissingArgument),
582			);
583
584			assert_eq!(
585				XcmOverBridge::validate(
586					BridgedRelayNetwork::get(),
587					0,
588					&mut Some(universal_source()),
589					&mut None,
590					&mut Some(Vec::new().into()),
591				),
592				Err(SendError::MissingArgument),
593			);
594		})
595	}
596
597	#[test]
598	fn exporter_computes_correct_lane_id() {
599		run_test(|| {
600			assert_ne!(bridged_universal_destination(), bridged_relative_destination());
601
602			let locations = BridgeLocations::bridge_locations(
603				UniversalLocation::get(),
604				SiblingLocation::get(),
605				bridged_universal_destination(),
606				BridgedRelayNetwork::get(),
607			)
608			.unwrap();
609			let expected_bridge_id = locations.bridge_id();
610			let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
611
612			if LanesManagerOf::<TestRuntime, ()>::new()
613				.create_outbound_lane(expected_lane_id)
614				.is_ok()
615			{
616				Bridges::<TestRuntime, ()>::insert(
617					expected_bridge_id,
618					Bridge {
619						bridge_origin_relative_location: Box::new(
620							locations.bridge_origin_relative_location().clone().into(),
621						),
622						bridge_origin_universal_location: Box::new(
623							locations.bridge_origin_universal_location().clone().into(),
624						),
625						bridge_destination_universal_location: Box::new(
626							locations.bridge_destination_universal_location().clone().into(),
627						),
628						state: BridgeState::Opened,
629						bridge_owner_account: [0u8; 32].into(),
630						deposit: 0,
631						lane_id: expected_lane_id,
632					},
633				);
634			}
635
636			let ticket = XcmOverBridge::validate(
637				BridgedRelayNetwork::get(),
638				0,
639				&mut Some(universal_source()),
640				// Note:  The `ExportMessage` expects relative `InteriorLocation` in the
641				// `BridgedRelayNetwork`.
642				&mut Some(bridged_relative_destination()),
643				&mut Some(Vec::new().into()),
644			)
645			.unwrap()
646			.0;
647			assert_eq!(&ticket.0, expected_bridge_id);
648			assert_eq!(ticket.1.lane_id, expected_lane_id);
649		});
650	}
651
652	#[test]
653	fn exporter_is_compatible_with_pallet_xcm_bridge_hub_router() {
654		run_test(|| {
655			// valid routable destination
656			let dest = Location::new(2, BridgedUniversalDestination::get());
657
658			// open bridge
659			let origin = OpenBridgeOrigin::sibling_parachain_origin();
660			let origin_as_location =
661				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
662			let (_, expected_lane_id) = open_lane(origin);
663
664			// check before - no messages
665			assert_eq!(
666				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
667					expected_lane_id
668				)
669				.unwrap()
670				.queued_messages()
671				.saturating_len(),
672				0
673			);
674
675			// send `ExportMessage(message)` by `UnpaidRemoteExporter`.
676			ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location);
677			assert_ok!(send_xcm::<
678				UnpaidRemoteExporter<
679					NetworkExportTable<BridgeTable>,
680					ExecuteXcmOverSendXcm,
681					UniversalLocation,
682				>,
683			>(dest.clone(), Xcm::<()>::default()));
684
685			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
686			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
687			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
688			// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
689			ExecuteXcmOverSendXcm::set_origin_for_execute(SiblingLocation::get());
690			assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
691				dest.clone(),
692				Xcm::<()>::default()
693			));
694
695			// check after - a message ready to be relayed
696			assert_eq!(
697				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
698					expected_lane_id
699				)
700				.unwrap()
701				.queued_messages()
702				.saturating_len(),
703				2
704			);
705		})
706	}
707
708	#[test]
709	fn validate_works() {
710		run_test(|| {
711			let xcm: Xcm<()> = vec![ClearOrigin].into();
712
713			// check that router does not consume when `NotApplicable`
714			let mut xcm_wrapper = Some(xcm.clone());
715			let mut universal_source_wrapper = Some(universal_source());
716
717			// wrong `NetworkId`
718			let mut dest_wrapper = Some(bridged_relative_destination());
719			assert_eq!(
720				XcmOverBridge::validate(
721					NetworkId::ByGenesis([0; 32]),
722					0,
723					&mut universal_source_wrapper,
724					&mut dest_wrapper,
725					&mut xcm_wrapper,
726				),
727				Err(SendError::NotApplicable),
728			);
729			// dest and xcm is NOT consumed and untouched
730			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
731			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
732			assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper);
733
734			// dest starts with wrong `NetworkId`
735			let mut invalid_dest_wrapper = Some(
736				[GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(BRIDGED_ASSET_HUB_ID)]
737					.into(),
738			);
739			assert_eq!(
740				XcmOverBridge::validate(
741					BridgedRelayNetwork::get(),
742					0,
743					&mut Some(universal_source()),
744					&mut invalid_dest_wrapper,
745					&mut xcm_wrapper,
746				),
747				Err(SendError::NotApplicable),
748			);
749			// dest and xcm is NOT consumed and untouched
750			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
751			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
752			assert_eq!(
753				&Some(
754					[
755						GlobalConsensus(NetworkId::ByGenesis([0; 32]),),
756						Parachain(BRIDGED_ASSET_HUB_ID)
757					]
758					.into()
759				),
760				&invalid_dest_wrapper
761			);
762
763			// no opened lane for dest
764			let mut dest_without_lane_wrapper =
765				Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into());
766			assert_eq!(
767				XcmOverBridge::validate(
768					BridgedRelayNetwork::get(),
769					0,
770					&mut Some(universal_source()),
771					&mut dest_without_lane_wrapper,
772					&mut xcm_wrapper,
773				),
774				Err(SendError::NotApplicable),
775			);
776			// dest and xcm is NOT consumed and untouched
777			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
778			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
779			assert_eq!(
780				&Some([GlobalConsensus(BridgedRelayNetwork::get(),), Parachain(5679)].into()),
781				&dest_without_lane_wrapper
782			);
783
784			// ok
785			let _ = open_lane(OpenBridgeOrigin::sibling_parachain_origin());
786			let mut dest_wrapper = Some(bridged_relative_destination());
787			assert_ok!(XcmOverBridge::validate(
788				BridgedRelayNetwork::get(),
789				0,
790				&mut Some(universal_source()),
791				&mut dest_wrapper,
792				&mut xcm_wrapper,
793			));
794			// dest and xcm IS consumed
795			assert_eq!(None, xcm_wrapper);
796			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
797			assert_eq!(None, dest_wrapper);
798		});
799	}
800
801	#[test]
802	fn congestion_with_pallet_xcm_bridge_hub_router_works() {
803		run_test(|| {
804			// valid routable destination
805			let dest = Location::new(2, BridgedUniversalDestination::get());
806
807			fn router_bridge_state() -> pallet_xcm_bridge_hub_router::BridgeState {
808				pallet_xcm_bridge_hub_router::Bridge::<
809					TestRuntime,
810					XcmOverBridgeWrappedWithExportMessageRouterInstance,
811				>::get()
812			}
813
814			// open two bridges
815			let origin = OpenBridgeOrigin::sibling_parachain_origin();
816			let origin_as_location =
817				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
818			let (bridge_1, expected_lane_id_1) = open_lane(origin);
819
820			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
821			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
822			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
823
824			// check before
825			// bridges are opened
826			assert_eq!(
827				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
828				BridgeState::Opened
829			);
830
831			// the router is uncongested
832			assert!(!router_bridge_state().is_congested);
833			assert!(!TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
834			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
835
836			// make bridges congested with sending too much messages
837			for _ in 1..(OUTBOUND_LANE_CONGESTED_THRESHOLD + 2) {
838				// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
839				ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
840				assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
841					dest.clone(),
842					Xcm::<()>::default()
843				));
844			}
845
846			// checks after
847			// bridges are suspended
848			assert_eq!(
849				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
850				BridgeState::Suspended,
851			);
852			// the router is congested
853			assert!(router_bridge_state().is_congested);
854			assert!(TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
855			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
856
857			// make bridges uncongested to trigger resume signal
858			XcmOverBridge::on_bridge_messages_delivered(
859				expected_lane_id_1,
860				OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
861			);
862
863			// bridge is again opened
864			assert_eq!(
865				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
866				BridgeState::Opened
867			);
868			// the router is uncongested
869			assert!(!router_bridge_state().is_congested);
870			assert!(TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
871		})
872	}
873}