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::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

            
146
use bp_messages::{LaneState, MessageNonce};
147
use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt};
148
pub use bp_xcm_bridge::{Bridge, BridgeId, BridgeLocations, BridgeState, Deposit, Receiver};
149
use bp_xcm_bridge::{
150
	BridgeLocationsError, ChannelStatusProvider as DispatchChannelStatusProvider, DepositOf,
151
	LocalXcmChannelManager,
152
};
153
use frame_support::{traits::fungible::MutateHold, DefaultNoBound};
154
use frame_system::Config as SystemConfig;
155
use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError};
156
use sp_std::{boxed::Box, vec::Vec};
157
use xcm::prelude::*;
158
use xcm_builder::DispatchBlob;
159
use xcm_executor::traits::ConvertLocation;
160

            
161
pub use bp_xcm_bridge::XcmAsPlainPayload;
162
pub use dispatcher::XcmBlobMessageDispatchResult;
163
pub use exporter::PalletAsHaulBlobExporter;
164
pub use pallet::*;
165

            
166
pub mod benchmarking;
167
pub mod congestion;
168
mod dispatcher;
169
mod exporter;
170
pub mod migration;
171
mod mock;
172
#[cfg(test)]
173
mod tests;
174
pub use weights::WeightInfo;
175
pub mod weights;
176

            
177
/// The target that will be used when publishing logs related to this pallet.
178
pub const LOG_TARGET: &str = "runtime::bridge-xcm";
179

            
180
25
#[frame_support::pallet]
181
pub mod pallet {
182
	use super::*;
183
	use frame_support::{
184
		pallet_prelude::*,
185
		traits::{tokens::Precision, Contains},
186
	};
187
	use frame_system::pallet_prelude::{BlockNumberFor, *};
188

            
189
	/// The reason for this pallet placing a hold on funds.
190
	#[pallet::composite_enum]
191
	pub enum HoldReason<I: 'static = ()> {
192
2
		/// The funds are held as a deposit for opened bridge.
193
		#[codec(index = 0)]
194
		BridgeDeposit,
195
	}
196

            
197
	#[pallet::config]
198
	#[pallet::disable_frame_system_supertrait_check]
199
	pub trait Config<I: 'static = ()>:
200
		BridgeMessagesConfig<Self::BridgeMessagesPalletInstance>
201
	{
202
		/// The overarching event type.
203
		type RuntimeEvent: From<Event<Self, I>>
204
			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
205
		/// Benchmarks results from runtime we're plugged into.
206
		type WeightInfo: WeightInfo;
207

            
208
		/// Runtime's universal location.
209
		type UniversalLocation: Get<InteriorLocation>;
210
		// TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and
211
		// replace it with the `NetworkId` - then we'll be able to use
212
		// `T as pallet_bridge_messages::Config<T::BridgeMessagesPalletInstance>::BridgedChain::NetworkId`
213
		/// Bridged network as relative location of bridged `GlobalConsensus`.
214
		#[pallet::constant]
215
		type BridgedNetwork: Get<Location>;
216
		/// Associated messages pallet instance that bridges us with the
217
		/// `BridgedNetworkId` consensus.
218
		type BridgeMessagesPalletInstance: 'static;
219

            
220
		/// Price of single message export to the bridged consensus (`Self::BridgedNetwork`).
221
		type MessageExportPrice: Get<Assets>;
222
		/// Checks the XCM version for the destination.
223
		type DestinationVersion: GetVersion;
224

            
225
		/// The origin that is allowed to call privileged operations on the pallet, e.g. open/close
226
		/// bridge for locations.
227
		type ForceOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
228
		/// A set of XCM locations within local consensus system that are allowed to open
229
		/// bridges with remote destinations.
230
		type OpenBridgeOrigin: EnsureOrigin<
231
			<Self as SystemConfig>::RuntimeOrigin,
232
			Success = Location,
233
		>;
234
		/// A converter between a location and a sovereign account.
235
		type BridgeOriginAccountIdConverter: ConvertLocation<AccountIdOf<ThisChainOf<Self, I>>>;
236

            
237
		/// Amount of this chain native tokens that is reserved on the sibling parachain account
238
		/// when bridge open request is registered.
239
		#[pallet::constant]
240
		type BridgeDeposit: Get<BalanceOf<ThisChainOf<Self, I>>>;
241
		/// Currency used to pay for bridge registration.
242
		type Currency: MutateHold<
243
			AccountIdOf<ThisChainOf<Self, I>>,
244
			Balance = BalanceOf<ThisChainOf<Self, I>>,
245
			Reason = Self::RuntimeHoldReason,
246
		>;
247
		/// The overarching runtime hold reason.
248
		type RuntimeHoldReason: From<HoldReason<I>>;
249
		/// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`.
250
		/// For example, it is possible to make an exception for a system parachain or relay.
251
		type AllowWithoutBridgeDeposit: Contains<Location>;
252

            
253
		/// Local XCM channel manager. Dedicated to exporting capabilities when handling congestion
254
		/// with the sending side.
255
		type LocalXcmChannelManager: LocalXcmChannelManager<BridgeId>;
256
		/// XCM-level dispatcher for inbound bridge messages.
257
		type BlobDispatcher: DispatchBlob + DispatchChannelStatusProvider;
258
		/// Limits for handling congestion.
259
		type CongestionLimits: Get<congestion::CongestionLimits>;
260
	}
261

            
262
	/// An alias for the bridge metadata.
263
	pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>, LaneIdOf<T, I>>;
264
	/// An alias for this chain.
265
	pub type ThisChainOf<T, I> =
266
		pallet_bridge_messages::ThisChainOf<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
267
	/// An alias for lane identifier type.
268
	pub type LaneIdOf<T, I> =
269
		<T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::LaneId;
270
	/// An alias for the associated lanes manager.
271
	pub type LanesManagerOf<T, I> =
272
		pallet_bridge_messages::LanesManager<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
273
	/// Weight info of the given pallet.
274
	pub type WeightInfoOf<T, I> = <T as Config<I>>::WeightInfo;
275

            
276
2
	#[pallet::pallet]
277
	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
278

            
279
1
	#[pallet::hooks]
280
	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
281
1
		fn integrity_test() {
282
1
			assert!(
283
1
				Self::bridged_network_id().is_ok(),
284
				"Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`!",
285
				T::BridgedNetwork::get()
286
			);
287
1
			assert!(
288
1
				T::CongestionLimits::get().is_valid(),
289
				"Configured `T::CongestionLimits`: {:?} are not valid!",
290
				T::CongestionLimits::get()
291
			);
292
1
		}
293

            
294
		#[cfg(feature = "try-runtime")]
295
		fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
296
			Self::do_try_state()
297
		}
298
	}
299

            
300
	#[pallet::call]
301
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
302
		/// Open a bridge between two locations.
303
		///
304
		/// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling
305
		/// parachain or a parent relay chain). The `bridge_destination_universal_location` must be
306
		/// a destination within the consensus of the `T::BridgedNetwork` network.
307
		///
308
		/// The `BridgeDeposit` amount is reserved on the caller account. This deposit
309
		/// is unreserved after bridge is closed.
310
		///
311
		/// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane
312
		/// is `Opened`.
313
		///
314
		/// Optional `maybe_notify` holds data about the `bridge_origin_relative_location` where
315
		/// notifications can be sent to handle congestion. The notification contains an encoded
316
		/// tuple `(BridgeId, bool)`, where `bool` is the `is_congested` flag.
317
		#[pallet::call_index(0)]
318
		#[pallet::weight(WeightInfoOf::<T, I>::open_bridge())]
319
		pub fn open_bridge(
320
			origin: OriginFor<T>,
321
			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
322
			maybe_notify: Option<Receiver>,
323
14
		) -> DispatchResult {
324
14
			// check and compute required bridge locations and laneId
325
14
			let xcm_version = bridge_destination_universal_location.identify_version();
326
9
			let locations =
327
14
				Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
328
9
			let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| {
329
				log::trace!(
330
					target: LOG_TARGET,
331
					"calculate_lane_id error: {e:?}",
332
				);
333
				Error::<T, I>::BridgeLocations(e)
334
9
			})?;
335

            
336
9
			Self::do_open_bridge(locations, lane_id, true, maybe_notify)
337
		}
338

            
339
		/// Try to close the bridge.
340
		///
341
		/// Can only be called by the "owner" of this side of the bridge, meaning that the
342
		/// inbound XCM channel with the local origin chain is working.
343
		///
344
		/// Closed bridge is a bridge without any traces in the runtime storage. So this method
345
		/// first tries to prune all queued messages at the outbound lane. When there are no
346
		/// outbound messages left, outbound and inbound lanes are purged. After that, funds
347
		/// are returned back to the owner of this side of the bridge.
348
		///
349
		/// The number of messages that we may prune in a single call is limited by the
350
		/// `may_prune_messages` argument. If there are more messages in the queue, the method
351
		/// prunes exactly `may_prune_messages` and exits early. The caller may call it again
352
		/// until outbound queue is depleted and get his funds back.
353
		///
354
		/// The states after this call: everything is either `Closed`, or purged from the
355
		/// runtime storage.
356
		#[pallet::call_index(1)]
357
		#[pallet::weight(WeightInfoOf::<T, I>::close_bridge())]
358
		pub fn close_bridge(
359
			origin: OriginFor<T>,
360
			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
361
			may_prune_messages: MessageNonce,
362
8
		) -> DispatchResult {
363
			// compute required bridge locations
364
5
			let locations =
365
8
				Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
366

            
367
			// TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if
368
			// bridge/lanes are already closed + for messages that are not pruned
369

            
370
			// update bridge metadata - this also guarantees that the bridge is in the proper state
371
5
			let bridge =
372
5
				Bridges::<T, I>::try_mutate_exists(locations.bridge_id(), |bridge| match bridge {
373
5
					Some(bridge) => {
374
5
						bridge.state = BridgeState::Closed;
375
5
						Ok(bridge.clone())
376
					}
377
					None => Err(Error::<T, I>::UnknownBridge),
378
5
				})?;
379

            
380
			// close inbound and outbound lanes
381
5
			let lanes_manager = LanesManagerOf::<T, I>::new();
382
5
			let mut inbound_lane = lanes_manager
383
5
				.any_state_inbound_lane(bridge.lane_id)
384
5
				.map_err(Error::<T, I>::LanesManager)?;
385
4
			let mut outbound_lane = lanes_manager
386
4
				.any_state_outbound_lane(bridge.lane_id)
387
4
				.map_err(Error::<T, I>::LanesManager)?;
388

            
389
			// now prune queued messages
390
3
			let mut pruned_messages = 0;
391
3
			for _ in outbound_lane.queued_messages() {
392
34
				if pruned_messages == may_prune_messages {
393
2
					break;
394
32
				}
395
32

            
396
32
				outbound_lane.remove_oldest_unpruned_message();
397
32
				pruned_messages += 1;
398
			}
399

            
400
			// if there are outbound messages in the queue, just update states and early exit
401
3
			if !outbound_lane.queued_messages().is_empty() {
402
				// update lanes state. Under normal circumstances, following calls shall never fail
403
2
				inbound_lane.set_state(LaneState::Closed);
404
2
				outbound_lane.set_state(LaneState::Closed);
405
2

            
406
2
				// write something to log
407
2
				let enqueued_messages = outbound_lane.queued_messages().saturating_len();
408
2
				log::trace!(
409
					target: LOG_TARGET,
410
					"Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining",
411
					locations.bridge_id(),
412
					locations.bridge_origin_universal_location(),
413
					locations.bridge_destination_universal_location(),
414
					bridge.lane_id,
415
					enqueued_messages,
416
				);
417

            
418
				// deposit the `ClosingBridge` event
419
2
				Self::deposit_event(Event::<T, I>::ClosingBridge {
420
2
					bridge_id: *locations.bridge_id(),
421
2
					lane_id: bridge.lane_id.into(),
422
2
					pruned_messages,
423
2
					enqueued_messages,
424
2
				});
425
2

            
426
2
				return Ok(());
427
1
			}
428
1

            
429
1
			// else we have pruned all messages, so lanes and the bridge itself may gone
430
1
			inbound_lane.purge();
431
1
			outbound_lane.purge();
432
1
			Bridges::<T, I>::remove(locations.bridge_id());
433
1
			LaneToBridge::<T, I>::remove(bridge.lane_id);
434

            
435
			// return deposit
436
1
			let released_deposit = if let Some(deposit) = bridge.deposit {
437
1
				T::Currency::release(
438
1
					&HoldReason::BridgeDeposit.into(),
439
1
					&deposit.account,
440
1
					deposit.amount,
441
1
					Precision::BestEffort,
442
1
				)
443
1
				.inspect_err(|e| {
444
					// we can't do anything here - looks like funds have been (partially) unreserved
445
					// before by someone else. Let's not fail, though - it'll be worse for the
446
					// caller
447
					log::error!(
448
						target: LOG_TARGET,
449
						"Failed to unreserve during the bridge {:?} closure with error: {e:?}",
450
						locations.bridge_id(),
451
					);
452
1
				})
453
1
				.map(|released| Deposit::new(deposit.account, released))
454
1
				.ok()
455
			} else {
456
				None
457
			};
458

            
459
			// write something to log
460
1
			log::trace!(
461
				target: LOG_TARGET,
462
				"Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned",
463
				locations.bridge_id(),
464
				bridge.lane_id,
465
				locations.bridge_origin_universal_location(),
466
				locations.bridge_destination_universal_location(),
467
			);
468

            
469
			// deposit the `BridgePruned` event
470
1
			Self::deposit_event(Event::<T, I>::BridgePruned {
471
1
				bridge_id: *locations.bridge_id(),
472
1
				lane_id: bridge.lane_id.into(),
473
1
				bridge_deposit: released_deposit,
474
1
				pruned_messages,
475
1
			});
476
1

            
477
1
			Ok(())
478
		}
479

            
480
		/// Attempts to update the `maybe_notify` callback for receiving congestion notifications.
481
		///
482
		/// This can only be called by the "owner" of this side of the bridge, which means that the
483
		/// inbound XCM channel with the local origin chain is functional.
484
		#[pallet::call_index(2)]
485
		#[pallet::weight(WeightInfoOf::<T, I>::update_notification_receiver())]
486
		pub fn update_notification_receiver(
487
			origin: OriginFor<T>,
488
			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
489
			maybe_notify: Option<Receiver>,
490
3
		) -> DispatchResult {
491
3
			let locations =
492
3
				Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
493
3
			Bridges::<T, I>::try_mutate_exists(locations.bridge_id(), |bridge| match bridge {
494
3
				Some(b) => {
495
3
					b.maybe_notify = maybe_notify;
496
3

            
497
3
					Self::deposit_event(Event::<T, I>::NotificationReceiverUpdated {
498
3
						bridge_id: *locations.bridge_id(),
499
3
						maybe_notify: b.maybe_notify.clone(),
500
3
					});
501
3
					Ok(())
502
				}
503
				None => Err(Error::<T, I>::UnknownBridge.into()),
504
3
			})
505
		}
506
	}
507

            
508
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
509
		/// Open bridge for lane.
510
22
		pub fn do_open_bridge(
511
22
			locations: Box<BridgeLocations>,
512
22
			lane_id: T::LaneId,
513
22
			create_lanes: bool,
514
22
			maybe_notify: Option<Receiver>,
515
22
		) -> Result<(), DispatchError> {
516
			// reserve balance (if needed)
517
22
			let deposit = if T::AllowWithoutBridgeDeposit::contains(
518
22
				locations.bridge_origin_relative_location(),
519
22
			) {
520
8
				None
521
			} else {
522
				// get origin's sovereign account
523
14
				let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
524
14
					locations.bridge_origin_relative_location(),
525
14
				)
526
14
				.ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
527

            
528
13
				let deposit = T::BridgeDeposit::get();
529
13
				T::Currency::hold(
530
13
					&HoldReason::BridgeDeposit.into(),
531
13
					&bridge_owner_account,
532
13
					deposit,
533
13
				)
534
13
				.map_err(|e| {
535
1
					log::error!(
536
						target: LOG_TARGET,
537
						"Failed to hold bridge deposit: {deposit:?} \
538
						from bridge_owner_account: {bridge_owner_account:?} derived from \
539
						bridge_origin_relative_location: {:?} with error: {e:?}",
540
						locations.bridge_origin_relative_location(),
541
					);
542
1
					Error::<T, I>::FailedToReserveBridgeDeposit
543
13
				})?;
544
12
				Some(Deposit::new(bridge_owner_account, deposit))
545
			};
546

            
547
			// save bridge metadata
548
20
			Bridges::<T, I>::try_mutate(locations.bridge_id(), |bridge| match bridge {
549
1
				Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
550
				None => {
551
19
					*bridge = Some(BridgeOf::<T, I> {
552
19
						bridge_origin_relative_location: Box::new(
553
19
							locations.bridge_origin_relative_location().clone().into(),
554
19
						),
555
19
						bridge_origin_universal_location: Box::new(
556
19
							locations.bridge_origin_universal_location().clone().into(),
557
19
						),
558
19
						bridge_destination_universal_location: Box::new(
559
19
							locations
560
19
								.bridge_destination_universal_location()
561
19
								.clone()
562
19
								.into(),
563
19
						),
564
19
						state: BridgeState::Opened,
565
19
						deposit: deposit.clone(),
566
19
						lane_id,
567
19
						maybe_notify,
568
19
					});
569
19
					Ok(())
570
				}
571
20
			})?;
572
			// save lane to bridge mapping
573
19
			LaneToBridge::<T, I>::try_mutate(lane_id, |bridge| match bridge {
574
				Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
575
				None => {
576
19
					*bridge = Some(*locations.bridge_id());
577
19
					Ok(())
578
				}
579
19
			})?;
580

            
581
19
			if create_lanes {
582
				// create new lanes. Under normal circumstances, following calls shall never fail
583
19
				let lanes_manager = LanesManagerOf::<T, I>::new();
584
19
				lanes_manager
585
19
					.create_inbound_lane(lane_id)
586
19
					.map_err(Error::<T, I>::LanesManager)?;
587
18
				lanes_manager
588
18
					.create_outbound_lane(lane_id)
589
18
					.map_err(Error::<T, I>::LanesManager)?;
590
			}
591

            
592
			// write something to log
593
17
			log::trace!(
594
				target: LOG_TARGET,
595
				"Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}",
596
				locations.bridge_id(),
597
				locations.bridge_origin_universal_location(),
598
				locations.bridge_destination_universal_location(),
599
			);
600

            
601
			// deposit `BridgeOpened` event
602
17
			Self::deposit_event(Event::<T, I>::BridgeOpened {
603
17
				bridge_id: *locations.bridge_id(),
604
17
				bridge_deposit: deposit,
605
17
				local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()),
606
17
				remote_endpoint: Box::new(
607
17
					locations.bridge_destination_universal_location().clone(),
608
17
				),
609
17
				lane_id: lane_id.into(),
610
17
			});
611
17

            
612
17
			Ok(())
613
22
		}
614
	}
615

            
616
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
617
		/// Return bridge endpoint locations and dedicated lane identifier. This method converts
618
		/// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin`
619
		/// converter.
620
32820
		pub fn bridge_locations_from_origin(
621
32820
			origin: OriginFor<T>,
622
32820
			bridge_destination_universal_location: Box<VersionedInteriorLocation>,
623
32820
		) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
624
32820
			Self::bridge_locations(
625
32820
				T::OpenBridgeOrigin::ensure_origin(origin)?,
626
32818
				(*bridge_destination_universal_location)
627
32818
					.try_into()
628
32818
					.map_err(|_| Error::<T, I>::UnsupportedXcmVersion)?,
629
			)
630
32820
		}
631

            
632
		/// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`).
633
81995
		pub fn bridge_locations(
634
81995
			bridge_origin_relative_location: Location,
635
81995
			bridge_destination_universal_location: InteriorLocation,
636
81995
		) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
637
81995
			BridgeLocations::bridge_locations(
638
81995
				T::UniversalLocation::get(),
639
81995
				bridge_origin_relative_location,
640
81995
				bridge_destination_universal_location,
641
81995
				Self::bridged_network_id()?,
642
			)
643
81995
			.map_err(|e| {
644
7
				log::trace!(
645
					target: LOG_TARGET,
646
					"bridge_locations error: {e:?}",
647
				);
648
7
				Error::<T, I>::BridgeLocations(e).into()
649
81995
			})
650
81995
		}
651

            
652
		/// Return bridge metadata by bridge_id
653
49207
		pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
654
49207
			Bridges::<T, I>::get(bridge_id)
655
49207
		}
656

            
657
		/// Return bridge metadata by lane_id
658
9
		pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
659
9
			LaneToBridge::<T, I>::get(lane_id)
660
9
				.and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
661
9
		}
662
	}
663

            
664
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
665
		/// Returns some `NetworkId` if contains `GlobalConsensus` junction.
666
81996
		pub fn bridged_network_id() -> Result<NetworkId, sp_runtime::DispatchError> {
667
81996
			match T::BridgedNetwork::get().take_first_interior() {
668
81996
				Some(GlobalConsensus(network)) => Ok(network),
669
				_ => Err(Error::<T, I>::BridgeLocations(
670
					BridgeLocationsError::InvalidBridgeDestination,
671
				)
672
				.into()),
673
			}
674
81996
		}
675
	}
676

            
677
	#[cfg(feature = "runtime-benchmarks")]
678
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
679
		/// Open bridge for lane.
680
		pub fn open_bridge_for_benchmarks(
681
			lane_id: LaneIdOf<T, I>,
682
			bridge_origin_relative_location: Location,
683
			bridge_destination_universal_location: InteriorLocation,
684
			create_lanes: bool,
685
			maybe_notify: Option<Receiver>,
686
			initial_balance: impl Fn() -> BalanceOf<ThisChainOf<T, I>>,
687
		) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
688
			let locations = Pallet::<T, I>::bridge_locations(
689
				bridge_origin_relative_location.into(),
690
				bridge_destination_universal_location.into(),
691
			)?;
692

            
693
			if !T::AllowWithoutBridgeDeposit::contains(locations.bridge_origin_relative_location())
694
			{
695
				let account_id = T::BridgeOriginAccountIdConverter::convert_location(
696
					locations.bridge_origin_relative_location(),
697
				)
698
				.ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
699

            
700
				use frame_support::traits::fungible::Unbalanced;
701
				use sp_runtime::Saturating;
702
				T::Currency::increase_balance(
703
					&account_id,
704
					initial_balance().saturating_add(T::BridgeDeposit::get()),
705
					Precision::BestEffort,
706
				)?;
707
			}
708

            
709
			Pallet::<T, I>::do_open_bridge(locations.clone(), lane_id, create_lanes, maybe_notify)?;
710

            
711
			Ok(locations)
712
		}
713
	}
714

            
715
	#[cfg(any(test, feature = "try-runtime", feature = "std"))]
716
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
717
		/// Ensure the correctness of the state of this pallet.
718
32806
		pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
719
			use sp_std::collections::btree_set::BTreeSet;
720

            
721
32806
			let mut lanes = BTreeSet::new();
722

            
723
			// check all known bridge configurations
724
65601
			for (bridge_id, bridge) in Bridges::<T, I>::iter() {
725
32800
				lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?);
726
			}
727
32801
			ensure!(
728
32801
				lanes.len() == Bridges::<T, I>::iter().count(),
729
				"Invalid `Bridges` configuration, probably two bridges handle the same laneId!"
730
			);
731
32801
			ensure!(
732
32801
				lanes.len() == LaneToBridge::<T, I>::iter().count(),
733
				"Invalid `LaneToBridge` configuration, probably missing or not removed laneId!"
734
			);
735

            
736
			// check connected `pallet_bridge_messages` state.
737
32801
			Self::do_try_state_for_messages()
738
32806
		}
739

            
740
		/// Ensure the correctness of the state of the bridge.
741
32800
		pub fn do_try_state_for_bridge(
742
32800
			bridge_id: BridgeId,
743
32800
			bridge: BridgeOf<T, I>,
744
32800
		) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
745
32800
			log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}");
746

            
747
			// check `BridgeId` points to the same `LaneId` and vice versa.
748
32800
			ensure!(
749
32800
				Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
750
1
				"Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
751
			);
752

            
753
			// check `pallet_bridge_messages` state for that `LaneId`.
754
32799
			let lanes_manager = LanesManagerOf::<T, I>::new();
755
32799
			ensure!(
756
32799
				lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(),
757
1
				"Inbound lane not found!",
758
			);
759
32798
			ensure!(
760
32798
				lanes_manager
761
32798
					.any_state_outbound_lane(bridge.lane_id)
762
32798
					.is_ok(),
763
1
				"Outbound lane not found!",
764
			);
765

            
766
			// check that `locations` are convertible to the `latest` XCM.
767
32797
			let bridge_origin_relative_location_as_latest: &Location = bridge
768
32797
				.bridge_origin_relative_location
769
32797
				.try_as()
770
32797
				.map_err(|_| {
771
					"`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!"
772
32797
				})?;
773
32797
			let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location
774
32797
				.try_as()
775
32797
				.map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
776
32797
			let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location
777
32797
				.try_as()
778
32797
				.map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
779

            
780
			// check `BridgeId` does not change
781
32797
			ensure!(
782
32797
				bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest),
783
1
				"`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!"
784
			);
785

            
786
			// check bridge account owner
787
32796
			if let Some(deposit) = bridge.deposit {
788
32786
				ensure!(
789
32786
					T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(deposit.account),
790
1
					"`bridge.deposit.account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!"
791
				);
792
10
			}
793

            
794
32795
			Ok(bridge.lane_id)
795
32800
		}
796

            
797
		/// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance.
798
32801
		pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
799
			// check that all `InboundLanes` laneIds have mapping to some bridge.
800
65596
			for lane_id in pallet_bridge_messages::InboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
801
32796
				log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}...");
802
32796
				ensure!(
803
32796
					LaneToBridge::<T, I>::get(lane_id).is_some(),
804
1
					"Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"
805
				);
806
			}
807

            
808
			// check that all `OutboundLanes` laneIds have mapping to some bridge.
809
65595
			for lane_id in pallet_bridge_messages::OutboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
810
32796
				log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}...");
811
32796
				ensure!(
812
32796
					LaneToBridge::<T, I>::get(lane_id).is_some(),
813
1
					"Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"
814
				);
815
			}
816

            
817
32799
			Ok(())
818
32801
		}
819
	}
820

            
821
	/// All registered bridges.
822
213292
	#[pallet::storage]
823
	pub type Bridges<T: Config<I>, I: 'static = ()> =
824
		StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
825
	/// All registered `lane_id` and `bridge_id` mappings.
826
164053
	#[pallet::storage]
827
	pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
828
		StorageMap<_, Identity, T::LaneId, BridgeId>;
829

            
830
	#[pallet::genesis_config]
831
	#[derive(DefaultNoBound)]
832
	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
833
		/// Opened bridges.
834
		///
835
		/// Keep in mind that we are **NOT** reserving any amount for the bridges opened at
836
		/// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using
837
		/// other pallets genesis configuration or some other means.
838
		pub opened_bridges: Vec<(
839
			Location,
840
			InteriorLocation,
841
			Option<T::LaneId>,
842
			Option<Receiver>,
843
		)>,
844
		/// Dummy marker.
845
		#[serde(skip)]
846
		pub _phantom: sp_std::marker::PhantomData<(T, I)>,
847
	}
848

            
849
	#[pallet::genesis_build]
850
	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I>
851
	where
852
		T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
853
	{
854
1
		fn build(&self) {
855
			for (
856
				bridge_origin_relative_location,
857
				bridge_destination_universal_location,
858
				maybe_lane_id,
859
				maybe_notify,
860
1
			) in &self.opened_bridges
861
			{
862
				let locations = Pallet::<T, I>::bridge_locations(
863
					bridge_origin_relative_location.clone(),
864
					bridge_destination_universal_location.clone().into(),
865
				)
866
				.expect("Invalid genesis configuration");
867

            
868
				let lane_id = match maybe_lane_id {
869
					Some(lane_id) => *lane_id,
870
					None => locations
871
						.calculate_lane_id(xcm::latest::VERSION)
872
						.expect("Valid locations"),
873
				};
874

            
875
				Pallet::<T, I>::do_open_bridge(locations, lane_id, true, maybe_notify.clone())
876
					.expect("Valid opened bridge!");
877
			}
878
1
		}
879
	}
880

            
881
	#[pallet::event]
882
23
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
883
	pub enum Event<T: Config<I>, I: 'static = ()> {
884
3
		/// The bridge between two locations has been opened.
885
		BridgeOpened {
886
			/// Bridge identifier.
887
			bridge_id: BridgeId,
888
			/// Bridge deposit held.
889
			bridge_deposit: Option<DepositOf<ThisChainOf<T, I>>>,
890

            
891
			/// Universal location of local bridge endpoint.
892
			local_endpoint: Box<InteriorLocation>,
893
			/// Universal location of remote bridge endpoint.
894
			remote_endpoint: Box<InteriorLocation>,
895
			/// Lane identifier.
896
			lane_id: T::LaneId,
897
		},
898
5
		/// Bridge is going to be closed, but not yet fully pruned from the runtime storage.
899
		ClosingBridge {
900
			/// Bridge identifier.
901
			bridge_id: BridgeId,
902
			/// Lane identifier.
903
			lane_id: T::LaneId,
904
			/// Number of pruned messages during the close call.
905
			pruned_messages: MessageNonce,
906
			/// Number of enqueued messages that need to be pruned in follow up calls.
907
			enqueued_messages: MessageNonce,
908
		},
909
1
		/// Bridge has been closed and pruned from the runtime storage. It now may be reopened
910
		/// again by any participant.
911
		BridgePruned {
912
			/// Bridge identifier.
913
			bridge_id: BridgeId,
914
			/// Lane identifier.
915
			lane_id: T::LaneId,
916
			/// Amount of deposit released.
917
			bridge_deposit: Option<DepositOf<ThisChainOf<T, I>>>,
918
			/// Number of pruned messages during the close call.
919
			pruned_messages: MessageNonce,
920
		},
921
		/// The bridge has been updated with a new notification receiver.
922
		NotificationReceiverUpdated {
923
			/// Bridge identifier.
924
			bridge_id: BridgeId,
925
			/// Updated notification receiver.
926
			maybe_notify: Option<Receiver>,
927
		},
928
	}
929

            
930
54
	#[pallet::error]
931
	pub enum Error<T, I = ()> {
932
		/// Bridge locations error.
933
		BridgeLocations(BridgeLocationsError),
934
		/// Invalid local bridge origin account.
935
		InvalidBridgeOriginAccount,
936
		/// The bridge is already registered in this pallet.
937
		BridgeAlreadyExists,
938
		/// The local origin already owns a maximal number of bridges.
939
		TooManyBridgesForLocalOrigin,
940
		/// Trying to close already closed bridge.
941
		BridgeAlreadyClosed,
942
		/// Lanes manager error.
943
		LanesManager(LanesManagerError),
944
		/// Trying to access unknown bridge.
945
		UnknownBridge,
946
		/// The bridge origin can't pay the required amount for opening the bridge.
947
		FailedToReserveBridgeDeposit,
948
		/// The version of XCM location argument is unsupported.
949
		UnsupportedXcmVersion,
950
	}
951
}