Skip to main content

bp_xcm_bridge_hub/
lib.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//! Primitives of the xcm-bridge-hub pallet.
18
19#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use bp_messages::LaneIdType;
23use bp_runtime::{AccountIdOf, BalanceOf, Chain};
24pub use call_info::XcmBridgeHubCall;
25use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
26use frame_support::{ensure, CloneNoBound, DebugNoBound, PalletError, PartialEqNoBound};
27use scale_info::TypeInfo;
28use serde::{Deserialize, Serialize};
29use sp_core::H256;
30use sp_io::hashing::blake2_256;
31use sp_std::boxed::Box;
32use xcm::{
33	latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation,
34	VersionedLocation,
35};
36
37mod call_info;
38
39/// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound
40/// and outbound payloads.
41pub type XcmAsPlainPayload = sp_std::vec::Vec<u8>;
42
43/// Bridge identifier - used **only** for communicating with sibling/parent chains in the same
44/// consensus.
45///
46/// For example, `SendXcm` implementations (which use the `latest` XCM) can use it to identify a
47/// bridge and the corresponding `LaneId` that is used for over-consensus communication between
48/// bridge hubs.
49///
50/// This identifier is constructed from the `latest` XCM, so it is expected to ensure migration to
51/// the `latest` XCM version. This could change the `BridgeId`, but it will not affect the `LaneId`.
52/// In other words, `LaneId` will never change, while `BridgeId` could change with (every) XCM
53/// upgrade.
54#[derive(
55	Clone,
56	Copy,
57	Decode,
58	Encode,
59	DecodeWithMemTracking,
60	Eq,
61	Ord,
62	PartialOrd,
63	PartialEq,
64	TypeInfo,
65	MaxEncodedLen,
66	Serialize,
67	Deserialize,
68)]
69pub struct BridgeId(H256);
70
71impl BridgeId {
72	/// Create bridge identifier from two universal locations.
73	///
74	/// Note: The `BridgeId` is constructed from `latest` XCM, so if stored, you need to ensure
75	/// compatibility with newer XCM versions.
76	pub fn new(
77		universal_source: &InteriorLocation,
78		universal_destination: &InteriorLocation,
79	) -> Self {
80		const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator";
81
82		BridgeId(
83			(universal_source, VALUES_SEPARATOR, universal_destination)
84				.using_encoded(blake2_256)
85				.into(),
86		)
87	}
88
89	/// Access the inner representation.
90	pub fn inner(&self) -> H256 {
91		self.0
92	}
93}
94
95impl core::fmt::Debug for BridgeId {
96	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97		core::fmt::Debug::fmt(&self.0, f)
98	}
99}
100
101/// Local XCM channel manager.
102pub trait LocalXcmChannelManager {
103	/// Error that may be returned when suspending/resuming the bridge.
104	type Error: sp_std::fmt::Debug;
105
106	/// Returns true if the channel with given location is currently congested.
107	///
108	/// The `with` is guaranteed to be in the same consensus. However, it may point to something
109	/// below the chain level - like the contract or pallet instance, for example.
110	fn is_congested(with: &Location) -> bool;
111
112	/// Suspend the bridge, opened by given origin.
113	///
114	/// The `local_origin` is guaranteed to be in the same consensus. However, it may point to
115	/// something below the chain level - like the contract or pallet instance, for example.
116	fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>;
117
118	/// Resume the previously suspended bridge, opened by given origin.
119	///
120	/// The `local_origin` is guaranteed to be in the same consensus. However, it may point to
121	/// something below the chain level - like the contract or pallet instance, for example.
122	fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>;
123}
124
125impl LocalXcmChannelManager for () {
126	type Error = ();
127
128	fn is_congested(_with: &Location) -> bool {
129		false
130	}
131
132	fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> {
133		Ok(())
134	}
135
136	fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> {
137		Ok(())
138	}
139}
140
141/// Bridge state.
142#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
143pub enum BridgeState {
144	/// Bridge is opened. Associated lanes are also opened.
145	Opened,
146	/// Bridge is suspended. Associated lanes are opened.
147	///
148	/// We keep accepting messages to the bridge. The only difference with the `Opened` state
149	/// is that we have sent the "Suspended" message/signal to the local bridge origin.
150	Suspended,
151	/// Bridge is closed. Associated lanes are also closed.
152	/// After all outbound messages will be pruned, the bridge will vanish without any traces.
153	Closed,
154}
155
156/// Bridge metadata.
157#[derive(
158	CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
159)]
160#[scale_info(skip_type_params(ThisChain, LaneId))]
161pub struct Bridge<ThisChain: Chain, LaneId: LaneIdType> {
162	/// Relative location of the bridge origin chain. This is expected to be **convertible** to the
163	/// `latest` XCM, so the check and migration needs to be ensured.
164	pub bridge_origin_relative_location: Box<VersionedLocation>,
165
166	/// See [`BridgeLocations::bridge_origin_universal_location`].
167	/// Stored for `BridgeId` sanity check.
168	pub bridge_origin_universal_location: Box<VersionedInteriorLocation>,
169	/// See [`BridgeLocations::bridge_destination_universal_location`].
170	/// Stored for `BridgeId` sanity check.
171	pub bridge_destination_universal_location: Box<VersionedInteriorLocation>,
172
173	/// Current bridge state.
174	pub state: BridgeState,
175	/// Account with the reserved funds. Derived from `self.bridge_origin_relative_location`.
176	pub bridge_owner_account: AccountIdOf<ThisChain>,
177	/// Reserved amount on the sovereign account of the sibling bridge origin.
178	pub deposit: BalanceOf<ThisChain>,
179
180	/// Mapping to the unique `LaneId`.
181	pub lane_id: LaneId,
182}
183
184/// Locations of bridge endpoints at both sides of the bridge.
185#[derive(Clone, Debug, PartialEq, Eq)]
186pub struct BridgeLocations {
187	/// Relative (to this bridge hub) location of this side of the bridge.
188	bridge_origin_relative_location: Location,
189	/// Universal (unique) location of this side of the bridge.
190	bridge_origin_universal_location: InteriorLocation,
191	/// Universal (unique) location of other side of the bridge.
192	bridge_destination_universal_location: InteriorLocation,
193	/// An identifier of the dedicated bridge message lane.
194	bridge_id: BridgeId,
195}
196
197/// Errors that may happen when we check bridge locations.
198#[derive(Encode, Decode, DecodeWithMemTracking, Debug, PartialEq, Eq, PalletError, TypeInfo)]
199pub enum BridgeLocationsError {
200	/// Origin or destination locations are not universal.
201	NonUniversalLocation,
202	/// Bridge origin location is not supported.
203	InvalidBridgeOrigin,
204	/// Bridge destination is not supported (in general).
205	InvalidBridgeDestination,
206	/// Destination location is within the same global consensus.
207	DestinationIsLocal,
208	/// Destination network is not the network we are bridged with.
209	UnreachableDestination,
210	/// Destination location is unsupported. We only support bridges with relay
211	/// chain or its parachains.
212	UnsupportedDestinationLocation,
213	/// The version of XCM location argument is unsupported.
214	UnsupportedXcmVersion,
215	/// The `LaneIdType` generator is not supported.
216	UnsupportedLaneIdType,
217}
218
219impl BridgeLocations {
220	/// Given XCM locations, generate lane id and universal locations of bridge endpoints.
221	///
222	/// The `here_universal_location` is the universal location of the bridge hub runtime.
223	///
224	/// The `bridge_origin_relative_location` is the relative (to the `here_universal_location`)
225	/// location of the bridge endpoint at this side of the bridge. It may be the parent relay
226	/// chain or the sibling parachain. All junctions below parachain level are dropped.
227	///
228	/// The `bridge_destination_universal_location` is the universal location of the bridge
229	/// destination. It may be the parent relay or the sibling parachain of the **bridged**
230	/// bridge hub. All junctions below parachain level are dropped.
231	///
232	/// Why we drop all junctions between parachain level - that's because the lane is a bridge
233	/// between two chains. All routing under this level happens when the message is delivered
234	/// to the bridge destination. So at bridge level we don't care about low level junctions.
235	///
236	/// Returns error if `bridge_origin_relative_location` is outside of `here_universal_location`
237	/// local consensus OR if `bridge_destination_universal_location` is not a universal location.
238	pub fn bridge_locations(
239		here_universal_location: InteriorLocation,
240		bridge_origin_relative_location: Location,
241		bridge_destination_universal_location: InteriorLocation,
242		expected_remote_network: NetworkId,
243	) -> Result<Box<Self>, BridgeLocationsError> {
244		fn strip_low_level_junctions(
245			location: InteriorLocation,
246		) -> Result<InteriorLocation, BridgeLocationsError> {
247			let mut junctions = location.into_iter();
248
249			let global_consensus = junctions
250				.next()
251				.filter(|junction| matches!(junction, GlobalConsensus(_)))
252				.ok_or(BridgeLocationsError::NonUniversalLocation)?;
253
254			// we only expect `Parachain` junction here. There are other junctions that
255			// may need to be supported (like `GeneralKey` and `OnlyChild`), but now we
256			// only support bridges with relay and parachans
257			//
258			// if there's something other than parachain, let's strip it
259			let maybe_parachain =
260				junctions.next().filter(|junction| matches!(junction, Parachain(_)));
261			Ok(match maybe_parachain {
262				Some(parachain) => [global_consensus, parachain].into(),
263				None => [global_consensus].into(),
264			})
265		}
266
267		// ensure that the `here_universal_location` and `bridge_destination_universal_location`
268		// are universal locations within different consensus systems
269		let local_network = here_universal_location
270			.global_consensus()
271			.map_err(|_| BridgeLocationsError::NonUniversalLocation)?;
272		let remote_network = bridge_destination_universal_location
273			.global_consensus()
274			.map_err(|_| BridgeLocationsError::NonUniversalLocation)?;
275		ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal);
276		ensure!(
277			remote_network == expected_remote_network,
278			BridgeLocationsError::UnreachableDestination
279		);
280
281		// get universal location of endpoint, located at this side of the bridge
282		let bridge_origin_universal_location = here_universal_location
283			.within_global(bridge_origin_relative_location.clone())
284			.map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?;
285		// strip low-level junctions within universal locations
286		let bridge_origin_universal_location =
287			strip_low_level_junctions(bridge_origin_universal_location)?;
288		let bridge_destination_universal_location =
289			strip_low_level_junctions(bridge_destination_universal_location)?;
290
291		// we know that the `bridge_destination_universal_location` starts from the
292		// `GlobalConsensus` and we know that the `bridge_origin_universal_location`
293		// is also within the `GlobalConsensus`. So we know that the lane id will be
294		// the same on both ends of the bridge
295		let bridge_id = BridgeId::new(
296			&bridge_origin_universal_location,
297			&bridge_destination_universal_location,
298		);
299
300		Ok(Box::new(BridgeLocations {
301			bridge_origin_relative_location,
302			bridge_origin_universal_location,
303			bridge_destination_universal_location,
304			bridge_id,
305		}))
306	}
307
308	/// Getter for `bridge_origin_relative_location`
309	pub fn bridge_origin_relative_location(&self) -> &Location {
310		&self.bridge_origin_relative_location
311	}
312
313	/// Getter for `bridge_origin_universal_location`
314	pub fn bridge_origin_universal_location(&self) -> &InteriorLocation {
315		&self.bridge_origin_universal_location
316	}
317
318	/// Getter for `bridge_destination_universal_location`
319	pub fn bridge_destination_universal_location(&self) -> &InteriorLocation {
320		&self.bridge_destination_universal_location
321	}
322
323	/// Getter for `bridge_id`
324	pub fn bridge_id(&self) -> &BridgeId {
325		&self.bridge_id
326	}
327
328	/// Generates the exact same `LaneId` on the both bridge hubs.
329	///
330	/// Note: Use this **only** when opening a new bridge.
331	pub fn calculate_lane_id<LaneId: LaneIdType>(
332		&self,
333		xcm_version: XcmVersion,
334	) -> Result<LaneId, BridgeLocationsError> {
335		// a tricky helper struct that adds required `Ord` support for
336		// `VersionedInteriorLocation`
337		#[derive(Eq, PartialEq, Ord, PartialOrd)]
338		struct EncodedVersionedInteriorLocation(sp_std::vec::Vec<u8>);
339		impl Encode for EncodedVersionedInteriorLocation {
340			fn encode(&self) -> sp_std::vec::Vec<u8> {
341				self.0.clone()
342			}
343		}
344
345		let universal_location1 =
346			VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone())
347				.into_version(xcm_version)
348				.map_err(|_| BridgeLocationsError::UnsupportedXcmVersion);
349		let universal_location2 =
350			VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone())
351				.into_version(xcm_version)
352				.map_err(|_| BridgeLocationsError::UnsupportedXcmVersion);
353
354		LaneId::try_new(
355			EncodedVersionedInteriorLocation(universal_location1.encode()),
356			EncodedVersionedInteriorLocation(universal_location2.encode()),
357		)
358		.map_err(|_| BridgeLocationsError::UnsupportedLaneIdType)
359	}
360}
361
362#[cfg(test)]
363mod tests {
364	use super::*;
365	use xcm::latest::ROCOCO_GENESIS_HASH;
366
367	const LOCAL_NETWORK: NetworkId = Kusama;
368	const REMOTE_NETWORK: NetworkId = Polkadot;
369	const UNREACHABLE_NETWORK: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH);
370	const SIBLING_PARACHAIN: u32 = 1000;
371	const LOCAL_BRIDGE_HUB: u32 = 1001;
372	const REMOTE_PARACHAIN: u32 = 2000;
373
374	struct SuccessfulTest {
375		here_universal_location: InteriorLocation,
376		bridge_origin_relative_location: Location,
377
378		bridge_origin_universal_location: InteriorLocation,
379		bridge_destination_universal_location: InteriorLocation,
380
381		expected_remote_network: NetworkId,
382	}
383
384	fn run_successful_test(test: SuccessfulTest) -> BridgeLocations {
385		let locations = BridgeLocations::bridge_locations(
386			test.here_universal_location,
387			test.bridge_origin_relative_location.clone(),
388			test.bridge_destination_universal_location.clone(),
389			test.expected_remote_network,
390		);
391		assert_eq!(
392			locations,
393			Ok(Box::new(BridgeLocations {
394				bridge_origin_relative_location: test.bridge_origin_relative_location,
395				bridge_origin_universal_location: test.bridge_origin_universal_location.clone(),
396				bridge_destination_universal_location: test
397					.bridge_destination_universal_location
398					.clone(),
399				bridge_id: BridgeId::new(
400					&test.bridge_origin_universal_location,
401					&test.bridge_destination_universal_location,
402				),
403			})),
404		);
405
406		*locations.unwrap()
407	}
408
409	// successful tests that with various origins and destinations
410
411	#[test]
412	fn at_relay_from_local_relay_to_remote_relay_works() {
413		run_successful_test(SuccessfulTest {
414			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
415			bridge_origin_relative_location: Here.into(),
416
417			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
418			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
419
420			expected_remote_network: REMOTE_NETWORK,
421		});
422	}
423
424	#[test]
425	fn at_relay_from_sibling_parachain_to_remote_relay_works() {
426		run_successful_test(SuccessfulTest {
427			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
428			bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(),
429
430			bridge_origin_universal_location: [
431				GlobalConsensus(LOCAL_NETWORK),
432				Parachain(SIBLING_PARACHAIN),
433			]
434			.into(),
435			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
436
437			expected_remote_network: REMOTE_NETWORK,
438		});
439	}
440
441	#[test]
442	fn at_relay_from_local_relay_to_remote_parachain_works() {
443		run_successful_test(SuccessfulTest {
444			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
445			bridge_origin_relative_location: Here.into(),
446
447			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
448			bridge_destination_universal_location: [
449				GlobalConsensus(REMOTE_NETWORK),
450				Parachain(REMOTE_PARACHAIN),
451			]
452			.into(),
453
454			expected_remote_network: REMOTE_NETWORK,
455		});
456	}
457
458	#[test]
459	fn at_relay_from_sibling_parachain_to_remote_parachain_works() {
460		run_successful_test(SuccessfulTest {
461			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
462			bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(),
463
464			bridge_origin_universal_location: [
465				GlobalConsensus(LOCAL_NETWORK),
466				Parachain(SIBLING_PARACHAIN),
467			]
468			.into(),
469			bridge_destination_universal_location: [
470				GlobalConsensus(REMOTE_NETWORK),
471				Parachain(REMOTE_PARACHAIN),
472			]
473			.into(),
474
475			expected_remote_network: REMOTE_NETWORK,
476		});
477	}
478
479	#[test]
480	fn at_bridge_hub_from_local_relay_to_remote_relay_works() {
481		run_successful_test(SuccessfulTest {
482			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
483				.into(),
484			bridge_origin_relative_location: Parent.into(),
485
486			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
487			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
488
489			expected_remote_network: REMOTE_NETWORK,
490		});
491	}
492
493	#[test]
494	fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() {
495		run_successful_test(SuccessfulTest {
496			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
497				.into(),
498			bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
499				.into(),
500
501			bridge_origin_universal_location: [
502				GlobalConsensus(LOCAL_NETWORK),
503				Parachain(SIBLING_PARACHAIN),
504			]
505			.into(),
506			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
507
508			expected_remote_network: REMOTE_NETWORK,
509		});
510	}
511
512	#[test]
513	fn at_bridge_hub_from_local_relay_to_remote_parachain_works() {
514		run_successful_test(SuccessfulTest {
515			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
516				.into(),
517			bridge_origin_relative_location: Parent.into(),
518
519			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
520			bridge_destination_universal_location: [
521				GlobalConsensus(REMOTE_NETWORK),
522				Parachain(REMOTE_PARACHAIN),
523			]
524			.into(),
525
526			expected_remote_network: REMOTE_NETWORK,
527		});
528	}
529
530	#[test]
531	fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() {
532		run_successful_test(SuccessfulTest {
533			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
534				.into(),
535			bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
536				.into(),
537
538			bridge_origin_universal_location: [
539				GlobalConsensus(LOCAL_NETWORK),
540				Parachain(SIBLING_PARACHAIN),
541			]
542			.into(),
543			bridge_destination_universal_location: [
544				GlobalConsensus(REMOTE_NETWORK),
545				Parachain(REMOTE_PARACHAIN),
546			]
547			.into(),
548
549			expected_remote_network: REMOTE_NETWORK,
550		});
551	}
552
553	// successful tests that show that we are ignoring low-level junctions of bridge origins
554
555	#[test]
556	fn low_level_junctions_at_bridge_origin_are_stripped() {
557		let locations1 = run_successful_test(SuccessfulTest {
558			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
559			bridge_origin_relative_location: Here.into(),
560
561			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
562			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
563
564			expected_remote_network: REMOTE_NETWORK,
565		});
566		let locations2 = run_successful_test(SuccessfulTest {
567			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
568			bridge_origin_relative_location: [PalletInstance(0)].into(),
569
570			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
571			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
572
573			expected_remote_network: REMOTE_NETWORK,
574		});
575
576		assert_eq!(locations1.bridge_id, locations2.bridge_id);
577	}
578
579	#[test]
580	fn low_level_junctions_at_bridge_destination_are_stripped() {
581		let locations1 = run_successful_test(SuccessfulTest {
582			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
583			bridge_origin_relative_location: Here.into(),
584
585			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
586			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
587
588			expected_remote_network: REMOTE_NETWORK,
589		});
590		let locations2 = run_successful_test(SuccessfulTest {
591			here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
592			bridge_origin_relative_location: Here.into(),
593
594			bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
595			bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
596
597			expected_remote_network: REMOTE_NETWORK,
598		});
599
600		assert_eq!(locations1.bridge_id, locations2.bridge_id);
601	}
602
603	#[test]
604	fn calculate_lane_id_works() {
605		type TestLaneId = bp_messages::HashedLaneId;
606
607		let from_local_to_remote = run_successful_test(SuccessfulTest {
608			here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
609				.into(),
610			bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
611				.into(),
612
613			bridge_origin_universal_location: [
614				GlobalConsensus(LOCAL_NETWORK),
615				Parachain(SIBLING_PARACHAIN),
616			]
617			.into(),
618			bridge_destination_universal_location: [
619				GlobalConsensus(REMOTE_NETWORK),
620				Parachain(REMOTE_PARACHAIN),
621			]
622			.into(),
623
624			expected_remote_network: REMOTE_NETWORK,
625		});
626
627		let from_remote_to_local = run_successful_test(SuccessfulTest {
628			here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
629				.into(),
630			bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into())
631				.into(),
632
633			bridge_origin_universal_location: [
634				GlobalConsensus(REMOTE_NETWORK),
635				Parachain(REMOTE_PARACHAIN),
636			]
637			.into(),
638			bridge_destination_universal_location: [
639				GlobalConsensus(LOCAL_NETWORK),
640				Parachain(SIBLING_PARACHAIN),
641			]
642			.into(),
643
644			expected_remote_network: LOCAL_NETWORK,
645		});
646
647		assert_ne!(
648			from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
649			from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION - 1),
650		);
651		assert_eq!(
652			from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
653			from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
654		);
655	}
656
657	// negative tests
658
659	#[test]
660	fn bridge_locations_fails_when_here_is_not_universal_location() {
661		assert_eq!(
662			BridgeLocations::bridge_locations(
663				[Parachain(1000)].into(),
664				Here.into(),
665				[GlobalConsensus(REMOTE_NETWORK)].into(),
666				REMOTE_NETWORK,
667			),
668			Err(BridgeLocationsError::NonUniversalLocation),
669		);
670	}
671
672	#[test]
673	fn bridge_locations_fails_when_computed_destination_is_not_universal_location() {
674		assert_eq!(
675			BridgeLocations::bridge_locations(
676				[GlobalConsensus(LOCAL_NETWORK)].into(),
677				Here.into(),
678				[OnlyChild].into(),
679				REMOTE_NETWORK,
680			),
681			Err(BridgeLocationsError::NonUniversalLocation),
682		);
683	}
684
685	#[test]
686	fn bridge_locations_fails_when_computed_destination_is_local() {
687		assert_eq!(
688			BridgeLocations::bridge_locations(
689				[GlobalConsensus(LOCAL_NETWORK)].into(),
690				Here.into(),
691				[GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(),
692				REMOTE_NETWORK,
693			),
694			Err(BridgeLocationsError::DestinationIsLocal),
695		);
696	}
697
698	#[test]
699	fn bridge_locations_fails_when_computed_destination_is_unreachable() {
700		assert_eq!(
701			BridgeLocations::bridge_locations(
702				[GlobalConsensus(LOCAL_NETWORK)].into(),
703				Here.into(),
704				[GlobalConsensus(UNREACHABLE_NETWORK)].into(),
705				REMOTE_NETWORK,
706			),
707			Err(BridgeLocationsError::UnreachableDestination),
708		);
709	}
710}