1#![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
171pub 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 #[pallet::composite_enum]
185 pub enum HoldReason<I: 'static = ()> {
186 #[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 #[allow(deprecated)]
198 type RuntimeEvent: From<Event<Self, I>>
199 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
200
201 type UniversalLocation: Get<InteriorLocation>;
203 #[pallet::constant]
208 type BridgedNetwork: Get<Location>;
209 type BridgeMessagesPalletInstance: 'static;
212
213 type MessageExportPrice: Get<Assets>;
215 type DestinationVersion: GetVersion;
217
218 type ForceOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
221 type OpenBridgeOrigin: EnsureOrigin<
224 <Self as SystemConfig>::RuntimeOrigin,
225 Success = Location,
226 >;
227 type BridgeOriginAccountIdConverter: ConvertLocation<AccountIdOf<ThisChainOf<Self, I>>>;
229
230 #[pallet::constant]
233 type BridgeDeposit: Get<BalanceOf<ThisChainOf<Self, I>>>;
234 type Currency: MutateHold<
236 AccountIdOf<ThisChainOf<Self, I>>,
237 Balance = BalanceOf<ThisChainOf<Self, I>>,
238 Reason = Self::RuntimeHoldReason,
239 >;
240 type RuntimeHoldReason: From<HoldReason<I>>;
242 type AllowWithoutBridgeDeposit: Contains<Location>;
245
246 type LocalXcmChannelManager: LocalXcmChannelManager;
248 type BlobDispatcher: DispatchBlob;
250 }
251
252 pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>, LaneIdOf<T, I>>;
254 pub type ThisChainOf<T, I> =
256 pallet_bridge_messages::ThisChainOf<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
257 pub type LaneIdOf<T, I> =
259 <T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::LaneId;
260 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 #[pallet::call_index(0)]
298 #[pallet::weight(Weight::zero())] pub fn open_bridge(
300 origin: OriginFor<T>,
301 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
302 ) -> DispatchResult {
303 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 #[pallet::call_index(1)]
336 #[pallet::weight(Weight::zero())] pub fn close_bridge(
338 origin: OriginFor<T>,
339 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
340 may_prune_messages: MessageNonce,
341 ) -> DispatchResult {
342 let locations =
344 Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
345
346 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 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 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 !outbound_lane.queued_messages().is_empty() {
381 inbound_lane.set_state(LaneState::Closed);
383 outbound_lane.set_state(LaneState::Closed);
384
385 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 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 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 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 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 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 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 pub fn do_open_bridge(
460 locations: Box<BridgeLocations>,
461 lane_id: T::LaneId,
462 create_lanes: bool,
463 ) -> Result<(), DispatchError> {
464 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 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 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 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 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 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 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 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 pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
599 Bridges::<T, I>::get(bridge_id)
600 }
601
602 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 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 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 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 Self::do_try_state_for_messages()
645 }
646
647 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 ensure!(
656 Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
657 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
658 );
659
660 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 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 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 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 pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
696 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 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 #[pallet::storage]
720 pub type Bridges<T: Config<I>, I: 'static = ()> =
721 StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
722 #[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 pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
736 #[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 BridgeOpened {
776 bridge_id: BridgeId,
778 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
780
781 local_endpoint: Box<InteriorLocation>,
783 remote_endpoint: Box<InteriorLocation>,
785 lane_id: T::LaneId,
787 },
788 ClosingBridge {
790 bridge_id: BridgeId,
792 lane_id: T::LaneId,
794 pruned_messages: MessageNonce,
796 enqueued_messages: MessageNonce,
798 },
799 BridgePruned {
802 bridge_id: BridgeId,
804 lane_id: T::LaneId,
806 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
808 pruned_messages: MessageNonce,
810 },
811 }
812
813 #[pallet::error]
814 pub enum Error<T, I = ()> {
815 BridgeLocations(BridgeLocationsError),
817 InvalidBridgeOriginAccount,
819 BridgeAlreadyExists,
821 TooManyBridgesForLocalOrigin,
823 BridgeAlreadyClosed,
825 LanesManager(LanesManagerError),
827 UnknownBridge,
829 FailedToReserveBridgeDeposit,
831 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 let origins = [
1099 (OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1100 (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1101 ];
1102
1103 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1105 let existential_deposit = ExistentialDeposit::get();
1106 for (origin, expected_deposit) in origins {
1107 System::set_block_number(1);
1109 System::reset_events();
1110
1111 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 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 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 assert_ok!(XcmOverBridge::open_bridge(
1149 origin,
1150 Box::new(locations.bridge_destination_universal_location().clone().into()),
1151 ));
1152
1153 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 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 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 let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1295 let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1296
1297 for _ in 0..32 {
1299 enqueue_message(bridge.lane_id);
1300 }
1301
1302 assert_ok!(XcmOverBridge::close_bridge(
1304 origin.clone(),
1305 Box::new(locations.bridge_destination_universal_location().clone().into()),
1306 16,
1307 ),);
1308
1309 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 assert_ok!(XcmOverBridge::close_bridge(
1354 origin.clone(),
1355 Box::new(locations.bridge_destination_universal_location().clone().into()),
1356 8,
1357 ),);
1358
1359 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 assert_ok!(XcmOverBridge::close_bridge(
1403 origin,
1404 Box::new(locations.bridge_destination_universal_location().clone().into()),
1405 9,
1406 ),);
1407
1408 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 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 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 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 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 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 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 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 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 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}