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`) 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

            
23
use crate::{Config, Pallet, LOG_TARGET};
24

            
25
use crate::{BridgeOf, Bridges};
26

            
27
use bp_messages::{
28
	source_chain::{MessagesBridge, OnMessagesDelivered},
29
	MessageNonce,
30
};
31
use bp_xcm_bridge::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload};
32
use frame_support::{ensure, traits::Get};
33
use pallet_bridge_messages::{
34
	Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet,
35
};
36
use xcm::prelude::*;
37
use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
38
use xcm_executor::traits::ExportXcm;
39

            
40
/// An easy way to access `HaulBlobExporter`.
41
pub type PalletAsHaulBlobExporter<T, I> = HaulBlobExporter<
42
	DummyHaulBlob,
43
	<T as Config<I>>::BridgedNetwork,
44
	<T as Config<I>>::DestinationVersion,
45
	<T as Config<I>>::MessageExportPrice,
46
>;
47
/// An easy way to access associated messages pallet.
48
type MessagesPallet<T, I> = BridgeMessagesPallet<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
49

            
50
impl<T: Config<I>, I: 'static> ExportXcm for Pallet<T, I>
51
where
52
	T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, OutboundPayload = XcmAsPlainPayload>,
53
{
54
	type Ticket = (
55
		BridgeId,
56
		BridgeOf<T, I>,
57
		<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload, T::LaneId>>::SendMessageArgs,
58
		XcmHash,
59
	);
60

            
61
49180
	fn validate(
62
49180
		network: NetworkId,
63
49180
		channel: u32,
64
49180
		universal_source: &mut Option<InteriorLocation>,
65
49180
		destination: &mut Option<InteriorLocation>,
66
49180
		message: &mut Option<Xcm<()>>,
67
49180
	) -> Result<(Self::Ticket, Assets), SendError> {
68
49180
		log::trace!(
69
			target: LOG_TARGET,
70
			"Validate for network: {network:?}, channel: {channel:?}, universal_source: {universal_source:?}, destination: {destination:?}"
71
		);
72

            
73
		// `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so
74
		// let's save them before
75
49180
		let bridge_origin_universal_location = universal_source
76
49180
			.clone()
77
49180
			.take()
78
49180
			.ok_or(SendError::MissingArgument)?;
79
		// Note: watch out this is `ExportMessage::destination`, which is relative to the `network`,
80
		// which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with
81
		// `Self::bridge_locations` which requires **universal** location for destination.
82
49177
		let bridge_destination_universal_location = {
83
49179
			let dest = destination
84
49179
				.clone()
85
49179
				.take()
86
49179
				.ok_or(SendError::MissingArgument)?;
87
49178
			match dest.global_consensus() {
88
32779
				Ok(dest_network) => {
89
32779
					log::trace!(
90
						target: LOG_TARGET,
91
						"Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}",
92
						dest_network == network
93
					);
94
32779
					ensure!(dest_network == network, SendError::NotApplicable);
95
					// ok, `dest` looks like a universal location, so let's use it
96
32778
					dest
97
				}
98
				Err(_) => {
99
					// `dest` is not a universal location, so we need to prepend it with
100
					// `GlobalConsensus`.
101
16399
					dest.pushed_front_with(GlobalConsensus(network))
102
16399
						.map_err(|error_data| {
103
							log::error!(
104
								target: LOG_TARGET,
105
								"Destination: {:?} is not a universal and prepending with {:?} failed!",
106
								error_data.0,
107
								error_data.1,
108
							);
109
							SendError::NotApplicable
110
16399
						})?
111
				}
112
			}
113
		};
114

            
115
		// prepare the origin relative location
116
49177
		let bridge_origin_relative_location =
117
49177
			bridge_origin_universal_location.relative_to(&T::UniversalLocation::get());
118

            
119
		// then we are able to compute the `BridgeId` and find `LaneId` used to send messages
120
49177
		let locations = Self::bridge_locations(
121
49177
			bridge_origin_relative_location,
122
49177
			bridge_destination_universal_location.into(),
123
49177
		)
124
49177
		.map_err(|e| {
125
1
			log::error!(
126
				target: LOG_TARGET,
127
				"Validate `bridge_locations` with error: {e:?}",
128
			);
129
1
			SendError::NotApplicable
130
49177
		})?;
131
49176
		let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| {
132
1
			log::error!(
133
				target: LOG_TARGET,
134
				"No opened bridge for requested bridge_origin_relative_location: {:?} (bridge_origin_universal_location: {:?}) and bridge_destination_universal_location: {:?}",
135
				locations.bridge_origin_relative_location(),
136
				locations.bridge_origin_universal_location(),
137
				locations.bridge_destination_universal_location(),
138
			);
139
1
			SendError::NotApplicable
140
49176
		})?;
141

            
142
		// check if we are able to route the message. We use existing `HaulBlobExporter` for that.
143
		// It will make all required changes and will encode message properly, so that the
144
		// `DispatchBlob` at the bridged bridge hub will be able to decode it
145
49175
		let ((blob, id), price) = PalletAsHaulBlobExporter::<T, I>::validate(
146
49175
			network,
147
49175
			channel,
148
49175
			universal_source,
149
49175
			destination,
150
49175
			message,
151
49175
		)?;
152

            
153
		// Here, we know that the message is relevant to this pallet instance, so let's check for
154
		// congestion defense.
155
49175
		if bridge.state == BridgeState::HardSuspended {
156
2
			log::error!(
157
				target: LOG_TARGET,
158
				"Bridge for requested bridge_origin_relative_location: {:?} (bridge_origin_universal_location: {:?}) and bridge_destination_universal_location: {:?} \
159
				is suspended and does not accept more messages!",
160
				locations.bridge_origin_relative_location(),
161
				locations.bridge_origin_universal_location(),
162
				locations.bridge_destination_universal_location(),
163
			);
164
2
			return Err(SendError::Transport("Exporter is suspended!"));
165
49173
		}
166

            
167
49173
		let bridge_message = MessagesPallet::<T, I>::validate_message(bridge.lane_id, &blob)
168
49173
			.map_err(|e| {
169
				match e {
170
					Error::LanesManager(ref ei) => {
171
						log::error!(target: LOG_TARGET, "LanesManager: {ei:?}")
172
					}
173
					Error::MessageRejectedByPallet(ref ei) => {
174
						log::error!(target: LOG_TARGET, "MessageRejectedByPallet: {ei:?}")
175
					}
176
					Error::ReceptionConfirmation(ref ei) => {
177
						log::error!(target: LOG_TARGET, "ReceptionConfirmation: {ei:?}")
178
					}
179
					_ => (),
180
				};
181

            
182
				log::error!(
183
					target: LOG_TARGET,
184
					"XCM message {:?} cannot be exported because of bridge error: {:?} on bridge {:?} and laneId: {:?}",
185
					id,
186
					e,
187
					locations,
188
					bridge.lane_id,
189
				);
190
				SendError::Transport("BridgeValidateError")
191
49173
			})?;
192

            
193
49173
		Ok(((*locations.bridge_id(), bridge, bridge_message, id), price))
194
49180
	}
195

            
196
49166
	fn deliver(
197
49166
		(bridge_id, bridge, bridge_message, id): Self::Ticket,
198
49166
	) -> Result<XcmHash, SendError> {
199
49166
		let artifacts = MessagesPallet::<T, I>::send_message(bridge_message);
200
49166

            
201
49166
		log::info!(
202
			target: LOG_TARGET,
203
			"XCM message {:?} has been enqueued at bridge {:?} and lane_id: {:?} with nonce {}",
204
			id,
205
			bridge_id,
206
			bridge.lane_id,
207
			artifacts.nonce,
208
		);
209

            
210
		// maybe we need switch to congested state
211
49166
		Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages);
212
49166

            
213
49166
		Ok(id)
214
49166
	}
215
}
216

            
217
impl<T: Config<I>, I: 'static> OnMessagesDelivered<T::LaneId> for Pallet<T, I> {
218
	fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
219
		Self::on_bridge_messages_delivered(lane_id, enqueued_messages);
220
	}
221
}
222

            
223
impl<T: Config<I>, I: 'static> Pallet<T, I> {
224
	/// Called when new message is pushed onto outbound bridge queue.
225
49168
	fn on_bridge_message_enqueued(
226
49168
		bridge_id: BridgeId,
227
49168
		bridge: BridgeOf<T, I>,
228
49168
		enqueued_messages: MessageNonce,
229
49168
	) {
230
49168
		// if the bridge queue is not congested, we don't want to do anything
231
49168
		let is_congested =
232
49168
			enqueued_messages > T::CongestionLimits::get().outbound_lane_congested_threshold;
233
49168
		if !is_congested {
234
40969
			return;
235
8199
		}
236
8199

            
237
8199
		// check if the lane is already suspended or not.
238
8199
		match bridge.state {
239
			BridgeState::SoftSuspended => {
240
8194
				if enqueued_messages > T::CongestionLimits::get().outbound_lane_stop_threshold {
241
					// If its suspended and reached `outbound_lane_stop_threshold`, we stop
242
					// accepting new messages (a.k.a. start dropping).
243
3
					Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
244
3
						bridge.state = BridgeState::HardSuspended;
245
3
					});
246
3
					return;
247
				} else {
248
					// We still can accept new messages to the suspended bridge, hoping that it'll
249
					// be actually resumed soon
250
8191
					return;
251
				}
252
			}
253
			BridgeState::HardSuspended => {
254
				// We cannot accept new messages and start dropping messages, until the outbound
255
				// lane goes below the drop limit.
256
				return;
257
			}
258
5
			_ => {
259
5
				// otherwise, continue handling the suspension
260
5
			}
261
		}
262

            
263
		// else - suspend the bridge
264
5
		let bridge_origin_relative_location = match bridge.bridge_origin_relative_location.try_as()
265
		{
266
5
			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
267
			Err(_) => {
268
				log::error!(
269
					target: LOG_TARGET,
270
					"Failed to convert the bridge {:?} origin location {:?}",
271
					bridge_id,
272
					bridge.bridge_origin_relative_location,
273
				);
274

            
275
				return;
276
			}
277
		};
278
5
		let suspend_result =
279
5
			T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id);
280
5
		match suspend_result {
281
			Ok(_) => {
282
5
				log::debug!(
283
					target: LOG_TARGET,
284
					"Suspended the bridge {:?}, originated by the {:?}",
285
					bridge_id,
286
					bridge.bridge_origin_relative_location,
287
				);
288
			}
289
			Err(e) => {
290
				log::error!(
291
					target: LOG_TARGET,
292
					"Failed to suspended the bridge {:?}, originated by the {:?}: {:?}",
293
					bridge_id,
294
					bridge.bridge_origin_relative_location,
295
					e,
296
				);
297

            
298
				return;
299
			}
300
		}
301

            
302
		// and remember that we have suspended the bridge
303
5
		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
304
5
			bridge.state = BridgeState::SoftSuspended;
305
5
		});
306
49168
	}
307

            
308
	/// Must be called whenever we receive a message delivery confirmation.
309
7
	fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
310
7
		// if the bridge queue is still congested, we don't want to do anything
311
7
		let is_congested =
312
7
			enqueued_messages > T::CongestionLimits::get().outbound_lane_uncongested_threshold;
313
7
		if is_congested {
314
			// and if it is below the `stop_threshold`
315
2
			if enqueued_messages < T::CongestionLimits::get().outbound_lane_stop_threshold {
316
2
				if let Some((bridge_id, bridge)) = Self::bridge_by_lane_id(&lane_id) {
317
2
					if let BridgeState::HardSuspended = bridge.state {
318
1
						// we allow exporting again
319
1
						Bridges::<T, I>::mutate_extant(bridge_id, |b| {
320
1
							b.state = BridgeState::SoftSuspended;
321
1
						});
322
1
					}
323
				}
324
			}
325
2
			return;
326
5
		}
327

            
328
		// if we have not suspended the bridge before (or it is closed), we don't want to do
329
		// anything
330
5
		let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) {
331
4
			Some(bridge)
332
5
				if bridge.1.state == BridgeState::SoftSuspended
333
4
					|| bridge.1.state == BridgeState::HardSuspended =>
334
4
			{
335
4
				bridge
336
			}
337
			_ => {
338
				// if there is no bridge, or it has been closed, then we don't need to send resume
339
				// signal to the local origin - it has closed bridge itself, so it should have
340
				// already pruned everything else
341
1
				return;
342
			}
343
		};
344

            
345
		// else - resume the bridge
346
4
		let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into();
347
4
		let bridge_origin_relative_location = match bridge_origin_relative_location {
348
4
			Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
349
			Err(e) => {
350
				log::error!(
351
					target: LOG_TARGET,
352
					"Failed to convert the bridge {:?} location for lane_id: {:?}, error {:?}",
353
					bridge_id,
354
					lane_id,
355
					e,
356
				);
357

            
358
				return;
359
			}
360
		};
361

            
362
4
		let resume_result =
363
4
			T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id);
364
4
		match resume_result {
365
			Ok(_) => {
366
4
				log::debug!(
367
					target: LOG_TARGET,
368
					"Resumed the bridge {:?} and lane_id: {:?}, originated by the {:?}",
369
					bridge_id,
370
					lane_id,
371
					bridge_origin_relative_location,
372
				);
373
			}
374
			Err(e) => {
375
				log::error!(
376
					target: LOG_TARGET,
377
					"Failed to resume the bridge {:?} and lane_id: {:?}, originated by the {:?}: {:?}",
378
					bridge_id,
379
					lane_id,
380
					bridge_origin_relative_location,
381
					e,
382
				);
383

            
384
				return;
385
			}
386
		}
387

            
388
		// and forget that we have previously suspended the bridge
389
4
		Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
390
4
			bridge.state = BridgeState::Opened;
391
4
		});
392
7
	}
393
}
394

            
395
/// Dummy implementation of the `HaulBlob` trait that is never called.
396
///
397
/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that
398
/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing
399
/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged
400
/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we
401
/// still need this `DummyHaulBlob`.
402
pub struct DummyHaulBlob;
403

            
404
impl HaulBlob for DummyHaulBlob {
405
	fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> {
406
		Err(HaulBlobError::Transport("DummyHaulBlob"))
407
	}
408
}
409

            
410
#[cfg(test)]
411
mod tests {
412
	use super::*;
413
	use crate::{mock::*, Bridges, LanesManagerOf};
414

            
415
	use bp_runtime::RangeInclusiveExt;
416
	use bp_xcm_bridge::{Bridge, BridgeLocations, BridgeState, Receiver};
417
	use frame_support::{
418
		assert_err, assert_ok,
419
		traits::{Contains, EnsureOrigin},
420
	};
421
	use pallet_xcm_bridge_router::ResolveBridgeId;
422
	use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter};
423
	use xcm_executor::traits::export_xcm;
424

            
425
17
	fn universal_source() -> InteriorLocation {
426
17
		SiblingUniversalLocation::get()
427
17
	}
428

            
429
13
	fn bridged_relative_destination() -> InteriorLocation {
430
13
		BridgedRelativeDestination::get()
431
13
	}
432

            
433
2
	fn bridged_universal_destination() -> InteriorLocation {
434
2
		BridgedUniversalDestination::get()
435
2
	}
436

            
437
32782
	fn open_lane(origin: RuntimeOrigin) -> (BridgeLocations, TestLaneIdType) {
438
32782
		// open expected outbound lane
439
32782
		let with = bridged_asset_hub_universal_location();
440
32782
		let locations =
441
32782
			XcmOverBridge::bridge_locations_from_origin(origin.clone(), Box::new(with.into()))
442
32782
				.unwrap();
443
32782
		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
444
32782

            
445
32782
		if !Bridges::<TestRuntime, ()>::contains_key(locations.bridge_id()) {
446
			// fund origin (if needed)
447
13
			if !<TestRuntime as Config<()>>::AllowWithoutBridgeDeposit::contains(
448
13
				locations.bridge_origin_relative_location(),
449
13
			) {
450
11
				fund_origin_sovereign_account(
451
11
					&locations,
452
11
					BridgeDeposit::get() + ExistentialDeposit::get(),
453
11
				);
454
11
			}
455

            
456
			// open bridge
457
13
			assert_ok!(XcmOverBridge::do_open_bridge(
458
13
				locations.clone(),
459
13
				lane_id,
460
13
				true,
461
13
				None
462
13
			));
463
32769
		}
464
32782
		assert_ok!(XcmOverBridge::do_try_state());
465

            
466
32782
		(*locations, lane_id)
467
32782
	}
468

            
469
32777
	fn open_lane_and_send_regular_message(
470
32777
		source_origin: RuntimeOrigin,
471
32777
	) -> (BridgeId, TestLaneIdType) {
472
32777
		let (locations, lane_id) = open_lane(source_origin);
473
32777

            
474
32777
		// now let's try to enqueue message using our `ExportXcm` implementation
475
32777
		export_xcm::<XcmOverBridge>(
476
32777
			BridgedRelayNetwork::get(),
477
32777
			0,
478
32777
			locations.bridge_origin_universal_location().clone(),
479
32777
			locations.bridge_destination_universal_location().clone(),
480
32777
			vec![Instruction::ClearOrigin].into(),
481
32777
		)
482
32777
		.unwrap();
483
32777

            
484
32777
		(*locations.bridge_id(), lane_id)
485
32777
	}
486

            
487
	#[test]
488
1
	fn exporter_works() {
489
1
		run_test(|| {
490
1
			let (_, lane_id) =
491
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
492
1

            
493
1
			// double check that the message has been pushed to the expected lane
494
1
			// (it should already been checked during `send_message` call)
495
1
			assert!(!LanesManagerOf::<TestRuntime, ()>::new()
496
1
				.active_outbound_lane(lane_id)
497
1
				.unwrap()
498
1
				.queued_messages()
499
1
				.is_empty());
500
1
		});
501
1
	}
502

            
503
	#[test]
504
1
	fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() {
505
1
		run_test(|| {
506
1
			let (bridge_id, _) =
507
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
508
1
			assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id));
509
1
			assert_eq!(
510
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
511
1
				BridgeState::Opened
512
1
			);
513
1
		});
514
1
	}
515

            
516
	#[test]
517
1
	fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() {
518
1
		run_test(|| {
519
1
			let (bridge_id, _) =
520
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
521
1
			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
522
1
				bridge.state = BridgeState::SoftSuspended;
523
1
			});
524
8191
			for _ in 1..TestCongestionLimits::get().outbound_lane_congested_threshold {
525
8191
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
526
8191
			}
527

            
528
1
			open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
529
1
			assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id));
530
1
		});
531
1
	}
532

            
533
	#[test]
534
1
	fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() {
535
1
		run_test(|| {
536
1
			let (bridge_id, _) =
537
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
538
8191
			for _ in 1..TestCongestionLimits::get().outbound_lane_congested_threshold {
539
8191
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
540
8191
			}
541

            
542
1
			assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id));
543
1
			assert_eq!(
544
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
545
1
				BridgeState::Opened
546
1
			);
547

            
548
1
			open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
549
1
			assert!(TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id));
550
1
			assert_eq!(
551
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
552
1
				BridgeState::SoftSuspended
553
1
			);
554

            
555
			// send more messages to reach `outbound_lane_stop_threshold`
556
1
			for _ in TestCongestionLimits::get().outbound_lane_congested_threshold
557
1
				..TestCongestionLimits::get().outbound_lane_stop_threshold
558
4096
			{
559
4096
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
560
4096
			}
561
1
			assert_eq!(
562
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
563
1
				BridgeState::HardSuspended
564
1
			);
565
1
		});
566
1
	}
567

            
568
	#[test]
569
1
	fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() {
570
1
		run_test(|| {
571
1
			let (bridge_id, lane_id) =
572
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
573
1
			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
574
1
				bridge.state = BridgeState::SoftSuspended;
575
1
			});
576
1
			XcmOverBridge::on_bridge_messages_delivered(
577
1
				lane_id,
578
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold + 1,
579
1
			);
580
1

            
581
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
582
1
			assert_eq!(
583
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
584
1
				BridgeState::SoftSuspended
585
1
			);
586
1
		});
587
1
	}
588

            
589
	#[test]
590
1
	fn bridge_is_not_resumed_if_it_was_not_suspended_before() {
591
1
		run_test(|| {
592
1
			let (bridge_id, lane_id) =
593
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
594
1
			XcmOverBridge::on_bridge_messages_delivered(
595
1
				lane_id,
596
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold,
597
1
			);
598
1

            
599
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
600
1
			assert_eq!(
601
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
602
1
				BridgeState::Opened
603
1
			);
604
1
		});
605
1
	}
606

            
607
	#[test]
608
1
	fn exporter_respects_stop_threshold() {
609
1
		run_test(|| {
610
1
			let (bridge_id, lane_id) =
611
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
612
1
			let xcm: Xcm<()> = vec![ClearOrigin].into();
613
1

            
614
1
			// Opened - exporter works
615
1
			assert_eq!(
616
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
617
1
				BridgeState::Opened
618
1
			);
619
1
			assert_ok!(XcmOverBridge::validate(
620
1
				BridgedRelayNetwork::get(),
621
1
				0,
622
1
				&mut Some(universal_source()),
623
1
				&mut Some(bridged_relative_destination()),
624
1
				&mut Some(xcm.clone()),
625
1
			),);
626

            
627
			// SoftSuspended - exporter still works
628
1
			XcmOverBridge::on_bridge_message_enqueued(
629
1
				bridge_id,
630
1
				XcmOverBridge::bridge(&bridge_id).unwrap(),
631
1
				TestCongestionLimits::get().outbound_lane_congested_threshold + 1,
632
1
			);
633
1
			assert_eq!(
634
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
635
1
				BridgeState::SoftSuspended
636
1
			);
637
1
			assert_ok!(XcmOverBridge::validate(
638
1
				BridgedRelayNetwork::get(),
639
1
				0,
640
1
				&mut Some(universal_source()),
641
1
				&mut Some(bridged_relative_destination()),
642
1
				&mut Some(xcm.clone()),
643
1
			),);
644

            
645
			// HardSuspended - exporter stops working
646
1
			XcmOverBridge::on_bridge_message_enqueued(
647
1
				bridge_id,
648
1
				XcmOverBridge::bridge(&bridge_id).unwrap(),
649
1
				TestCongestionLimits::get().outbound_lane_stop_threshold + 1,
650
1
			);
651
1
			assert_eq!(
652
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
653
1
				BridgeState::HardSuspended
654
1
			);
655
1
			assert_err!(
656
1
				XcmOverBridge::validate(
657
1
					BridgedRelayNetwork::get(),
658
1
					0,
659
1
					&mut Some(universal_source()),
660
1
					&mut Some(bridged_relative_destination()),
661
1
					&mut Some(xcm.clone()),
662
1
				),
663
1
				SendError::Transport("Exporter is suspended!"),
664
1
			);
665

            
666
			// Back to SoftSuspended - exporter again works
667
1
			XcmOverBridge::on_bridge_messages_delivered(
668
1
				lane_id,
669
1
				TestCongestionLimits::get().outbound_lane_stop_threshold - 1,
670
1
			);
671
1
			assert_eq!(
672
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
673
1
				BridgeState::SoftSuspended
674
1
			);
675
1
			assert_ok!(XcmOverBridge::validate(
676
1
				BridgedRelayNetwork::get(),
677
1
				0,
678
1
				&mut Some(universal_source()),
679
1
				&mut Some(bridged_relative_destination()),
680
1
				&mut Some(xcm.clone()),
681
1
			),);
682

            
683
			// Back to Opened - exporter works
684
1
			XcmOverBridge::on_bridge_messages_delivered(
685
1
				lane_id,
686
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold - 1,
687
1
			);
688
1
			assert_eq!(
689
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
690
1
				BridgeState::Opened
691
1
			);
692
1
			assert_ok!(XcmOverBridge::validate(
693
1
				BridgedRelayNetwork::get(),
694
1
				0,
695
1
				&mut Some(universal_source()),
696
1
				&mut Some(bridged_relative_destination()),
697
1
				&mut Some(xcm.clone()),
698
1
			),);
699
1
		});
700
1
	}
701

            
702
	#[test]
703
1
	fn bridge_is_resumed_when_enough_messages_are_delivered() {
704
1
		run_test(|| {
705
1
			let (bridge_id, lane_id) =
706
1
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
707
1
			Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
708
1
				bridge.state = BridgeState::SoftSuspended;
709
1
			});
710
1
			XcmOverBridge::on_bridge_messages_delivered(
711
1
				lane_id,
712
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold,
713
1
			);
714
1

            
715
1
			assert!(TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
716
1
			assert_eq!(
717
1
				XcmOverBridge::bridge(&bridge_id).unwrap().state,
718
1
				BridgeState::Opened
719
1
			);
720
1
		});
721
1
	}
722

            
723
	#[test]
724
1
	fn export_fails_if_argument_is_missing() {
725
1
		run_test(|| {
726
1
			assert_eq!(
727
1
				XcmOverBridge::validate(
728
1
					BridgedRelayNetwork::get(),
729
1
					0,
730
1
					&mut None,
731
1
					&mut Some(bridged_relative_destination()),
732
1
					&mut Some(Vec::new().into()),
733
1
				),
734
1
				Err(SendError::MissingArgument),
735
1
			);
736

            
737
1
			assert_eq!(
738
1
				XcmOverBridge::validate(
739
1
					BridgedRelayNetwork::get(),
740
1
					0,
741
1
					&mut Some(universal_source()),
742
1
					&mut None,
743
1
					&mut Some(Vec::new().into()),
744
1
				),
745
1
				Err(SendError::MissingArgument),
746
1
			);
747
1
		})
748
1
	}
749

            
750
	#[test]
751
1
	fn exporter_computes_correct_lane_id() {
752
1
		run_test(|| {
753
1
			assert_ne!(
754
1
				bridged_universal_destination(),
755
1
				bridged_relative_destination()
756
1
			);
757

            
758
1
			let locations = BridgeLocations::bridge_locations(
759
1
				UniversalLocation::get(),
760
1
				SiblingLocation::get(),
761
1
				bridged_universal_destination(),
762
1
				BridgedRelayNetwork::get(),
763
1
			)
764
1
			.unwrap();
765
1
			let expected_bridge_id = locations.bridge_id();
766
1
			let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
767
1

            
768
1
			if LanesManagerOf::<TestRuntime, ()>::new()
769
1
				.create_outbound_lane(expected_lane_id)
770
1
				.is_ok()
771
1
			{
772
1
				Bridges::<TestRuntime, ()>::insert(
773
1
					expected_bridge_id,
774
1
					Bridge {
775
1
						bridge_origin_relative_location: Box::new(
776
1
							locations.bridge_origin_relative_location().clone().into(),
777
1
						),
778
1
						bridge_origin_universal_location: Box::new(
779
1
							locations.bridge_origin_universal_location().clone().into(),
780
1
						),
781
1
						bridge_destination_universal_location: Box::new(
782
1
							locations
783
1
								.bridge_destination_universal_location()
784
1
								.clone()
785
1
								.into(),
786
1
						),
787
1
						state: BridgeState::Opened,
788
1
						deposit: None,
789
1
						lane_id: expected_lane_id,
790
1
						maybe_notify: None,
791
1
					},
792
1
				);
793
1
			}
794

            
795
1
			let ticket = XcmOverBridge::validate(
796
1
				BridgedRelayNetwork::get(),
797
1
				0,
798
1
				&mut Some(universal_source()),
799
1
				// Note:  The `ExportMessage` expects relative `InteriorLocation` in the
800
1
				// `BridgedRelayNetwork`.
801
1
				&mut Some(bridged_relative_destination()),
802
1
				&mut Some(Vec::new().into()),
803
1
			)
804
1
			.unwrap()
805
1
			.0;
806
1
			assert_eq!(&ticket.0, expected_bridge_id);
807
1
			assert_eq!(ticket.1.lane_id, expected_lane_id);
808
1
		});
809
1
	}
810

            
811
	#[test]
812
1
	fn pallet_as_exporter_is_compatible_with_pallet_xcm_bridge_hub_router_for_export_message() {
813
1
		run_test(|| {
814
1
			// valid routable destination
815
1
			let dest = Location::new(2, BridgedUniversalDestination::get());
816
1

            
817
1
			// open bridge
818
1
			let origin = OpenBridgeOrigin::sibling_parachain_origin();
819
1
			let origin_as_location =
820
1
				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
821
1
			let (bridge, expected_lane_id) = open_lane(origin);
822
1

            
823
1
			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
824
1
			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
825
1
			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
826
1

            
827
1
			// check compatible bridge_id
828
1
			assert_eq!(
829
1
				bridge.bridge_id(),
830
1
				&<TestRuntime as pallet_xcm_bridge_router::Config<
831
1
					XcmOverBridgeWrappedWithExportMessageRouterInstance,
832
1
				>>::BridgeIdResolver::resolve_for_dest(&dest)
833
1
				.unwrap()
834
1
			);
835

            
836
			// check before - no messages
837
1
			assert_eq!(
838
1
				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
839
1
					expected_lane_id
840
1
				)
841
1
				.unwrap()
842
1
				.queued_messages()
843
1
				.saturating_len(),
844
1
				0
845
1
			);
846

            
847
			// send `ExportMessage(message)` by `UnpaidRemoteExporter`.
848
1
			ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
849
1
			assert_ok!(send_xcm::<
850
1
				UnpaidRemoteExporter<
851
1
					NetworkExportTable<BridgeTable>,
852
1
					ExecuteXcmOverSendXcm,
853
1
					UniversalLocation,
854
1
				>,
855
1
			>(dest.clone(), Xcm::<()>::default()));
856

            
857
			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
858
			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
859
1
			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
860
1
			// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
861
1
			ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location);
862
1
			assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
863
1
				dest,
864
1
				Xcm::<()>::default()
865
1
			));
866

            
867
			// check after - a message ready to be relayed
868
1
			assert_eq!(
869
1
				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
870
1
					expected_lane_id
871
1
				)
872
1
				.unwrap()
873
1
				.queued_messages()
874
1
				.saturating_len(),
875
1
				2
876
1
			);
877
1
		})
878
1
	}
879

            
880
	#[test]
881
1
	fn pallet_as_exporter_is_compatible_with_pallet_xcm_bridge_hub_router_for_export_xcm() {
882
1
		run_test(|| {
883
1
			// valid routable destination
884
1
			let dest = Location::new(2, BridgedUniversalDestination::get());
885
1

            
886
1
			// open bridge as a root on the local chain, which should be converted as
887
1
			// `Location::here()`
888
1
			let (bridge, expected_lane_id) = open_lane(RuntimeOrigin::root());
889
1

            
890
1
			// check compatible bridge_id
891
1
			assert_eq!(
892
1
				bridge.bridge_id(),
893
1
				&<TestRuntime as pallet_xcm_bridge_router::Config<
894
1
					XcmOverBridgeByExportXcmRouterInstance,
895
1
				>>::BridgeIdResolver::resolve_for_dest(&dest)
896
1
				.unwrap()
897
1
			);
898

            
899
			// check before - no messages
900
1
			assert_eq!(
901
1
				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
902
1
					expected_lane_id
903
1
				)
904
1
				.unwrap()
905
1
				.queued_messages()
906
1
				.saturating_len(),
907
1
				0
908
1
			);
909

            
910
			// trigger `ExportXcm` by `pallet_xcm_bridge_hub_router`.
911
1
			assert_ok!(send_xcm::<XcmOverBridgeByExportXcmRouter>(
912
1
				dest,
913
1
				Xcm::<()>::default()
914
1
			));
915

            
916
			// check after - a message ready to be relayed
917
1
			assert_eq!(
918
1
				pallet_bridge_messages::Pallet::<TestRuntime, ()>::outbound_lane_data(
919
1
					expected_lane_id
920
1
				)
921
1
				.unwrap()
922
1
				.queued_messages()
923
1
				.saturating_len(),
924
1
				1
925
1
			);
926
1
		})
927
1
	}
928

            
929
	#[test]
930
1
	fn validate_works() {
931
1
		run_test(|| {
932
1
			let xcm: Xcm<()> = vec![ClearOrigin].into();
933
1

            
934
1
			// check that router does not consume when `NotApplicable`
935
1
			let mut xcm_wrapper = Some(xcm.clone());
936
1
			let mut universal_source_wrapper = Some(universal_source());
937
1

            
938
1
			// wrong `NetworkId`
939
1
			let mut dest_wrapper = Some(bridged_relative_destination());
940
1
			assert_eq!(
941
1
				XcmOverBridge::validate(
942
1
					NetworkId::ByGenesis([0; 32]),
943
1
					0,
944
1
					&mut universal_source_wrapper,
945
1
					&mut dest_wrapper,
946
1
					&mut xcm_wrapper,
947
1
				),
948
1
				Err(SendError::NotApplicable),
949
1
			);
950
			// dest and xcm is NOT consumed and untouched
951
1
			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
952
1
			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
953
1
			assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper);
954

            
955
			// dest starts with wrong `NetworkId`
956
1
			let mut invalid_dest_wrapper = Some(
957
1
				[
958
1
					GlobalConsensus(NetworkId::ByGenesis([0; 32])),
959
1
					Parachain(BRIDGED_ASSET_HUB_ID),
960
1
				]
961
1
				.into(),
962
1
			);
963
1
			assert_eq!(
964
1
				XcmOverBridge::validate(
965
1
					BridgedRelayNetwork::get(),
966
1
					0,
967
1
					&mut Some(universal_source()),
968
1
					&mut invalid_dest_wrapper,
969
1
					&mut xcm_wrapper,
970
1
				),
971
1
				Err(SendError::NotApplicable),
972
1
			);
973
			// dest and xcm is NOT consumed and untouched
974
1
			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
975
1
			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
976
1
			assert_eq!(
977
1
				&Some(
978
1
					[
979
1
						GlobalConsensus(NetworkId::ByGenesis([0; 32]),),
980
1
						Parachain(BRIDGED_ASSET_HUB_ID)
981
1
					]
982
1
					.into()
983
1
				),
984
1
				&invalid_dest_wrapper
985
1
			);
986

            
987
			// no opened lane for dest
988
1
			let mut dest_without_lane_wrapper =
989
1
				Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into());
990
1
			assert_eq!(
991
1
				XcmOverBridge::validate(
992
1
					BridgedRelayNetwork::get(),
993
1
					0,
994
1
					&mut Some(universal_source()),
995
1
					&mut dest_without_lane_wrapper,
996
1
					&mut xcm_wrapper,
997
1
				),
998
1
				Err(SendError::NotApplicable),
999
1
			);
			// dest and xcm is NOT consumed and untouched
1
			assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
1
			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
1
			assert_eq!(
1
				&Some(
1
					[
1
						GlobalConsensus(BridgedRelayNetwork::get(),),
1
						Parachain(5679)
1
					]
1
					.into()
1
				),
1
				&dest_without_lane_wrapper
1
			);
			// ok
1
			let (locations, _) = open_lane(OpenBridgeOrigin::sibling_parachain_origin());
1
			let mut dest_wrapper = Some(bridged_relative_destination());
1
			assert_ok!(XcmOverBridge::validate(
1
				BridgedRelayNetwork::get(),
1
				0,
1
				&mut Some(universal_source()),
1
				&mut dest_wrapper,
1
				&mut xcm_wrapper,
1
			));
			// dest and xcm IS consumed
1
			assert_eq!(None, xcm_wrapper);
1
			assert_eq!(&Some(universal_source()), &universal_source_wrapper);
1
			assert_eq!(None, dest_wrapper);
			// send more messages to reach `outbound_lane_congested_threshold`
8193
			for _ in 0..=TestCongestionLimits::get().outbound_lane_congested_threshold {
8193
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
8193
			}
			// bridge is suspended but exporter accepts more messages
1
			assert_eq!(
1
				XcmOverBridge::bridge(locations.bridge_id()).unwrap().state,
1
				BridgeState::SoftSuspended
1
			);
			// export still can accept more messages
1
			assert_ok!(XcmOverBridge::validate(
1
				BridgedRelayNetwork::get(),
1
				0,
1
				&mut Some(universal_source()),
1
				&mut Some(bridged_relative_destination()),
1
				&mut Some(xcm.clone()),
1
			));
			// send more messages to reach `outbound_lane_stop_threshold`
1
			for _ in TestCongestionLimits::get().outbound_lane_congested_threshold
1
				..TestCongestionLimits::get().outbound_lane_stop_threshold
4096
			{
4096
				open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin());
4096
			}
			// bridge is suspended but exporter CANNOT accept more messages
1
			assert_eq!(
1
				XcmOverBridge::bridge(locations.bridge_id()).unwrap().state,
1
				BridgeState::HardSuspended
1
			);
			// export still can accept more messages
1
			assert_err!(
1
				XcmOverBridge::validate(
1
					BridgedRelayNetwork::get(),
1
					0,
1
					&mut Some(universal_source()),
1
					&mut Some(bridged_relative_destination()),
1
					&mut Some(xcm.clone()),
1
				),
1
				SendError::Transport("Exporter is suspended!"),
1
			);
1
		});
1
	}
	#[test]
1
	fn congestion_with_pallet_xcm_bridge_hub_router_works() {
1
		run_test(|| {
1
			// valid routable destination
1
			let dest = Location::new(2, BridgedUniversalDestination::get());
6
			fn router_bridge_state<T: pallet_xcm_bridge_router::Config<I>, I: 'static>(
6
				dest: &Location,
6
			) -> Option<pallet_xcm_bridge_router::BridgeState> {
6
				let bridge_id =
6
					<T::BridgeIdResolver as ResolveBridgeId>::resolve_for_dest(dest).unwrap();
6
				pallet_xcm_bridge_router::Bridges::<T, I>::get(&bridge_id)
6
			}
			// open two bridges
1
			let origin = OpenBridgeOrigin::sibling_parachain_origin();
1
			let origin_as_location =
1
				OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
1
			let (bridge_1, expected_lane_id_1) = open_lane(origin);
1
			let (bridge_2, expected_lane_id_2) = open_lane(RuntimeOrigin::root());
1
			assert_ne!(expected_lane_id_1, expected_lane_id_2);
1
			assert_ne!(bridge_1.bridge_id(), bridge_2.bridge_id());
			// we need to set `UniversalLocation` for `sibling_parachain_origin` for
			// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
1
			ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
1

            
1
			// we need to update `maybe_notify` for `bridge_1` with `pallet_index` of
1
			// `XcmOverBridgeWrappedWithExportMessageRouter`,
1
			Bridges::<TestRuntime, ()>::mutate_extant(bridge_1.bridge_id(), |bridge| {
1
				bridge.maybe_notify = Some(Receiver::new(57, 0));
1
			});
1

            
1
			// check before
1
			// bridges are opened
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
1
				BridgeState::Opened
1
			);
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state,
1
				BridgeState::Opened
1
			);
			// both routers are uncongested
1
			assert!(!router_bridge_state::<
1
				TestRuntime,
1
				XcmOverBridgeWrappedWithExportMessageRouterInstance,
1
			>(&dest)
1
			.map(|bs| bs.is_congested)
1
			.unwrap_or(false));
1
			assert!(
1
				!router_bridge_state::<TestRuntime, XcmOverBridgeByExportXcmRouterInstance>(&dest)
1
					.map(|bs| bs.is_congested)
1
					.unwrap_or(false)
1
			);
1
			assert!(!TestLocalXcmChannelManager::is_bridge_suspened(
1
				bridge_1.bridge_id()
1
			));
1
			assert!(!TestLocalXcmChannelManager::is_bridge_suspened(
1
				bridge_2.bridge_id()
1
			));
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_1.bridge_id()
1
			));
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_2.bridge_id()
1
			));
			// make bridges congested with sending too much messages
1
			for _ in 1..(TestCongestionLimits::get().outbound_lane_congested_threshold + 2) {
				// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
8193
				ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
8193
				assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
8193
					dest.clone(),
8193
					Xcm::<()>::default()
8193
				));
				// call direct `ExportXcm` by `pallet_xcm_bridge_hub_router`.
8193
				assert_ok!(send_xcm::<XcmOverBridgeByExportXcmRouter>(
8193
					dest.clone(),
8193
					Xcm::<()>::default()
8193
				));
			}
			// checks after
			// bridges are suspended
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
1
				BridgeState::SoftSuspended
1
			);
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state,
1
				BridgeState::SoftSuspended
1
			);
			// both routers are congested
1
			assert!(
1
				router_bridge_state::<
1
					TestRuntime,
1
					XcmOverBridgeWrappedWithExportMessageRouterInstance,
1
				>(&dest)
1
				.unwrap()
1
				.is_congested
1
			);
1
			assert!(
1
				router_bridge_state::<TestRuntime, XcmOverBridgeByExportXcmRouterInstance>(&dest)
1
					.unwrap()
1
					.is_congested
1
			);
1
			assert!(TestLocalXcmChannelManager::is_bridge_suspened(
1
				bridge_1.bridge_id()
1
			));
1
			assert!(TestLocalXcmChannelManager::is_bridge_suspened(
1
				bridge_2.bridge_id()
1
			));
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_1.bridge_id()
1
			));
1
			assert!(!TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_2.bridge_id()
1
			));
			// make bridges uncongested to trigger resume signal
1
			XcmOverBridge::on_bridge_messages_delivered(
1
				expected_lane_id_1,
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold,
1
			);
1
			XcmOverBridge::on_bridge_messages_delivered(
1
				expected_lane_id_2,
1
				TestCongestionLimits::get().outbound_lane_uncongested_threshold,
1
			);
1

            
1
			// bridges are again opened
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
1
				BridgeState::Opened
1
			);
1
			assert_eq!(
1
				XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state,
1
				BridgeState::Opened
1
			);
			// both routers are uncongested
1
			assert!(router_bridge_state::<
1
				TestRuntime,
1
				XcmOverBridgeWrappedWithExportMessageRouterInstance,
1
			>(&dest)
1
			.is_none());
1
			assert!(
1
				router_bridge_state::<TestRuntime, XcmOverBridgeByExportXcmRouterInstance>(&dest)
1
					.is_none()
1
			);
1
			assert!(TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_1.bridge_id()
1
			));
1
			assert!(TestLocalXcmChannelManager::is_bridge_resumed(
1
				bridge_2.bridge_id()
1
			));
1
		})
1
	}
}