referrerpolicy=no-referrer-when-downgrade

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