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				Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
766					.expect("Valid opened bridge!");
767			}
768		}
769	}
770
771	#[pallet::event]
772	#[pallet::generate_deposit(pub(super) fn deposit_event)]
773	pub enum Event<T: Config<I>, I: 'static = ()> {
774		/// The bridge between two locations has been opened.
775		BridgeOpened {
776			/// Bridge identifier.
777			bridge_id: BridgeId,
778			/// Amount of deposit held.
779			bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
780
781			/// Universal location of local bridge endpoint.
782			local_endpoint: Box<InteriorLocation>,
783			/// Universal location of remote bridge endpoint.
784			remote_endpoint: Box<InteriorLocation>,
785			/// Lane identifier.
786			lane_id: T::LaneId,
787		},
788		/// Bridge is going to be closed, but not yet fully pruned from the runtime storage.
789		ClosingBridge {
790			/// Bridge identifier.
791			bridge_id: BridgeId,
792			/// Lane identifier.
793			lane_id: T::LaneId,
794			/// Number of pruned messages during the close call.
795			pruned_messages: MessageNonce,
796			/// Number of enqueued messages that need to be pruned in follow up calls.
797			enqueued_messages: MessageNonce,
798		},
799		/// Bridge has been closed and pruned from the runtime storage. It now may be reopened
800		/// again by any participant.
801		BridgePruned {
802			/// Bridge identifier.
803			bridge_id: BridgeId,
804			/// Lane identifier.
805			lane_id: T::LaneId,
806			/// Amount of deposit released.
807			bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
808			/// Number of pruned messages during the close call.
809			pruned_messages: MessageNonce,
810		},
811	}
812
813	#[pallet::error]
814	pub enum Error<T, I = ()> {
815		/// Bridge locations error.
816		BridgeLocations(BridgeLocationsError),
817		/// Invalid local bridge origin account.
818		InvalidBridgeOriginAccount,
819		/// The bridge is already registered in this pallet.
820		BridgeAlreadyExists,
821		/// The local origin already owns a maximal number of bridges.
822		TooManyBridgesForLocalOrigin,
823		/// Trying to close already closed bridge.
824		BridgeAlreadyClosed,
825		/// Lanes manager error.
826		LanesManager(LanesManagerError),
827		/// Trying to access unknown bridge.
828		UnknownBridge,
829		/// The bridge origin can't pay the required amount for opening the bridge.
830		FailedToReserveBridgeDeposit,
831		/// The version of XCM location argument is unsupported.
832		UnsupportedXcmVersion,
833	}
834}
835
836#[cfg(test)]
837mod tests {
838	use super::*;
839	use bp_messages::LaneIdType;
840	use mock::*;
841
842	use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
843	use frame_system::{EventRecord, Phase};
844	use sp_runtime::TryRuntimeError;
845
846	fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
847		let bridge_owner_account =
848			LocationToAccountId::convert_location(locations.bridge_origin_relative_location())
849				.unwrap();
850		assert_ok!(Balances::mint_into(&bridge_owner_account, balance));
851		bridge_owner_account
852	}
853
854	fn mock_open_bridge_from_with(
855		origin: RuntimeOrigin,
856		deposit: Balance,
857		with: InteriorLocation,
858	) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
859		let locations =
860			XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
861		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
862		let bridge_owner_account =
863			fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
864		Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap();
865
866		let bridge = Bridge {
867			bridge_origin_relative_location: Box::new(
868				locations.bridge_origin_relative_location().clone().into(),
869			),
870			bridge_origin_universal_location: Box::new(
871				locations.bridge_origin_universal_location().clone().into(),
872			),
873			bridge_destination_universal_location: Box::new(
874				locations.bridge_destination_universal_location().clone().into(),
875			),
876			state: BridgeState::Opened,
877			bridge_owner_account,
878			deposit,
879			lane_id,
880		};
881		Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
882		LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
883
884		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
885		lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
886		lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
887
888		assert_ok!(XcmOverBridge::do_try_state());
889
890		(bridge, *locations)
891	}
892
893	fn mock_open_bridge_from(
894		origin: RuntimeOrigin,
895		deposit: Balance,
896	) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
897		mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
898	}
899
900	fn enqueue_message(lane: TestLaneIdType) {
901		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
902		lanes_manager
903			.active_outbound_lane(lane)
904			.unwrap()
905			.send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
906	}
907
908	#[test]
909	fn open_bridge_fails_if_origin_is_not_allowed() {
910		run_test(|| {
911			assert_noop!(
912				XcmOverBridge::open_bridge(
913					OpenBridgeOrigin::disallowed_origin(),
914					Box::new(bridged_asset_hub_universal_location().into()),
915				),
916				sp_runtime::DispatchError::BadOrigin,
917			);
918		})
919	}
920
921	#[test]
922	fn open_bridge_fails_if_origin_is_not_relative() {
923		run_test(|| {
924			assert_noop!(
925				XcmOverBridge::open_bridge(
926					OpenBridgeOrigin::parent_relay_chain_universal_origin(),
927					Box::new(bridged_asset_hub_universal_location().into()),
928				),
929				Error::<TestRuntime, ()>::BridgeLocations(
930					BridgeLocationsError::InvalidBridgeOrigin
931				),
932			);
933
934			assert_noop!(
935				XcmOverBridge::open_bridge(
936					OpenBridgeOrigin::sibling_parachain_universal_origin(),
937					Box::new(bridged_asset_hub_universal_location().into()),
938				),
939				Error::<TestRuntime, ()>::BridgeLocations(
940					BridgeLocationsError::InvalidBridgeOrigin
941				),
942			);
943		})
944	}
945
946	#[test]
947	fn open_bridge_fails_if_destination_is_not_remote() {
948		run_test(|| {
949			assert_noop!(
950				XcmOverBridge::open_bridge(
951					OpenBridgeOrigin::parent_relay_chain_origin(),
952					Box::new(
953						[GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)]
954							.into()
955					),
956				),
957				Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
958			);
959		});
960	}
961
962	#[test]
963	fn open_bridge_fails_if_outside_of_bridged_consensus() {
964		run_test(|| {
965			assert_noop!(
966				XcmOverBridge::open_bridge(
967					OpenBridgeOrigin::parent_relay_chain_origin(),
968					Box::new(
969						[
970							GlobalConsensus(NonBridgedRelayNetwork::get()),
971							Parachain(BRIDGED_ASSET_HUB_ID)
972						]
973						.into()
974					),
975				),
976				Error::<TestRuntime, ()>::BridgeLocations(
977					BridgeLocationsError::UnreachableDestination
978				),
979			);
980		});
981	}
982
983	#[test]
984	fn open_bridge_fails_if_origin_has_no_sovereign_account() {
985		run_test(|| {
986			assert_noop!(
987				XcmOverBridge::open_bridge(
988					OpenBridgeOrigin::origin_without_sovereign_account(),
989					Box::new(bridged_asset_hub_universal_location().into()),
990				),
991				Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
992			);
993		});
994	}
995
996	#[test]
997	fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
998		run_test(|| {
999			assert_noop!(
1000				XcmOverBridge::open_bridge(
1001					OpenBridgeOrigin::sibling_parachain_origin(),
1002					Box::new(bridged_asset_hub_universal_location().into()),
1003				),
1004				Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
1005			);
1006		});
1007	}
1008
1009	#[test]
1010	fn open_bridge_fails_if_it_already_exists() {
1011		run_test(|| {
1012			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1013			let locations = XcmOverBridge::bridge_locations_from_origin(
1014				origin.clone(),
1015				Box::new(bridged_asset_hub_universal_location().into()),
1016			)
1017			.unwrap();
1018			let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1019			fund_origin_sovereign_account(
1020				&locations,
1021				BridgeDeposit::get() + ExistentialDeposit::get(),
1022			);
1023
1024			Bridges::<TestRuntime, ()>::insert(
1025				locations.bridge_id(),
1026				Bridge {
1027					bridge_origin_relative_location: Box::new(
1028						locations.bridge_origin_relative_location().clone().into(),
1029					),
1030					bridge_origin_universal_location: Box::new(
1031						locations.bridge_origin_universal_location().clone().into(),
1032					),
1033					bridge_destination_universal_location: Box::new(
1034						locations.bridge_destination_universal_location().clone().into(),
1035					),
1036					state: BridgeState::Opened,
1037					bridge_owner_account: [0u8; 32].into(),
1038					deposit: 0,
1039					lane_id,
1040				},
1041			);
1042
1043			assert_noop!(
1044				XcmOverBridge::open_bridge(
1045					origin,
1046					Box::new(bridged_asset_hub_universal_location().into()),
1047				),
1048				Error::<TestRuntime, ()>::BridgeAlreadyExists,
1049			);
1050		})
1051	}
1052
1053	#[test]
1054	fn open_bridge_fails_if_its_lanes_already_exists() {
1055		run_test(|| {
1056			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1057			let locations = XcmOverBridge::bridge_locations_from_origin(
1058				origin.clone(),
1059				Box::new(bridged_asset_hub_universal_location().into()),
1060			)
1061			.unwrap();
1062			let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1063			fund_origin_sovereign_account(
1064				&locations,
1065				BridgeDeposit::get() + ExistentialDeposit::get(),
1066			);
1067
1068			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1069
1070			lanes_manager.create_inbound_lane(lane_id).unwrap();
1071			assert_noop!(
1072				XcmOverBridge::open_bridge(
1073					origin.clone(),
1074					Box::new(bridged_asset_hub_universal_location().into()),
1075				),
1076				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
1077			);
1078
1079			lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
1080			lanes_manager.create_outbound_lane(lane_id).unwrap();
1081			assert_noop!(
1082				XcmOverBridge::open_bridge(
1083					origin,
1084					Box::new(bridged_asset_hub_universal_location().into()),
1085				),
1086				Error::<TestRuntime, ()>::LanesManager(
1087					LanesManagerError::OutboundLaneAlreadyExists
1088				),
1089			);
1090		})
1091	}
1092
1093	#[test]
1094	fn open_bridge_works() {
1095		run_test(|| {
1096			// in our test runtime, we expect that bridge may be opened by parent relay chain
1097			// and any sibling parachain
1098			let origins = [
1099				(OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1100				(OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1101			];
1102
1103			// check that every origin may open the bridge
1104			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1105			let existential_deposit = ExistentialDeposit::get();
1106			for (origin, expected_deposit) in origins {
1107				// reset events
1108				System::set_block_number(1);
1109				System::reset_events();
1110
1111				// compute all other locations
1112				let xcm_version = xcm::latest::VERSION;
1113				let locations = XcmOverBridge::bridge_locations_from_origin(
1114					origin.clone(),
1115					Box::new(
1116						VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
1117							.into_version(xcm_version)
1118							.expect("valid conversion"),
1119					),
1120				)
1121				.unwrap();
1122				let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
1123
1124				// ensure that there's no bridge and lanes in the storage
1125				assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
1126				assert_eq!(
1127					lanes_manager.active_inbound_lane(lane_id).map(drop),
1128					Err(LanesManagerError::UnknownInboundLane)
1129				);
1130				assert_eq!(
1131					lanes_manager.active_outbound_lane(lane_id).map(drop),
1132					Err(LanesManagerError::UnknownOutboundLane)
1133				);
1134				assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
1135
1136				// give enough funds to the sovereign account of the bridge origin
1137				let bridge_owner_account = fund_origin_sovereign_account(
1138					&locations,
1139					expected_deposit + existential_deposit,
1140				);
1141				assert_eq!(
1142					Balances::free_balance(&bridge_owner_account),
1143					expected_deposit + existential_deposit
1144				);
1145				assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
1146
1147				// now open the bridge
1148				assert_ok!(XcmOverBridge::open_bridge(
1149					origin,
1150					Box::new(locations.bridge_destination_universal_location().clone().into()),
1151				));
1152
1153				// ensure that everything has been set up in the runtime storage
1154				assert_eq!(
1155					Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
1156					Some(Bridge {
1157						bridge_origin_relative_location: Box::new(
1158							locations.bridge_origin_relative_location().clone().into()
1159						),
1160						bridge_origin_universal_location: Box::new(
1161							locations.bridge_origin_universal_location().clone().into(),
1162						),
1163						bridge_destination_universal_location: Box::new(
1164							locations.bridge_destination_universal_location().clone().into(),
1165						),
1166						state: BridgeState::Opened,
1167						bridge_owner_account: bridge_owner_account.clone(),
1168						deposit: expected_deposit,
1169						lane_id
1170					}),
1171				);
1172				assert_eq!(
1173					lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()),
1174					Ok(LaneState::Opened)
1175				);
1176				assert_eq!(
1177					lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()),
1178					Ok(LaneState::Opened)
1179				);
1180				assert_eq!(
1181					LaneToBridge::<TestRuntime, ()>::get(lane_id),
1182					Some(*locations.bridge_id())
1183				);
1184				assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit);
1185				assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit);
1186
1187				// ensure that the proper event is deposited
1188				assert_eq!(
1189					System::events().last(),
1190					Some(&EventRecord {
1191						phase: Phase::Initialization,
1192						event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
1193							bridge_id: *locations.bridge_id(),
1194							bridge_deposit: expected_deposit,
1195							local_endpoint: Box::new(
1196								locations.bridge_origin_universal_location().clone()
1197							),
1198							remote_endpoint: Box::new(
1199								locations.bridge_destination_universal_location().clone()
1200							),
1201							lane_id: lane_id.into()
1202						}),
1203						topics: vec![],
1204					}),
1205				);
1206
1207				// check state
1208				assert_ok!(XcmOverBridge::do_try_state());
1209			}
1210		});
1211	}
1212
1213	#[test]
1214	fn close_bridge_fails_if_origin_is_not_allowed() {
1215		run_test(|| {
1216			assert_noop!(
1217				XcmOverBridge::close_bridge(
1218					OpenBridgeOrigin::disallowed_origin(),
1219					Box::new(bridged_asset_hub_universal_location().into()),
1220					0,
1221				),
1222				sp_runtime::DispatchError::BadOrigin,
1223			);
1224		})
1225	}
1226
1227	#[test]
1228	fn close_bridge_fails_if_origin_is_not_relative() {
1229		run_test(|| {
1230			assert_noop!(
1231				XcmOverBridge::close_bridge(
1232					OpenBridgeOrigin::parent_relay_chain_universal_origin(),
1233					Box::new(bridged_asset_hub_universal_location().into()),
1234					0,
1235				),
1236				Error::<TestRuntime, ()>::BridgeLocations(
1237					BridgeLocationsError::InvalidBridgeOrigin
1238				),
1239			);
1240
1241			assert_noop!(
1242				XcmOverBridge::close_bridge(
1243					OpenBridgeOrigin::sibling_parachain_universal_origin(),
1244					Box::new(bridged_asset_hub_universal_location().into()),
1245					0,
1246				),
1247				Error::<TestRuntime, ()>::BridgeLocations(
1248					BridgeLocationsError::InvalidBridgeOrigin
1249				),
1250			);
1251		})
1252	}
1253
1254	#[test]
1255	fn close_bridge_fails_if_its_lanes_are_unknown() {
1256		run_test(|| {
1257			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1258			let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0);
1259
1260			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1261			lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge();
1262			assert_noop!(
1263				XcmOverBridge::close_bridge(
1264					origin.clone(),
1265					Box::new(locations.bridge_destination_universal_location().clone().into()),
1266					0,
1267				),
1268				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
1269			);
1270			lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1271
1272			let (_, locations) = mock_open_bridge_from(origin.clone(), 0);
1273			lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1274			assert_noop!(
1275				XcmOverBridge::close_bridge(
1276					origin,
1277					Box::new(locations.bridge_destination_universal_location().clone().into()),
1278					0,
1279				),
1280				Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
1281			);
1282		});
1283	}
1284
1285	#[test]
1286	fn close_bridge_works() {
1287		run_test(|| {
1288			let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1289			let expected_deposit = BridgeDeposit::get();
1290			let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit);
1291			System::set_block_number(1);
1292
1293			// remember owner balances
1294			let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1295			let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1296
1297			// enqueue some messages
1298			for _ in 0..32 {
1299				enqueue_message(bridge.lane_id);
1300			}
1301
1302			// now call the `close_bridge`, which will only partially prune messages
1303			assert_ok!(XcmOverBridge::close_bridge(
1304				origin.clone(),
1305				Box::new(locations.bridge_destination_universal_location().clone().into()),
1306				16,
1307			),);
1308
1309			// as a result, the bridge and lanes are switched to the `Closed` state, some messages
1310			// are pruned, but funds are not unreserved
1311			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1312			assert_eq!(
1313				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1314				Some(BridgeState::Closed)
1315			);
1316			assert_eq!(
1317				lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1318				LaneState::Closed
1319			);
1320			assert_eq!(
1321				lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1322				LaneState::Closed
1323			);
1324			assert_eq!(
1325				lanes_manager
1326					.any_state_outbound_lane(bridge.lane_id)
1327					.unwrap()
1328					.queued_messages()
1329					.checked_len(),
1330				Some(16)
1331			);
1332			assert_eq!(
1333				LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1334				Some(*locations.bridge_id())
1335			);
1336			assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1337			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1338			assert_eq!(
1339				System::events().last(),
1340				Some(&EventRecord {
1341					phase: Phase::Initialization,
1342					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1343						bridge_id: *locations.bridge_id(),
1344						lane_id: bridge.lane_id.into(),
1345						pruned_messages: 16,
1346						enqueued_messages: 16,
1347					}),
1348					topics: vec![],
1349				}),
1350			);
1351
1352			// now call the `close_bridge` again, which will only partially prune messages
1353			assert_ok!(XcmOverBridge::close_bridge(
1354				origin.clone(),
1355				Box::new(locations.bridge_destination_universal_location().clone().into()),
1356				8,
1357			),);
1358
1359			// nothing is changed (apart from the pruned messages)
1360			assert_eq!(
1361				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1362				Some(BridgeState::Closed)
1363			);
1364			assert_eq!(
1365				lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1366				LaneState::Closed
1367			);
1368			assert_eq!(
1369				lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1370				LaneState::Closed
1371			);
1372			assert_eq!(
1373				lanes_manager
1374					.any_state_outbound_lane(bridge.lane_id)
1375					.unwrap()
1376					.queued_messages()
1377					.checked_len(),
1378				Some(8)
1379			);
1380			assert_eq!(
1381				LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1382				Some(*locations.bridge_id())
1383			);
1384			assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1385			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1386			assert_eq!(
1387				System::events().last(),
1388				Some(&EventRecord {
1389					phase: Phase::Initialization,
1390					event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1391						bridge_id: *locations.bridge_id(),
1392						lane_id: bridge.lane_id.into(),
1393						pruned_messages: 8,
1394						enqueued_messages: 8,
1395					}),
1396					topics: vec![],
1397				}),
1398			);
1399
1400			// now call the `close_bridge` again that will prune all remaining messages and the
1401			// bridge
1402			assert_ok!(XcmOverBridge::close_bridge(
1403				origin,
1404				Box::new(locations.bridge_destination_universal_location().clone().into()),
1405				9,
1406			),);
1407
1408			// there's no traces of bridge in the runtime storage and funds are unreserved
1409			assert_eq!(
1410				Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1411				None
1412			);
1413			assert_eq!(
1414				lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop),
1415				Err(LanesManagerError::UnknownInboundLane)
1416			);
1417			assert_eq!(
1418				lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop),
1419				Err(LanesManagerError::UnknownOutboundLane)
1420			);
1421			assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
1422			assert_eq!(
1423				Balances::free_balance(&bridge.bridge_owner_account),
1424				free_balance + reserved_balance
1425			);
1426			assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0);
1427			assert_eq!(
1428				System::events().last(),
1429				Some(&EventRecord {
1430					phase: Phase::Initialization,
1431					event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
1432						bridge_id: *locations.bridge_id(),
1433						lane_id: bridge.lane_id.into(),
1434						bridge_deposit: expected_deposit,
1435						pruned_messages: 8,
1436					}),
1437					topics: vec![],
1438				}),
1439			);
1440		});
1441	}
1442
1443	#[test]
1444	fn do_try_state_works() {
1445		let bridge_origin_relative_location = SiblingLocation::get();
1446		let bridge_origin_universal_location = SiblingUniversalLocation::get();
1447		let bridge_destination_universal_location = BridgedUniversalDestination::get();
1448		let bridge_owner_account =
1449			LocationToAccountId::convert_location(&bridge_origin_relative_location)
1450				.expect("valid accountId");
1451		let bridge_owner_account_mismatch =
1452			LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
1453		let bridge_id = BridgeId::new(
1454			&bridge_origin_universal_location,
1455			&bridge_destination_universal_location,
1456		);
1457		let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
1458		let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
1459		let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
1460
1461		let test_bridge_state =
1462			|id,
1463			 bridge,
1464			 (lane_id, bridge_id),
1465			 (inbound_lane_id, outbound_lane_id),
1466			 expected_error: Option<TryRuntimeError>| {
1467				Bridges::<TestRuntime, ()>::insert(id, bridge);
1468				LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
1469
1470				let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1471				lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
1472				lanes_manager.create_outbound_lane(outbound_lane_id).unwrap();
1473
1474				let result = XcmOverBridge::do_try_state();
1475				if let Some(e) = expected_error {
1476					assert_err!(result, e);
1477				} else {
1478					assert_ok!(result);
1479				}
1480			};
1481		let cleanup = |bridge_id, lane_ids| {
1482			Bridges::<TestRuntime, ()>::remove(bridge_id);
1483			for lane_id in lane_ids {
1484				LaneToBridge::<TestRuntime, ()>::remove(lane_id);
1485				let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1486				if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
1487					lane.purge();
1488				}
1489				if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
1490					lane.purge();
1491				}
1492			}
1493			assert_ok!(XcmOverBridge::do_try_state());
1494		};
1495
1496		run_test(|| {
1497			// ok state
1498			test_bridge_state(
1499				bridge_id,
1500				Bridge {
1501					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1502						bridge_origin_relative_location.clone(),
1503					)),
1504					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1505						bridge_origin_universal_location.clone(),
1506					)),
1507					bridge_destination_universal_location: Box::new(
1508						VersionedInteriorLocation::from(
1509							bridge_destination_universal_location.clone(),
1510						),
1511					),
1512					state: BridgeState::Opened,
1513					bridge_owner_account: bridge_owner_account.clone(),
1514					deposit: Zero::zero(),
1515					lane_id,
1516				},
1517				(lane_id, bridge_id),
1518				(lane_id, lane_id),
1519				None,
1520			);
1521			cleanup(bridge_id, vec![lane_id]);
1522
1523			// error - missing `LaneToBridge` mapping
1524			test_bridge_state(
1525				bridge_id,
1526				Bridge {
1527					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1528						bridge_origin_relative_location.clone(),
1529					)),
1530					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1531						bridge_origin_universal_location.clone(),
1532					)),
1533					bridge_destination_universal_location: Box::new(
1534						VersionedInteriorLocation::from(
1535							bridge_destination_universal_location.clone(),
1536						),
1537					),
1538					state: BridgeState::Opened,
1539					bridge_owner_account: bridge_owner_account.clone(),
1540					deposit: Zero::zero(),
1541					lane_id,
1542				},
1543				(lane_id, bridge_id_mismatch),
1544				(lane_id, lane_id),
1545				Some(TryRuntimeError::Other(
1546					"Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
1547				)),
1548			);
1549			cleanup(bridge_id, vec![lane_id]);
1550
1551			// error bridge owner account cannot be calculated
1552			test_bridge_state(
1553				bridge_id,
1554				Bridge {
1555					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1556						bridge_origin_relative_location.clone(),
1557					)),
1558					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1559						bridge_origin_universal_location.clone(),
1560					)),
1561					bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1562						bridge_destination_universal_location.clone(),
1563					)),
1564					state: BridgeState::Opened,
1565					bridge_owner_account: bridge_owner_account_mismatch.clone(),
1566					deposit: Zero::zero(),
1567					lane_id,
1568				},
1569				(lane_id, bridge_id),
1570				(lane_id, lane_id),
1571				Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
1572			);
1573			cleanup(bridge_id, vec![lane_id]);
1574
1575			// error when (bridge_origin_universal_location + bridge_destination_universal_location)
1576			// produces different `BridgeId`
1577			test_bridge_state(
1578				bridge_id_mismatch,
1579				Bridge {
1580					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1581						bridge_origin_relative_location.clone(),
1582					)),
1583					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1584						bridge_origin_universal_location.clone(),
1585					)),
1586					bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1587						bridge_destination_universal_location.clone(),
1588					)),
1589					state: BridgeState::Opened,
1590					bridge_owner_account: bridge_owner_account_mismatch.clone(),
1591					deposit: Zero::zero(),
1592					lane_id,
1593				},
1594				(lane_id, bridge_id_mismatch),
1595				(lane_id, lane_id),
1596				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!")),
1597			);
1598			cleanup(bridge_id_mismatch, vec![lane_id]);
1599
1600			// missing inbound lane for a bridge
1601			test_bridge_state(
1602				bridge_id,
1603				Bridge {
1604					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1605						bridge_origin_relative_location.clone(),
1606					)),
1607					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1608						bridge_origin_universal_location.clone(),
1609					)),
1610					bridge_destination_universal_location: Box::new(
1611						VersionedInteriorLocation::from(
1612							bridge_destination_universal_location.clone(),
1613						),
1614					),
1615					state: BridgeState::Opened,
1616					bridge_owner_account: bridge_owner_account.clone(),
1617					deposit: Zero::zero(),
1618					lane_id,
1619				},
1620				(lane_id, bridge_id),
1621				(lane_id_mismatch, lane_id),
1622				Some(TryRuntimeError::Other("Inbound lane not found!")),
1623			);
1624			cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1625
1626			// missing outbound lane for a bridge
1627			test_bridge_state(
1628				bridge_id,
1629				Bridge {
1630					bridge_origin_relative_location: Box::new(VersionedLocation::from(
1631						bridge_origin_relative_location.clone(),
1632					)),
1633					bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1634						bridge_origin_universal_location.clone(),
1635					)),
1636					bridge_destination_universal_location: Box::new(
1637						VersionedInteriorLocation::from(
1638							bridge_destination_universal_location.clone(),
1639						),
1640					),
1641					state: BridgeState::Opened,
1642					bridge_owner_account: bridge_owner_account.clone(),
1643					deposit: Zero::zero(),
1644					lane_id,
1645				},
1646				(lane_id, bridge_id),
1647				(lane_id, lane_id_mismatch),
1648				Some(TryRuntimeError::Other("Outbound lane not found!")),
1649			);
1650			cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1651
1652			// ok state with old XCM version
1653			test_bridge_state(
1654				bridge_id,
1655				Bridge {
1656					bridge_origin_relative_location: Box::new(
1657						VersionedLocation::from(bridge_origin_relative_location.clone())
1658							.into_version(XCM_VERSION - 1)
1659							.unwrap(),
1660					),
1661					bridge_origin_universal_location: Box::new(
1662						VersionedInteriorLocation::from(bridge_origin_universal_location.clone())
1663							.into_version(XCM_VERSION - 1)
1664							.unwrap(),
1665					),
1666					bridge_destination_universal_location: Box::new(
1667						VersionedInteriorLocation::from(
1668							bridge_destination_universal_location.clone(),
1669						)
1670						.into_version(XCM_VERSION - 1)
1671						.unwrap(),
1672					),
1673					state: BridgeState::Opened,
1674					bridge_owner_account: bridge_owner_account.clone(),
1675					deposit: Zero::zero(),
1676					lane_id,
1677				},
1678				(lane_id, bridge_id),
1679				(lane_id, lane_id),
1680				None,
1681			);
1682			cleanup(bridge_id, vec![lane_id]);
1683
1684			// missing bridge for inbound lane
1685			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1686			assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1687			assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1688			cleanup(bridge_id, vec![lane_id]);
1689
1690			// missing bridge for outbound lane
1691			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1692			assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1693			assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1694			cleanup(bridge_id, vec![lane_id]);
1695		});
1696	}
1697
1698	#[test]
1699	fn ensure_encoding_compatibility() {
1700		use codec::Encode;
1701
1702		let bridge_destination_universal_location = BridgedUniversalDestination::get();
1703		let may_prune_messages = 13;
1704
1705		assert_eq!(
1706			bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge {
1707				bridge_destination_universal_location: Box::new(
1708					bridge_destination_universal_location.clone().into()
1709				)
1710			}
1711			.encode(),
1712			Call::<TestRuntime, ()>::open_bridge {
1713				bridge_destination_universal_location: Box::new(
1714					bridge_destination_universal_location.clone().into()
1715				)
1716			}
1717			.encode()
1718		);
1719		assert_eq!(
1720			bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge {
1721				bridge_destination_universal_location: Box::new(
1722					bridge_destination_universal_location.clone().into()
1723				),
1724				may_prune_messages,
1725			}
1726			.encode(),
1727			Call::<TestRuntime, ()>::close_bridge {
1728				bridge_destination_universal_location: Box::new(
1729					bridge_destination_universal_location.clone().into()
1730				),
1731				may_prune_messages,
1732			}
1733			.encode()
1734		);
1735	}
1736}