referrerpolicy=no-referrer-when-downgrade

pallet_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//! Module that adds XCM support to bridge pallets. The pallet allows to dynamically
18//! open and close bridges between local (to this pallet location) and remote XCM
19//! destinations.
20//!
21//! The `pallet_xcm_bridge_hub` pallet is used to manage (open, close) bridges between chains from
22//! different consensuses. The new extrinsics `fn open_bridge` and `fn close_bridge` are introduced.
23//! Other chains can manage channels with different bridged global consensuses.
24//!
25//! # Concept of `lane` and `LaneId`
26//!
27//! There is another `pallet_bridge_messages` pallet that handles inbound/outbound lanes for
28//! messages. Each lane is a unique connection between two chains from different consensuses and is
29//! identified by `LaneId`. `LaneId` is generated once when a new bridge is requested by `fn
30//! open_bridge`. It is generated by `BridgeLocations::calculate_lane_id` based on the following
31//! parameters:
32//! - Source `bridge_origin_universal_location` (latest XCM)
33//! - Destination `bridge_destination_universal_location` (latest XCM)
34//! - XCM version (both sides of the bridge must use the same parameters to generate the same
35//!   `LaneId`)
36//!   - `bridge_origin_universal_location`, `bridge_destination_universal_location` is converted to
37//!     the `Versioned*` structs
38//!
39//! `LaneId` is expected to never change because:
40//! - We need the same `LaneId` on both sides of the bridge, as `LaneId` is part of the message key
41//!   proofs.
42//! - Runtime upgrades are entirely asynchronous.
43//! - We already have a running production Polkadot/Kusama bridge that uses `LaneId([0, 0, 0, 0])`.
44//!
45//! `LaneId` is backward compatible, meaning it can be encoded/decoded from the older format `[u8;
46//! 4]` used for static lanes, as well as the new format `H256` generated by
47//! `BridgeLocations::calculate_lane_id`.
48//!
49//! # Concept of `bridge` and `BridgeId`
50//!
51//! The `pallet_xcm_bridge_hub` pallet needs to store some metadata about opened bridges. The bridge
52//! (or bridge metadata) is stored under the `BridgeId` key.
53//!
54//! `BridgeId` is generated from `bridge_origin_relative_location` and
55//! `bridge_origin_universal_location` using the `latest` XCM structs. `BridgeId` is not transferred
56//! over the bridge; it is only important for local consensus. It essentially serves as an index/key
57//! to bridge metadata. All the XCM infrastructure around `XcmExecutor`, `SendXcm`, `ExportXcm` use
58//! the `latest` XCM, so `BridgeId` must remain compatible with the `latest` XCM. For example, we
59//! have an `ExportXcm` implementation in `exporter.rs` that handles the `ExportMessage` instruction
60//! with `universal_source` and `destination` (latest XCM), so we need to create `BridgeId` and the
61//! corresponding `LaneId`.
62//!
63//! # Migrations and State
64//!
65//! This pallet implements `try_state`, ensuring compatibility and checking everything so we know if
66//! any migration is needed. `do_try_state` checks for `BridgeId` compatibility, which is
67//! recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything,
68//! except removing older XCM versions. In such cases, we need to add migration for `BridgeId` and
69//! stored `Versioned*` structs and update `LaneToBridge` mapping, but this won't affect `LaneId`
70//! over the bridge.
71//!
72//! # How to Open a Bridge?
73//!
74//! The `pallet_xcm_bridge_hub` pallet has the extrinsic `fn open_bridge` and an important
75//! configuration `pallet_xcm_bridge_hub::Config::OpenBridgeOrigin`, which translates the call's
76//! origin to the XCM `Location` and converts it to the `bridge_origin_universal_location`. With the
77//! current setup, this origin/location is expected to be either the relay chain or a sibling
78//! parachain as one side of the bridge. Another parameter is
79//! `bridge_destination_universal_location`, which is the other side of the bridge from a different
80//! global consensus.
81//!
82//! Every bridge between two XCM locations has a dedicated lane in associated
83//! messages pallet. Assuming that this pallet is deployed at the bridge hub
84//! parachain and there's a similar pallet at the bridged network, the dynamic
85//! bridge lifetime is as follows:
86//!
87//! 1) the sibling parachain opens a XCMP channel with this bridge hub;
88//!
89//! 2) the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold
90//!    enough funds to pay for the bridge (see `BridgeDeposit`);
91//!
92//! 3) the sibling parachain opens the bridge by sending XCM `Transact` instruction with the
93//!    `open_bridge` call. The `BridgeDeposit` amount is reserved on the sovereign account of
94//!    sibling parachain;
95//!
96//! 4) at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to
97//!    connect over the bridge need to coordinate the moment when they start sending messages over
98//!    the bridge. Otherwise they may lose messages and/or bundled assets;
99//!
100//! 5) when either side wants to close the bridge, it sends the XCM `Transact` with the
101//!    `close_bridge` call. The bridge is closed immediately if there are no queued messages.
102//!    Otherwise, the owner must repeat the `close_bridge` call to prune all queued messages first.
103//!
104//! The pallet doesn't provide any mechanism for graceful closure, because it always involves
105//! some contract between two connected chains and the bridge hub knows nothing about that. It
106//! is the task for the connected chains to make sure that all required actions are completed
107//! before the closure. In the end, the bridge hub can't even guarantee that all messages that
108//! are delivered to the destination, are processed in the way their sender expects. So if we
109//! can't guarantee that, we shall not care about more complex procedures and leave it to the
110//! participating parties.
111//!
112//! # Example
113//!
114//! Example of opening a bridge between some random parachains from Polkadot and Kusama:
115//!
116//! 0. Let's have:
117//! 	- BridgeHubPolkadot with `UniversalLocation` = `[GlobalConsensus(Polkadot), Parachain(1002)]`
118//! 	- BridgeHubKusama with `UniversalLocation` = `[GlobalConsensus(Kusama), Parachain(1002)]`
119//! 1. The Polkadot local sibling parachain `Location::new(1, Parachain(1234))` must send some DOTs
120//!    to its sovereign account on BridgeHubPolkadot to cover `BridgeDeposit`, fees for `Transact`,
121//!    and the existential deposit.
122//! 2. Send a call to the BridgeHubPolkadot from the local sibling parachain: `Location::new(1,
123//!    Parachain(1234))` ``` xcm::Transact( origin_kind: OriginKind::Xcm,
124//!    XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama),
125//!    Parachain(4567)].into()), ); ) ```
126//! 3. Check the stored bridge metadata and generated `LaneId`.
127//! 4. The Kusama local sibling parachain `Location::new(1, Parachain(4567))` must send some KSMs to
128//!    its sovereign account
129//! on BridgeHubKusama to cover `BridgeDeposit`, fees for `Transact`, and the existential deposit.
130//! 5. Send a call to the BridgeHubKusama from the local sibling parachain: `Location::new(1,
131//!    Parachain(4567))` ``` xcm::Transact( origin_kind: OriginKind::Xcm,
132//!    XcmOverBridgeHubKusama::open_bridge(
133//!    VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); ) ```
134//! 6. Check the stored bridge metadata and generated `LaneId`.
135//! 7. Both `LaneId`s from steps 3 and 6 must be the same (see above _Concept of `lane` and
136//!    `LaneId`_).
137//! 8. Run the bridge messages relayer for `LaneId`.
138//! 9. Send messages from both sides.
139//!
140//! The opening bridge holds the configured `BridgeDeposit` from the origin's sovereign account, but
141//! this deposit is returned when the bridge is closed with `fn close_bridge`.
142
143#![warn(missing_docs)]
144#![cfg_attr(not(feature = "std"), no_std)]
145
146use bp_messages::{LaneState, MessageNonce};
147use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt};
148use bp_xcm_bridge_hub::BridgeLocationsError;
149pub use bp_xcm_bridge_hub::{
150	Bridge, BridgeId, BridgeLocations, BridgeState, LocalXcmChannelManager,
151};
152use frame_support::{traits::fungible::MutateHold, DefaultNoBound};
153use frame_system::Config as SystemConfig;
154use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError};
155use sp_runtime::traits::Zero;
156use sp_std::{boxed::Box, vec::Vec};
157use xcm::prelude::*;
158use xcm_builder::DispatchBlob;
159use xcm_executor::traits::ConvertLocation;
160
161pub use bp_xcm_bridge_hub::XcmAsPlainPayload;
162pub use dispatcher::XcmBlobMessageDispatchResult;
163pub use exporter::PalletAsHaulBlobExporter;
164pub use pallet::*;
165
166mod dispatcher;
167mod exporter;
168pub mod migration;
169mod mock;
170
171/// The target that will be used when publishing logs related to this pallet.
172pub const LOG_TARGET: &str = "runtime::bridge-xcm";
173
174#[frame_support::pallet]
175pub mod pallet {
176	use super::*;
177	use frame_support::{
178		pallet_prelude::*,
179		traits::{tokens::Precision, Contains},
180	};
181	use frame_system::pallet_prelude::{BlockNumberFor, *};
182
183	/// The reason for this pallet placing a hold on funds.
184	#[pallet::composite_enum]
185	pub enum HoldReason<I: 'static = ()> {
186		/// The funds are held as a deposit for opened bridge.
187		#[codec(index = 0)]
188		BridgeDeposit,
189	}
190
191	#[pallet::config]
192	#[pallet::disable_frame_system_supertrait_check]
193	pub trait Config<I: 'static = ()>:
194		BridgeMessagesConfig<Self::BridgeMessagesPalletInstance>
195	{
196		/// The overarching event type.
197		#[allow(deprecated)]
198		type RuntimeEvent: From<Event<Self, I>>
199			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
200
201		/// Runtime's universal location.
202		type UniversalLocation: Get<InteriorLocation>;
203		// TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and
204		// replace it with the `NetworkId` - then we'll be able to use
205		// `T as pallet_bridge_messages::Config<T::BridgeMessagesPalletInstance>::BridgedChain::NetworkId`
206		/// Bridged network as relative location of bridged `GlobalConsensus`.
207		#[pallet::constant]
208		type BridgedNetwork: Get<Location>;
209		/// Associated messages pallet instance that bridges us with the
210		/// `BridgedNetworkId` consensus.
211		type BridgeMessagesPalletInstance: 'static;
212
213		/// Price of single message export to the bridged consensus (`Self::BridgedNetwork`).
214		type MessageExportPrice: Get<Assets>;
215		/// Checks the XCM version for the destination.
216		type DestinationVersion: GetVersion;
217
218		/// The origin that is allowed to call privileged operations on the pallet, e.g. open/close
219		/// bridge for locations.
220		type ForceOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
221		/// A set of XCM locations within local consensus system that are allowed to open
222		/// bridges with remote destinations.
223		type OpenBridgeOrigin: EnsureOrigin<
224			<Self as SystemConfig>::RuntimeOrigin,
225			Success = Location,
226		>;
227		/// A converter between a location and a sovereign account.
228		type BridgeOriginAccountIdConverter: ConvertLocation<AccountIdOf<ThisChainOf<Self, I>>>;
229
230		/// Amount of this chain native tokens that is reserved on the sibling parachain account
231		/// when bridge open request is registered.
232		#[pallet::constant]
233		type BridgeDeposit: Get<BalanceOf<ThisChainOf<Self, I>>>;
234		/// Currency used to pay for bridge registration.
235		type Currency: MutateHold<
236			AccountIdOf<ThisChainOf<Self, I>>,
237			Balance = BalanceOf<ThisChainOf<Self, I>>,
238			Reason = Self::RuntimeHoldReason,
239		>;
240		/// The overarching runtime hold reason.
241		type RuntimeHoldReason: From<HoldReason<I>>;
242		/// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`.
243		/// For example, it is possible to make an exception for a system parachain or relay.
244		type AllowWithoutBridgeDeposit: Contains<Location>;
245
246		/// Local XCM channel manager.
247		type LocalXcmChannelManager: LocalXcmChannelManager;
248		/// XCM-level dispatcher for inbound bridge messages.
249		type BlobDispatcher: DispatchBlob;
250	}
251
252	/// An alias for the bridge metadata.
253	pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>, LaneIdOf<T, I>>;
254	/// An alias for this chain.
255	pub type ThisChainOf<T, I> =
256		pallet_bridge_messages::ThisChainOf<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
257	/// An alias for lane identifier type.
258	pub type LaneIdOf<T, I> =
259		<T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::LaneId;
260	/// An alias for the associated lanes manager.
261	pub type LanesManagerOf<T, I> =
262		pallet_bridge_messages::LanesManager<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
263
264	#[pallet::pallet]
265	#[pallet::storage_version(migration::STORAGE_VERSION)]
266	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
267
268	#[pallet::hooks]
269	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
270		fn integrity_test() {
271			assert!(
272				Self::bridged_network_id().is_ok(),
273				"Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`",
274				T::BridgedNetwork::get()
275			)
276		}
277
278		#[cfg(feature = "try-runtime")]
279		fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
280			Self::do_try_state()
281		}
282	}
283
284	#[pallet::call]
285	impl<T: Config<I>, I: 'static> Pallet<T, I> {
286		/// Open a bridge between two locations.
287		///
288		/// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling
289		/// parachain or a parent relay chain). The `bridge_destination_universal_location` must be
290		/// a destination within the consensus of the `T::BridgedNetwork` network.
291		///
292		/// The `BridgeDeposit` amount is reserved on the caller account. This deposit
293		/// is unreserved after bridge is closed.
294		///
295		/// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane
296		/// is `Opened`.
297		#[pallet::call_index(0)]
298		#[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl
299		pub fn open_bridge(
300			origin: OriginFor<T>,
301			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
302		) -> DispatchResult {
303			// check and compute required bridge locations and laneId
304			let xcm_version = bridge_destination_universal_location.identify_version();
305			let locations =
306				Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
307			let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| {
308				tracing::trace!(
309					target: LOG_TARGET, error=?e,
310					"calculate_lane_id error"
311				);
312				Error::<T, I>::BridgeLocations(e)
313			})?;
314
315			Self::do_open_bridge(locations, lane_id, true)
316		}
317
318		/// Try to close the bridge.
319		///
320		/// Can only be called by the "owner" of this side of the bridge, meaning that the
321		/// inbound XCM channel with the local origin chain is working.
322		///
323		/// Closed bridge is a bridge without any traces in the runtime storage. So this method
324		/// first tries to prune all queued messages at the outbound lane. When there are no
325		/// outbound messages left, outbound and inbound lanes are purged. After that, funds
326		/// are returned back to the owner of this side of the bridge.
327		///
328		/// The number of messages that we may prune in a single call is limited by the
329		/// `may_prune_messages` argument. If there are more messages in the queue, the method
330		/// prunes exactly `may_prune_messages` and exits early. The caller may call it again
331		/// until outbound queue is depleted and get his funds back.
332		///
333		/// The states after this call: everything is either `Closed`, or purged from the
334		/// runtime storage.
335		#[pallet::call_index(1)]
336		#[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl
337		pub fn close_bridge(
338			origin: OriginFor<T>,
339			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
340			may_prune_messages: MessageNonce,
341		) -> DispatchResult {
342			// compute required bridge locations
343			let locations =
344				Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
345
346			// TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if
347			// bridge/lanes are already closed + for messages that are not pruned
348
349			// update bridge metadata - this also guarantees that the bridge is in the proper state
350			let bridge =
351				Bridges::<T, I>::try_mutate_exists(locations.bridge_id(), |bridge| match bridge {
352					Some(bridge) => {
353						bridge.state = BridgeState::Closed;
354						Ok(bridge.clone())
355					},
356					None => Err(Error::<T, I>::UnknownBridge),
357				})?;
358
359			// close inbound and outbound lanes
360			let lanes_manager = LanesManagerOf::<T, I>::new();
361			let mut inbound_lane = lanes_manager
362				.any_state_inbound_lane(bridge.lane_id)
363				.map_err(Error::<T, I>::LanesManager)?;
364			let mut outbound_lane = lanes_manager
365				.any_state_outbound_lane(bridge.lane_id)
366				.map_err(Error::<T, I>::LanesManager)?;
367
368			// now prune queued messages
369			let mut pruned_messages = 0;
370			for _ in outbound_lane.queued_messages() {
371				if pruned_messages == may_prune_messages {
372					break;
373				}
374
375				outbound_lane.remove_oldest_unpruned_message();
376				pruned_messages += 1;
377			}
378
379			// if there are outbound messages in the queue, just update states and early exit
380			if !outbound_lane.queued_messages().is_empty() {
381				// update lanes state. Under normal circumstances, following calls shall never fail
382				inbound_lane.set_state(LaneState::Closed);
383				outbound_lane.set_state(LaneState::Closed);
384
385				// write something to log
386				let enqueued_messages = outbound_lane.queued_messages().saturating_len();
387				tracing::trace!(
388					target: LOG_TARGET,
389					bridge_id=?locations.bridge_id(),
390					bridge_origin=?locations.bridge_origin_universal_location(),
391					bridge_destination=?locations.bridge_destination_universal_location(),
392					lane_id=?bridge.lane_id,
393					messages_remaining=%enqueued_messages,
394					"Bridge is closing."
395				);
396
397				// deposit the `ClosingBridge` event
398				Self::deposit_event(Event::<T, I>::ClosingBridge {
399					bridge_id: *locations.bridge_id(),
400					lane_id: bridge.lane_id.into(),
401					pruned_messages,
402					enqueued_messages,
403				});
404
405				return Ok(());
406			}
407
408			// else we have pruned all messages, so lanes and the bridge itself may gone
409			inbound_lane.purge();
410			outbound_lane.purge();
411			Bridges::<T, I>::remove(locations.bridge_id());
412			LaneToBridge::<T, I>::remove(bridge.lane_id);
413
414			// return deposit
415			let released_deposit = T::Currency::release(
416				&HoldReason::BridgeDeposit.into(),
417				&bridge.bridge_owner_account,
418				bridge.deposit,
419				Precision::BestEffort,
420			)
421			.inspect_err(|e| {
422				// we can't do anything here - looks like funds have been (partially) unreserved
423				// before by someone else. Let's not fail, though - it'll be worse for the caller
424				tracing::error!(
425					target: LOG_TARGET,
426					error=?e,
427					bridge_id=?locations.bridge_id(),
428					"Failed to unreserve during the bridge closure"
429				);
430			})
431			.ok()
432			.unwrap_or(BalanceOf::<ThisChainOf<T, I>>::zero());
433
434			// write something to log
435			tracing::trace!(
436				target: LOG_TARGET,
437				bridge_id=?locations.bridge_id(),
438				bridge_origin=?locations.bridge_origin_universal_location(),
439				bridge_destination=?locations.bridge_destination_universal_location(),
440				lane_id=?bridge.lane_id,
441				bridge_deposit=?released_deposit,
442				"Bridge has closed, the bridge deposit was returned"
443			);
444
445			// deposit the `BridgePruned` event
446			Self::deposit_event(Event::<T, I>::BridgePruned {
447				bridge_id: *locations.bridge_id(),
448				lane_id: bridge.lane_id.into(),
449				bridge_deposit: released_deposit,
450				pruned_messages,
451			});
452
453			Ok(())
454		}
455	}
456
457	impl<T: Config<I>, I: 'static> Pallet<T, I> {
458		/// Open bridge for lane.
459		pub fn do_open_bridge(
460			locations: Box<BridgeLocations>,
461			lane_id: T::LaneId,
462			create_lanes: bool,
463		) -> Result<(), DispatchError> {
464			// reserve balance on the origin's sovereign account (if needed)
465			let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
466				locations.bridge_origin_relative_location(),
467			)
468			.ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
469			let deposit = if T::AllowWithoutBridgeDeposit::contains(
470				locations.bridge_origin_relative_location(),
471			) {
472				BalanceOf::<ThisChainOf<T, I>>::zero()
473			} else {
474				let deposit = T::BridgeDeposit::get();
475				T::Currency::hold(
476					&HoldReason::BridgeDeposit.into(),
477					&bridge_owner_account,
478					deposit,
479				)
480				.map_err(|e| {
481					tracing::error!(
482						target: LOG_TARGET,
483						error=?e,
484						?deposit,
485						?bridge_owner_account,
486						bridge_origin_relative_location=?locations.bridge_origin_relative_location(),
487						"Failed to hold bridge deposit"
488					);
489					Error::<T, I>::FailedToReserveBridgeDeposit
490				})?;
491				deposit
492			};
493
494			// save bridge metadata
495			Bridges::<T, I>::try_mutate(locations.bridge_id(), |bridge| match bridge {
496				Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
497				None => {
498					*bridge = Some(BridgeOf::<T, I> {
499						bridge_origin_relative_location: Box::new(
500							locations.bridge_origin_relative_location().clone().into(),
501						),
502						bridge_origin_universal_location: Box::new(
503							locations.bridge_origin_universal_location().clone().into(),
504						),
505						bridge_destination_universal_location: Box::new(
506							locations.bridge_destination_universal_location().clone().into(),
507						),
508						state: BridgeState::Opened,
509						bridge_owner_account,
510						deposit,
511						lane_id,
512					});
513					Ok(())
514				},
515			})?;
516			// save lane to bridge mapping
517			LaneToBridge::<T, I>::try_mutate(lane_id, |bridge| match bridge {
518				Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
519				None => {
520					*bridge = Some(*locations.bridge_id());
521					Ok(())
522				},
523			})?;
524
525			if create_lanes {
526				// create new lanes. Under normal circumstances, following calls shall never fail
527				let lanes_manager = LanesManagerOf::<T, I>::new();
528				lanes_manager
529					.create_inbound_lane(lane_id)
530					.map_err(Error::<T, I>::LanesManager)?;
531				lanes_manager
532					.create_outbound_lane(lane_id)
533					.map_err(Error::<T, I>::LanesManager)?;
534			}
535
536			// write something to log
537			tracing::trace!(
538				target: LOG_TARGET,
539				bridge_id=?locations.bridge_id(),
540				bridge_origin=?locations.bridge_origin_universal_location(),
541				bridge_destination=?locations.bridge_destination_universal_location(),
542				lane_id=?lane_id,
543				"Bridge has been opened"
544			);
545
546			// deposit `BridgeOpened` event
547			Self::deposit_event(Event::<T, I>::BridgeOpened {
548				bridge_id: *locations.bridge_id(),
549				bridge_deposit: deposit,
550				local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()),
551				remote_endpoint: Box::new(
552					locations.bridge_destination_universal_location().clone(),
553				),
554				lane_id: lane_id.into(),
555			});
556
557			Ok(())
558		}
559	}
560
561	impl<T: Config<I>, I: 'static> Pallet<T, I> {
562		/// Return bridge endpoint locations and dedicated lane identifier. This method converts
563		/// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin`
564		/// converter.
565		pub fn bridge_locations_from_origin(
566			origin: OriginFor<T>,
567			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
568		) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
569			Self::bridge_locations(
570				T::OpenBridgeOrigin::ensure_origin(origin)?,
571				(*bridge_destination_universal_location)
572					.try_into()
573					.map_err(|_| Error::<T, I>::UnsupportedXcmVersion)?,
574			)
575		}
576
577		/// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`).
578		pub fn bridge_locations(
579			bridge_origin_relative_location: Location,
580			bridge_destination_universal_location: InteriorLocation,
581		) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
582			BridgeLocations::bridge_locations(
583				T::UniversalLocation::get(),
584				bridge_origin_relative_location,
585				bridge_destination_universal_location,
586				Self::bridged_network_id()?,
587			)
588			.map_err(|e| {
589				tracing::trace!(
590					target: LOG_TARGET, error=?e,
591					"bridge_locations error"
592				);
593				Error::<T, I>::BridgeLocations(e).into()
594			})
595		}
596
597		/// Return bridge metadata by bridge_id
598		pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
599			Bridges::<T, I>::get(bridge_id)
600		}
601
602		/// Return bridge metadata by lane_id
603		pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
604			LaneToBridge::<T, I>::get(lane_id)
605				.and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
606		}
607	}
608
609	impl<T: Config<I>, I: 'static> Pallet<T, I> {
610		/// Returns some `NetworkId` if contains `GlobalConsensus` junction.
611		fn bridged_network_id() -> Result<NetworkId, sp_runtime::DispatchError> {
612			match T::BridgedNetwork::get().take_first_interior() {
613				Some(GlobalConsensus(network)) => Ok(network),
614				_ => Err(Error::<T, I>::BridgeLocations(
615					BridgeLocationsError::InvalidBridgeDestination,
616				)
617				.into()),
618			}
619		}
620	}
621
622	#[cfg(any(test, feature = "try-runtime", feature = "std"))]
623	impl<T: Config<I>, I: 'static> Pallet<T, I> {
624		/// Ensure the correctness of the state of this pallet.
625		pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
626			use sp_std::collections::btree_set::BTreeSet;
627
628			let mut lanes = BTreeSet::new();
629
630			// check all known bridge configurations
631			for (bridge_id, bridge) in Bridges::<T, I>::iter() {
632				lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?);
633			}
634			ensure!(
635				lanes.len() == Bridges::<T, I>::iter().count(),
636				"Invalid `Bridges` configuration, probably two bridges handle the same laneId!"
637			);
638			ensure!(
639				lanes.len() == LaneToBridge::<T, I>::iter().count(),
640				"Invalid `LaneToBridge` configuration, probably missing or not removed laneId!"
641			);
642
643			// check connected `pallet_bridge_messages` state.
644			Self::do_try_state_for_messages()
645		}
646
647		/// Ensure the correctness of the state of the bridge.
648		pub fn do_try_state_for_bridge(
649			bridge_id: BridgeId,
650			bridge: BridgeOf<T, I>,
651		) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
652			tracing::info!(target: LOG_TARGET, ?bridge_id, ?bridge, "Checking `do_try_state_for_bridge`");
653
654			// check `BridgeId` points to the same `LaneId` and vice versa.
655			ensure!(
656				Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
657				"Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
658			);
659
660			// check `pallet_bridge_messages` state for that `LaneId`.
661			let lanes_manager = LanesManagerOf::<T, I>::new();
662			ensure!(
663				lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(),
664				"Inbound lane not found!",
665			);
666			ensure!(
667				lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(),
668				"Outbound lane not found!",
669			);
670
671			// check that `locations` are convertible to the `latest` XCM.
672			let bridge_origin_relative_location_as_latest: &Location = &(*bridge.bridge_origin_relative_location).try_into()
673				.map_err(|_| "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!")?;
674			let bridge_origin_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_origin_universal_location).try_into()
675				.map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
676			let bridge_destination_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_destination_universal_location).try_into()
677				.map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
678
679			// check `BridgeId` does not change
680			ensure!(
681				bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest),
682				"`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!"
683			);
684
685			// check bridge account owner
686			ensure!(
687				T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account),
688				"`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!"
689			);
690
691			Ok(bridge.lane_id)
692		}
693
694		/// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance.
695		pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
696			// check that all `InboundLanes` laneIds have mapping to some bridge.
697			for lane_id in pallet_bridge_messages::InboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
698				tracing::info!(target: LOG_TARGET, ?lane_id, "Checking `do_try_state_for_messages` for `InboundLanes`...");
699				ensure!(
700					LaneToBridge::<T, I>::get(lane_id).is_some(),
701					"Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"
702				);
703			}
704
705			// check that all `OutboundLanes` laneIds have mapping to some bridge.
706			for lane_id in pallet_bridge_messages::OutboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
707				tracing::info!(target: LOG_TARGET, ?lane_id, "Checking `do_try_state_for_messages` for `OutboundLanes`");
708				ensure!(
709					LaneToBridge::<T, I>::get(lane_id).is_some(),
710					"Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"
711				);
712			}
713
714			Ok(())
715		}
716	}
717
718	/// All registered bridges.
719	#[pallet::storage]
720	pub type Bridges<T: Config<I>, I: 'static = ()> =
721		StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
722	/// All registered `lane_id` and `bridge_id` mappings.
723	#[pallet::storage]
724	pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
725		StorageMap<_, Identity, T::LaneId, BridgeId>;
726
727	#[pallet::genesis_config]
728	#[derive(DefaultNoBound)]
729	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
730		/// Opened bridges.
731		///
732		/// Keep in mind that we are **NOT** reserving any amount for the bridges opened at
733		/// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using
734		/// other pallets genesis configuration or some other means.
735		pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
736		/// Dummy marker.
737		#[serde(skip)]
738		pub _phantom: sp_std::marker::PhantomData<(T, I)>,
739	}
740
741	#[pallet::genesis_build]
742	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I>
743	where
744		T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
745	{
746		fn build(&self) {
747			for (
748				bridge_origin_relative_location,
749				bridge_destination_universal_location,
750				maybe_lane_id,
751			) in &self.opened_bridges
752			{
753				let locations = Pallet::<T, I>::bridge_locations(
754					bridge_origin_relative_location.clone(),
755					bridge_destination_universal_location.clone().into(),
756				)
757				.expect("Invalid genesis configuration");
758
759				let lane_id = match maybe_lane_id {
760					Some(lane_id) => *lane_id,
761					None => {
762						locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations")
763					},
764				};
765
766				Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
767					.expect("Valid opened bridge!");
768			}
769		}
770	}
771
772	#[pallet::event]
773	#[pallet::generate_deposit(pub(super) fn deposit_event)]
774	pub enum Event<T: Config<I>, I: 'static = ()> {
775		/// The bridge between two locations has been opened.
776		BridgeOpened {
777			/// Bridge identifier.
778			bridge_id: BridgeId,
779			/// Amount of deposit held.
780			bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
781
782			/// Universal location of local bridge endpoint.
783			local_endpoint: Box<InteriorLocation>,
784			/// Universal location of remote bridge endpoint.
785			remote_endpoint: Box<InteriorLocation>,
786			/// Lane identifier.
787			lane_id: T::LaneId,
788		},
789		/// Bridge is going to be closed, but not yet fully pruned from the runtime storage.
790		ClosingBridge {
791			/// Bridge identifier.
792			bridge_id: BridgeId,
793			/// Lane identifier.
794			lane_id: T::LaneId,
795			/// Number of pruned messages during the close call.
796			pruned_messages: MessageNonce,
797			/// Number of enqueued messages that need to be pruned in follow up calls.
798			enqueued_messages: MessageNonce,
799		},
800		/// Bridge has been closed and pruned from the runtime storage. It now may be reopened
801		/// again by any participant.
802		BridgePruned {
803			/// Bridge identifier.
804			bridge_id: BridgeId,
805			/// Lane identifier.
806			lane_id: T::LaneId,
807			/// Amount of deposit released.
808			bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
809			/// Number of pruned messages during the close call.
810			pruned_messages: MessageNonce,
811		},
812	}
813
814	#[pallet::error]
815	pub enum Error<T, I = ()> {
816		/// Bridge locations error.
817		BridgeLocations(BridgeLocationsError),
818		/// Invalid local bridge origin account.
819		InvalidBridgeOriginAccount,
820		/// The bridge is already registered in this pallet.
821		BridgeAlreadyExists,
822		/// The local origin already owns a maximal number of bridges.
823		TooManyBridgesForLocalOrigin,
824		/// Trying to close already closed bridge.
825		BridgeAlreadyClosed,
826		/// Lanes manager error.
827		LanesManager(LanesManagerError),
828		/// Trying to access unknown bridge.
829		UnknownBridge,
830		/// The bridge origin can't pay the required amount for opening the bridge.
831		FailedToReserveBridgeDeposit,
832		/// The version of XCM location argument is unsupported.
833		UnsupportedXcmVersion,
834	}
835}
836
837#[cfg(test)]
838mod tests {
839	use super::*;
840	use bp_messages::LaneIdType;
841	use mock::*;
842
843	use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
844	use frame_system::{EventRecord, Phase};
845	use sp_runtime::TryRuntimeError;
846
847	fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
848		let bridge_owner_account =
849			LocationToAccountId::convert_location(locations.bridge_origin_relative_location())
850				.unwrap();
851		assert_ok!(Balances::mint_into(&bridge_owner_account, balance));
852		bridge_owner_account
853	}
854
855	fn mock_open_bridge_from_with(
856		origin: RuntimeOrigin,
857		deposit: Balance,
858		with: InteriorLocation,
859	) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
860		let locations =
861			XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
862		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
863		let bridge_owner_account =
864			fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
865		Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap();
866
867		let bridge = Bridge {
868			bridge_origin_relative_location: Box::new(
869				locations.bridge_origin_relative_location().clone().into(),
870			),
871			bridge_origin_universal_location: Box::new(
872				locations.bridge_origin_universal_location().clone().into(),
873			),
874			bridge_destination_universal_location: Box::new(
875				locations.bridge_destination_universal_location().clone().into(),
876			),
877			state: BridgeState::Opened,
878			bridge_owner_account,
879			deposit,
880			lane_id,
881		};
882		Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
883		LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
884
885		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
886		lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
887		lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
888
889		assert_ok!(XcmOverBridge::do_try_state());
890
891		(bridge, *locations)
892	}
893
894	fn mock_open_bridge_from(
895		origin: RuntimeOrigin,
896		deposit: Balance,
897	) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
898		mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
899	}
900
901	fn enqueue_message(lane: TestLaneIdType) {
902		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
903		lanes_manager
904			.active_outbound_lane(lane)
905			.unwrap()
906			.send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
907	}
908
909	#[test]
910	fn open_bridge_fails_if_origin_is_not_allowed() {
911		run_test(|| {
912			assert_noop!(
913				XcmOverBridge::open_bridge(
914					OpenBridgeOrigin::disallowed_origin(),
915					Box::new(bridged_asset_hub_universal_location().into()),
916				),
917				sp_runtime::DispatchError::BadOrigin,
918			);
919		})
920	}
921
922	#[test]
923	fn open_bridge_fails_if_origin_is_not_relative() {
924		run_test(|| {
925			assert_noop!(
926				XcmOverBridge::open_bridge(
927					OpenBridgeOrigin::parent_relay_chain_universal_origin(),
928					Box::new(bridged_asset_hub_universal_location().into()),
929				),
930				Error::<TestRuntime, ()>::BridgeLocations(
931					BridgeLocationsError::InvalidBridgeOrigin
932				),
933			);
934
935			assert_noop!(
936				XcmOverBridge::open_bridge(
937					OpenBridgeOrigin::sibling_parachain_universal_origin(),
938					Box::new(bridged_asset_hub_universal_location().into()),
939				),
940				Error::<TestRuntime, ()>::BridgeLocations(
941					BridgeLocationsError::InvalidBridgeOrigin
942				),
943			);
944		})
945	}
946
947	#[test]
948	fn open_bridge_fails_if_destination_is_not_remote() {
949		run_test(|| {
950			assert_noop!(
951				XcmOverBridge::open_bridge(
952					OpenBridgeOrigin::parent_relay_chain_origin(),
953					Box::new(
954						[GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)]
955							.into()
956					),
957				),
958				Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
959			);
960		});
961	}
962
963	#[test]
964	fn open_bridge_fails_if_outside_of_bridged_consensus() {
965		run_test(|| {
966			assert_noop!(
967				XcmOverBridge::open_bridge(
968					OpenBridgeOrigin::parent_relay_chain_origin(),
969					Box::new(
970						[
971							GlobalConsensus(NonBridgedRelayNetwork::get()),
972							Parachain(BRIDGED_ASSET_HUB_ID)
973						]
974						.into()
975					),
976				),
977				Error::<TestRuntime, ()>::BridgeLocations(
978					BridgeLocationsError::UnreachableDestination
979				),
980			);
981		});
982	}
983
984	#[test]
985	fn open_bridge_fails_if_origin_has_no_sovereign_account() {
986		run_test(|| {
987			assert_noop!(
988				XcmOverBridge::open_bridge(
989					OpenBridgeOrigin::origin_without_sovereign_account(),
990					Box::new(bridged_asset_hub_universal_location().into()),
991				),
992				Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
993			);
994		});
995	}
996
997	#[test]
998	fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
999		run_test(|| {
1000			assert_noop!(
1001				XcmOverBridge::open_bridge(
1002					OpenBridgeOrigin::sibling_parachain_origin(),
1003					Box::new(bridged_asset_hub_universal_location().into()),
1004				),
1005				Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
1006			);
1007		});
1008	}
1009
1010	#[test]
1011	fn open_bridge_fails_if_it_already_exists() {
1012		run_test(|| {
1013			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1014			let locations = XcmOverBridge::bridge_locations_from_origin(
1015				origin.clone(),
1016				Box::new(bridged_asset_hub_universal_location().into()),
1017			)
1018			.unwrap();
1019			let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1020			fund_origin_sovereign_account(
1021				&locations,
1022				BridgeDeposit::get() + ExistentialDeposit::get(),
1023			);
1024
1025			Bridges::<TestRuntime, ()>::insert(
1026				locations.bridge_id(),
1027				Bridge {
1028					bridge_origin_relative_location: Box::new(
1029						locations.bridge_origin_relative_location().clone().into(),
1030					),
1031					bridge_origin_universal_location: Box::new(
1032						locations.bridge_origin_universal_location().clone().into(),
1033					),
1034					bridge_destination_universal_location: Box::new(
1035						locations.bridge_destination_universal_location().clone().into(),
1036					),
1037					state: BridgeState::Opened,
1038					bridge_owner_account: [0u8; 32].into(),
1039					deposit: 0,
1040					lane_id,
1041				},
1042			);
1043
1044			assert_noop!(
1045				XcmOverBridge::open_bridge(
1046					origin,
1047					Box::new(bridged_asset_hub_universal_location().into()),
1048				),
1049				Error::<TestRuntime, ()>::BridgeAlreadyExists,
1050			);
1051		})
1052	}
1053
1054	#[test]
1055	fn open_bridge_fails_if_its_lanes_already_exists() {
1056		run_test(|| {
1057			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1058			let locations = XcmOverBridge::bridge_locations_from_origin(
1059				origin.clone(),
1060				Box::new(bridged_asset_hub_universal_location().into()),
1061			)
1062			.unwrap();
1063			let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1064			fund_origin_sovereign_account(
1065				&locations,
1066				BridgeDeposit::get() + ExistentialDeposit::get(),
1067			);
1068
1069			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1070
1071			lanes_manager.create_inbound_lane(lane_id).unwrap();
1072			assert_noop!(
1073				XcmOverBridge::open_bridge(
1074					origin.clone(),
1075					Box::new(bridged_asset_hub_universal_location().into()),
1076				),
1077				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
1078			);
1079
1080			lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
1081			lanes_manager.create_outbound_lane(lane_id).unwrap();
1082			assert_noop!(
1083				XcmOverBridge::open_bridge(
1084					origin,
1085					Box::new(bridged_asset_hub_universal_location().into()),
1086				),
1087				Error::<TestRuntime, ()>::LanesManager(
1088					LanesManagerError::OutboundLaneAlreadyExists
1089				),
1090			);
1091		})
1092	}
1093
1094	#[test]
1095	fn open_bridge_works() {
1096		run_test(|| {
1097			// in our test runtime, we expect that bridge may be opened by parent relay chain
1098			// and any sibling parachain
1099			let origins = [
1100				(OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1101				(OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1102			];
1103
1104			// check that every origin may open the bridge
1105			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1106			let existential_deposit = ExistentialDeposit::get();
1107			for (origin, expected_deposit) in origins {
1108				// reset events
1109				System::set_block_number(1);
1110				System::reset_events();
1111
1112				// compute all other locations
1113				let xcm_version = xcm::latest::VERSION;
1114				let locations = XcmOverBridge::bridge_locations_from_origin(
1115					origin.clone(),
1116					Box::new(
1117						VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
1118							.into_version(xcm_version)
1119							.expect("valid conversion"),
1120					),
1121				)
1122				.unwrap();
1123				let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
1124
1125				// ensure that there's no bridge and lanes in the storage
1126				assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
1127				assert_eq!(
1128					lanes_manager.active_inbound_lane(lane_id).map(drop),
1129					Err(LanesManagerError::UnknownInboundLane)
1130				);
1131				assert_eq!(
1132					lanes_manager.active_outbound_lane(lane_id).map(drop),
1133					Err(LanesManagerError::UnknownOutboundLane)
1134				);
1135				assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
1136
1137				// give enough funds to the sovereign account of the bridge origin
1138				let bridge_owner_account = fund_origin_sovereign_account(
1139					&locations,
1140					expected_deposit + existential_deposit,
1141				);
1142				assert_eq!(
1143					Balances::free_balance(&bridge_owner_account),
1144					expected_deposit + existential_deposit
1145				);
1146				assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
1147
1148				// now open the bridge
1149				assert_ok!(XcmOverBridge::open_bridge(
1150					origin,
1151					Box::new(locations.bridge_destination_universal_location().clone().into()),
1152				));
1153
1154				// ensure that everything has been set up in the runtime storage
1155				assert_eq!(
1156					Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
1157					Some(Bridge {
1158						bridge_origin_relative_location: Box::new(
1159							locations.bridge_origin_relative_location().clone().into()
1160						),
1161						bridge_origin_universal_location: Box::new(
1162							locations.bridge_origin_universal_location().clone().into(),
1163						),
1164						bridge_destination_universal_location: Box::new(
1165							locations.bridge_destination_universal_location().clone().into(),
1166						),
1167						state: BridgeState::Opened,
1168						bridge_owner_account: bridge_owner_account.clone(),
1169						deposit: expected_deposit,
1170						lane_id
1171					}),
1172				);
1173				assert_eq!(
1174					lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()),
1175					Ok(LaneState::Opened)
1176				);
1177				assert_eq!(
1178					lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()),
1179					Ok(LaneState::Opened)
1180				);
1181				assert_eq!(
1182					LaneToBridge::<TestRuntime, ()>::get(lane_id),
1183					Some(*locations.bridge_id())
1184				);
1185				assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit);
1186				assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit);
1187
1188				// ensure that the proper event is deposited
1189				assert_eq!(
1190					System::events().last(),
1191					Some(&EventRecord {
1192						phase: Phase::Initialization,
1193						event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
1194							bridge_id: *locations.bridge_id(),
1195							bridge_deposit: expected_deposit,
1196							local_endpoint: Box::new(
1197								locations.bridge_origin_universal_location().clone()
1198							),
1199							remote_endpoint: Box::new(
1200								locations.bridge_destination_universal_location().clone()
1201							),
1202							lane_id: lane_id.into()
1203						}),
1204						topics: vec![],
1205					}),
1206				);
1207
1208				// check state
1209				assert_ok!(XcmOverBridge::do_try_state());
1210			}
1211		});
1212	}
1213
1214	#[test]
1215	fn close_bridge_fails_if_origin_is_not_allowed() {
1216		run_test(|| {
1217			assert_noop!(
1218				XcmOverBridge::close_bridge(
1219					OpenBridgeOrigin::disallowed_origin(),
1220					Box::new(bridged_asset_hub_universal_location().into()),
1221					0,
1222				),
1223				sp_runtime::DispatchError::BadOrigin,
1224			);
1225		})
1226	}
1227
1228	#[test]
1229	fn close_bridge_fails_if_origin_is_not_relative() {
1230		run_test(|| {
1231			assert_noop!(
1232				XcmOverBridge::close_bridge(
1233					OpenBridgeOrigin::parent_relay_chain_universal_origin(),
1234					Box::new(bridged_asset_hub_universal_location().into()),
1235					0,
1236				),
1237				Error::<TestRuntime, ()>::BridgeLocations(
1238					BridgeLocationsError::InvalidBridgeOrigin
1239				),
1240			);
1241
1242			assert_noop!(
1243				XcmOverBridge::close_bridge(
1244					OpenBridgeOrigin::sibling_parachain_universal_origin(),
1245					Box::new(bridged_asset_hub_universal_location().into()),
1246					0,
1247				),
1248				Error::<TestRuntime, ()>::BridgeLocations(
1249					BridgeLocationsError::InvalidBridgeOrigin
1250				),
1251			);
1252		})
1253	}
1254
1255	#[test]
1256	fn close_bridge_fails_if_its_lanes_are_unknown() {
1257		run_test(|| {
1258			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1259			let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0);
1260
1261			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1262			lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge();
1263			assert_noop!(
1264				XcmOverBridge::close_bridge(
1265					origin.clone(),
1266					Box::new(locations.bridge_destination_universal_location().clone().into()),
1267					0,
1268				),
1269				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
1270			);
1271			lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1272
1273			let (_, locations) = mock_open_bridge_from(origin.clone(), 0);
1274			lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1275			assert_noop!(
1276				XcmOverBridge::close_bridge(
1277					origin,
1278					Box::new(locations.bridge_destination_universal_location().clone().into()),
1279					0,
1280				),
1281				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
1282			);
1283		});
1284	}
1285
1286	#[test]
1287	fn close_bridge_works() {
1288		run_test(|| {
1289			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1290			let expected_deposit = BridgeDeposit::get();
1291			let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit);
1292			System::set_block_number(1);
1293
1294			// remember owner balances
1295			let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1296			let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1297
1298			// enqueue some messages
1299			for _ in 0..32 {
1300				enqueue_message(bridge.lane_id);
1301			}
1302
1303			// now call the `close_bridge`, which will only partially prune messages
1304			assert_ok!(XcmOverBridge::close_bridge(
1305				origin.clone(),
1306				Box::new(locations.bridge_destination_universal_location().clone().into()),
1307				16,
1308			),);
1309
1310			// as a result, the bridge and lanes are switched to the `Closed` state, some messages
1311			// are pruned, but funds are not unreserved
1312			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1313			assert_eq!(
1314				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1315				Some(BridgeState::Closed)
1316			);
1317			assert_eq!(
1318				lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1319				LaneState::Closed
1320			);
1321			assert_eq!(
1322				lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1323				LaneState::Closed
1324			);
1325			assert_eq!(
1326				lanes_manager
1327					.any_state_outbound_lane(bridge.lane_id)
1328					.unwrap()
1329					.queued_messages()
1330					.checked_len(),
1331				Some(16)
1332			);
1333			assert_eq!(
1334				LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1335				Some(*locations.bridge_id())
1336			);
1337			assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1338			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1339			assert_eq!(
1340				System::events().last(),
1341				Some(&EventRecord {
1342					phase: Phase::Initialization,
1343					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1344						bridge_id: *locations.bridge_id(),
1345						lane_id: bridge.lane_id.into(),
1346						pruned_messages: 16,
1347						enqueued_messages: 16,
1348					}),
1349					topics: vec![],
1350				}),
1351			);
1352
1353			// now call the `close_bridge` again, which will only partially prune messages
1354			assert_ok!(XcmOverBridge::close_bridge(
1355				origin.clone(),
1356				Box::new(locations.bridge_destination_universal_location().clone().into()),
1357				8,
1358			),);
1359
1360			// nothing is changed (apart from the pruned messages)
1361			assert_eq!(
1362				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1363				Some(BridgeState::Closed)
1364			);
1365			assert_eq!(
1366				lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1367				LaneState::Closed
1368			);
1369			assert_eq!(
1370				lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1371				LaneState::Closed
1372			);
1373			assert_eq!(
1374				lanes_manager
1375					.any_state_outbound_lane(bridge.lane_id)
1376					.unwrap()
1377					.queued_messages()
1378					.checked_len(),
1379				Some(8)
1380			);
1381			assert_eq!(
1382				LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1383				Some(*locations.bridge_id())
1384			);
1385			assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1386			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1387			assert_eq!(
1388				System::events().last(),
1389				Some(&EventRecord {
1390					phase: Phase::Initialization,
1391					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1392						bridge_id: *locations.bridge_id(),
1393						lane_id: bridge.lane_id.into(),
1394						pruned_messages: 8,
1395						enqueued_messages: 8,
1396					}),
1397					topics: vec![],
1398				}),
1399			);
1400
1401			// now call the `close_bridge` again that will prune all remaining messages and the
1402			// bridge
1403			assert_ok!(XcmOverBridge::close_bridge(
1404				origin,
1405				Box::new(locations.bridge_destination_universal_location().clone().into()),
1406				9,
1407			),);
1408
1409			// there's no traces of bridge in the runtime storage and funds are unreserved
1410			assert_eq!(
1411				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1412				None
1413			);
1414			assert_eq!(
1415				lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop),
1416				Err(LanesManagerError::UnknownInboundLane)
1417			);
1418			assert_eq!(
1419				lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop),
1420				Err(LanesManagerError::UnknownOutboundLane)
1421			);
1422			assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
1423			assert_eq!(
1424				Balances::free_balance(&bridge.bridge_owner_account),
1425				free_balance + reserved_balance
1426			);
1427			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0);
1428			assert_eq!(
1429				System::events().last(),
1430				Some(&EventRecord {
1431					phase: Phase::Initialization,
1432					event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
1433						bridge_id: *locations.bridge_id(),
1434						lane_id: bridge.lane_id.into(),
1435						bridge_deposit: expected_deposit,
1436						pruned_messages: 8,
1437					}),
1438					topics: vec![],
1439				}),
1440			);
1441		});
1442	}
1443
1444	#[test]
1445	fn do_try_state_works() {
1446		let bridge_origin_relative_location = SiblingLocation::get();
1447		let bridge_origin_universal_location = SiblingUniversalLocation::get();
1448		let bridge_destination_universal_location = BridgedUniversalDestination::get();
1449		let bridge_owner_account =
1450			LocationToAccountId::convert_location(&bridge_origin_relative_location)
1451				.expect("valid accountId");
1452		let bridge_owner_account_mismatch =
1453			LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
1454		let bridge_id = BridgeId::new(
1455			&bridge_origin_universal_location,
1456			&bridge_destination_universal_location,
1457		);
1458		let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
1459		let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
1460		let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
1461
1462		let test_bridge_state =
1463			|id,
1464			 bridge,
1465			 (lane_id, bridge_id),
1466			 (inbound_lane_id, outbound_lane_id),
1467			 expected_error: Option<TryRuntimeError>| {
1468				Bridges::<TestRuntime, ()>::insert(id, bridge);
1469				LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
1470
1471				let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1472				lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
1473				lanes_manager.create_outbound_lane(outbound_lane_id).unwrap();
1474
1475				let result = XcmOverBridge::do_try_state();
1476				if let Some(e) = expected_error {
1477					assert_err!(result, e);
1478				} else {
1479					assert_ok!(result);
1480				}
1481			};
1482		let cleanup = |bridge_id, lane_ids| {
1483			Bridges::<TestRuntime, ()>::remove(bridge_id);
1484			for lane_id in lane_ids {
1485				LaneToBridge::<TestRuntime, ()>::remove(lane_id);
1486				let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1487				if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
1488					lane.purge();
1489				}
1490				if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
1491					lane.purge();
1492				}
1493			}
1494			assert_ok!(XcmOverBridge::do_try_state());
1495		};
1496
1497		run_test(|| {
1498			// ok state
1499			test_bridge_state(
1500				bridge_id,
1501				Bridge {
1502					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1503						bridge_origin_relative_location.clone(),
1504					)),
1505					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1506						bridge_origin_universal_location.clone(),
1507					)),
1508					bridge_destination_universal_location: Box::new(
1509						VersionedInteriorLocation::from(
1510							bridge_destination_universal_location.clone(),
1511						),
1512					),
1513					state: BridgeState::Opened,
1514					bridge_owner_account: bridge_owner_account.clone(),
1515					deposit: Zero::zero(),
1516					lane_id,
1517				},
1518				(lane_id, bridge_id),
1519				(lane_id, lane_id),
1520				None,
1521			);
1522			cleanup(bridge_id, vec![lane_id]);
1523
1524			// error - missing `LaneToBridge` mapping
1525			test_bridge_state(
1526				bridge_id,
1527				Bridge {
1528					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1529						bridge_origin_relative_location.clone(),
1530					)),
1531					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1532						bridge_origin_universal_location.clone(),
1533					)),
1534					bridge_destination_universal_location: Box::new(
1535						VersionedInteriorLocation::from(
1536							bridge_destination_universal_location.clone(),
1537						),
1538					),
1539					state: BridgeState::Opened,
1540					bridge_owner_account: bridge_owner_account.clone(),
1541					deposit: Zero::zero(),
1542					lane_id,
1543				},
1544				(lane_id, bridge_id_mismatch),
1545				(lane_id, lane_id),
1546				Some(TryRuntimeError::Other(
1547					"Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
1548				)),
1549			);
1550			cleanup(bridge_id, vec![lane_id]);
1551
1552			// error bridge owner account cannot be calculated
1553			test_bridge_state(
1554				bridge_id,
1555				Bridge {
1556					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1557						bridge_origin_relative_location.clone(),
1558					)),
1559					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1560						bridge_origin_universal_location.clone(),
1561					)),
1562					bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1563						bridge_destination_universal_location.clone(),
1564					)),
1565					state: BridgeState::Opened,
1566					bridge_owner_account: bridge_owner_account_mismatch.clone(),
1567					deposit: Zero::zero(),
1568					lane_id,
1569				},
1570				(lane_id, bridge_id),
1571				(lane_id, lane_id),
1572				Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
1573			);
1574			cleanup(bridge_id, vec![lane_id]);
1575
1576			// error when (bridge_origin_universal_location + bridge_destination_universal_location)
1577			// produces different `BridgeId`
1578			test_bridge_state(
1579				bridge_id_mismatch,
1580				Bridge {
1581					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1582						bridge_origin_relative_location.clone(),
1583					)),
1584					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1585						bridge_origin_universal_location.clone(),
1586					)),
1587					bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1588						bridge_destination_universal_location.clone(),
1589					)),
1590					state: BridgeState::Opened,
1591					bridge_owner_account: bridge_owner_account_mismatch.clone(),
1592					deposit: Zero::zero(),
1593					lane_id,
1594				},
1595				(lane_id, bridge_id_mismatch),
1596				(lane_id, lane_id),
1597				Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")),
1598			);
1599			cleanup(bridge_id_mismatch, vec![lane_id]);
1600
1601			// missing inbound lane for a bridge
1602			test_bridge_state(
1603				bridge_id,
1604				Bridge {
1605					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1606						bridge_origin_relative_location.clone(),
1607					)),
1608					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1609						bridge_origin_universal_location.clone(),
1610					)),
1611					bridge_destination_universal_location: Box::new(
1612						VersionedInteriorLocation::from(
1613							bridge_destination_universal_location.clone(),
1614						),
1615					),
1616					state: BridgeState::Opened,
1617					bridge_owner_account: bridge_owner_account.clone(),
1618					deposit: Zero::zero(),
1619					lane_id,
1620				},
1621				(lane_id, bridge_id),
1622				(lane_id_mismatch, lane_id),
1623				Some(TryRuntimeError::Other("Inbound lane not found!")),
1624			);
1625			cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1626
1627			// missing outbound lane for a bridge
1628			test_bridge_state(
1629				bridge_id,
1630				Bridge {
1631					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1632						bridge_origin_relative_location.clone(),
1633					)),
1634					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1635						bridge_origin_universal_location.clone(),
1636					)),
1637					bridge_destination_universal_location: Box::new(
1638						VersionedInteriorLocation::from(
1639							bridge_destination_universal_location.clone(),
1640						),
1641					),
1642					state: BridgeState::Opened,
1643					bridge_owner_account: bridge_owner_account.clone(),
1644					deposit: Zero::zero(),
1645					lane_id,
1646				},
1647				(lane_id, bridge_id),
1648				(lane_id, lane_id_mismatch),
1649				Some(TryRuntimeError::Other("Outbound lane not found!")),
1650			);
1651			cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1652
1653			// ok state with old XCM version
1654			test_bridge_state(
1655				bridge_id,
1656				Bridge {
1657					bridge_origin_relative_location: Box::new(
1658						VersionedLocation::from(bridge_origin_relative_location.clone())
1659							.into_version(XCM_VERSION - 1)
1660							.unwrap(),
1661					),
1662					bridge_origin_universal_location: Box::new(
1663						VersionedInteriorLocation::from(bridge_origin_universal_location.clone())
1664							.into_version(XCM_VERSION - 1)
1665							.unwrap(),
1666					),
1667					bridge_destination_universal_location: Box::new(
1668						VersionedInteriorLocation::from(
1669							bridge_destination_universal_location.clone(),
1670						)
1671						.into_version(XCM_VERSION - 1)
1672						.unwrap(),
1673					),
1674					state: BridgeState::Opened,
1675					bridge_owner_account: bridge_owner_account.clone(),
1676					deposit: Zero::zero(),
1677					lane_id,
1678				},
1679				(lane_id, bridge_id),
1680				(lane_id, lane_id),
1681				None,
1682			);
1683			cleanup(bridge_id, vec![lane_id]);
1684
1685			// missing bridge for inbound lane
1686			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1687			assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1688			assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1689			cleanup(bridge_id, vec![lane_id]);
1690
1691			// missing bridge for outbound lane
1692			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1693			assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1694			assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1695			cleanup(bridge_id, vec![lane_id]);
1696		});
1697	}
1698
1699	#[test]
1700	fn ensure_encoding_compatibility() {
1701		use codec::Encode;
1702
1703		let bridge_destination_universal_location = BridgedUniversalDestination::get();
1704		let may_prune_messages = 13;
1705
1706		assert_eq!(
1707			bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge {
1708				bridge_destination_universal_location: Box::new(
1709					bridge_destination_universal_location.clone().into()
1710				)
1711			}
1712			.encode(),
1713			Call::<TestRuntime, ()>::open_bridge {
1714				bridge_destination_universal_location: Box::new(
1715					bridge_destination_universal_location.clone().into()
1716				)
1717			}
1718			.encode()
1719		);
1720		assert_eq!(
1721			bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge {
1722				bridge_destination_universal_location: Box::new(
1723					bridge_destination_universal_location.clone().into()
1724				),
1725				may_prune_messages,
1726			}
1727			.encode(),
1728			Call::<TestRuntime, ()>::close_bridge {
1729				bridge_destination_universal_location: Box::new(
1730					bridge_destination_universal_location.clone().into()
1731				),
1732				may_prune_messages,
1733			}
1734			.encode()
1735		);
1736	}
1737}