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 type RuntimeEvent: From<Event<Self, I>>
198 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
199
200 type UniversalLocation: Get<InteriorLocation>;
202 #[pallet::constant]
207 type BridgedNetwork: Get<Location>;
208 type BridgeMessagesPalletInstance: 'static;
211
212 type MessageExportPrice: Get<Assets>;
214 type DestinationVersion: GetVersion;
216
217 type ForceOrigin: EnsureOrigin<<Self as SystemConfig>::RuntimeOrigin>;
220 type OpenBridgeOrigin: EnsureOrigin<
223 <Self as SystemConfig>::RuntimeOrigin,
224 Success = Location,
225 >;
226 type BridgeOriginAccountIdConverter: ConvertLocation<AccountIdOf<ThisChainOf<Self, I>>>;
228
229 #[pallet::constant]
232 type BridgeDeposit: Get<BalanceOf<ThisChainOf<Self, I>>>;
233 type Currency: MutateHold<
235 AccountIdOf<ThisChainOf<Self, I>>,
236 Balance = BalanceOf<ThisChainOf<Self, I>>,
237 Reason = Self::RuntimeHoldReason,
238 >;
239 type RuntimeHoldReason: From<HoldReason<I>>;
241 type AllowWithoutBridgeDeposit: Contains<Location>;
244
245 type LocalXcmChannelManager: LocalXcmChannelManager;
247 type BlobDispatcher: DispatchBlob;
249 }
250
251 pub type BridgeOf<T, I> = Bridge<ThisChainOf<T, I>, LaneIdOf<T, I>>;
253 pub type ThisChainOf<T, I> =
255 pallet_bridge_messages::ThisChainOf<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
256 pub type LaneIdOf<T, I> =
258 <T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::LaneId;
259 pub type LanesManagerOf<T, I> =
261 pallet_bridge_messages::LanesManager<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
262
263 #[pallet::pallet]
264 #[pallet::storage_version(migration::STORAGE_VERSION)]
265 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
266
267 #[pallet::hooks]
268 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
269 fn integrity_test() {
270 assert!(
271 Self::bridged_network_id().is_ok(),
272 "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`",
273 T::BridgedNetwork::get()
274 )
275 }
276
277 #[cfg(feature = "try-runtime")]
278 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
279 Self::do_try_state()
280 }
281 }
282
283 #[pallet::call]
284 impl<T: Config<I>, I: 'static> Pallet<T, I> {
285 #[pallet::call_index(0)]
297 #[pallet::weight(Weight::zero())] pub fn open_bridge(
299 origin: OriginFor<T>,
300 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
301 ) -> DispatchResult {
302 let xcm_version = bridge_destination_universal_location.identify_version();
304 let locations =
305 Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
306 let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| {
307 log::trace!(
308 target: LOG_TARGET,
309 "calculate_lane_id error: {e:?}",
310 );
311 Error::<T, I>::BridgeLocations(e)
312 })?;
313
314 Self::do_open_bridge(locations, lane_id, true)
315 }
316
317 #[pallet::call_index(1)]
335 #[pallet::weight(Weight::zero())] pub fn close_bridge(
337 origin: OriginFor<T>,
338 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
339 may_prune_messages: MessageNonce,
340 ) -> DispatchResult {
341 let locations =
343 Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?;
344
345 let bridge =
350 Bridges::<T, I>::try_mutate_exists(locations.bridge_id(), |bridge| match bridge {
351 Some(bridge) => {
352 bridge.state = BridgeState::Closed;
353 Ok(bridge.clone())
354 },
355 None => Err(Error::<T, I>::UnknownBridge),
356 })?;
357
358 let lanes_manager = LanesManagerOf::<T, I>::new();
360 let mut inbound_lane = lanes_manager
361 .any_state_inbound_lane(bridge.lane_id)
362 .map_err(Error::<T, I>::LanesManager)?;
363 let mut outbound_lane = lanes_manager
364 .any_state_outbound_lane(bridge.lane_id)
365 .map_err(Error::<T, I>::LanesManager)?;
366
367 let mut pruned_messages = 0;
369 for _ in outbound_lane.queued_messages() {
370 if pruned_messages == may_prune_messages {
371 break
372 }
373
374 outbound_lane.remove_oldest_unpruned_message();
375 pruned_messages += 1;
376 }
377
378 if !outbound_lane.queued_messages().is_empty() {
380 inbound_lane.set_state(LaneState::Closed);
382 outbound_lane.set_state(LaneState::Closed);
383
384 let enqueued_messages = outbound_lane.queued_messages().saturating_len();
386 log::trace!(
387 target: LOG_TARGET,
388 "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining",
389 locations.bridge_id(),
390 locations.bridge_origin_universal_location(),
391 locations.bridge_destination_universal_location(),
392 bridge.lane_id,
393 enqueued_messages,
394 );
395
396 Self::deposit_event(Event::<T, I>::ClosingBridge {
398 bridge_id: *locations.bridge_id(),
399 lane_id: bridge.lane_id.into(),
400 pruned_messages,
401 enqueued_messages,
402 });
403
404 return Ok(())
405 }
406
407 inbound_lane.purge();
409 outbound_lane.purge();
410 Bridges::<T, I>::remove(locations.bridge_id());
411 LaneToBridge::<T, I>::remove(bridge.lane_id);
412
413 let released_deposit = T::Currency::release(
415 &HoldReason::BridgeDeposit.into(),
416 &bridge.bridge_owner_account,
417 bridge.deposit,
418 Precision::BestEffort,
419 )
420 .inspect_err(|e| {
421 log::error!(
424 target: LOG_TARGET,
425 "Failed to unreserve during the bridge {:?} closure with error: {e:?}",
426 locations.bridge_id(),
427 );
428 })
429 .ok()
430 .unwrap_or(BalanceOf::<ThisChainOf<T, I>>::zero());
431
432 log::trace!(
434 target: LOG_TARGET,
435 "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned",
436 locations.bridge_id(),
437 bridge.lane_id,
438 locations.bridge_origin_universal_location(),
439 locations.bridge_destination_universal_location(),
440 );
441
442 Self::deposit_event(Event::<T, I>::BridgePruned {
444 bridge_id: *locations.bridge_id(),
445 lane_id: bridge.lane_id.into(),
446 bridge_deposit: released_deposit,
447 pruned_messages,
448 });
449
450 Ok(())
451 }
452 }
453
454 impl<T: Config<I>, I: 'static> Pallet<T, I> {
455 pub fn do_open_bridge(
457 locations: Box<BridgeLocations>,
458 lane_id: T::LaneId,
459 create_lanes: bool,
460 ) -> Result<(), DispatchError> {
461 let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
463 locations.bridge_origin_relative_location(),
464 )
465 .ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
466 let deposit = if T::AllowWithoutBridgeDeposit::contains(
467 locations.bridge_origin_relative_location(),
468 ) {
469 BalanceOf::<ThisChainOf<T, I>>::zero()
470 } else {
471 let deposit = T::BridgeDeposit::get();
472 T::Currency::hold(
473 &HoldReason::BridgeDeposit.into(),
474 &bridge_owner_account,
475 deposit,
476 )
477 .map_err(|e| {
478 log::error!(
479 target: LOG_TARGET,
480 "Failed to hold bridge deposit: {deposit:?} \
481 from bridge_owner_account: {bridge_owner_account:?} derived from \
482 bridge_origin_relative_location: {:?} with error: {e:?}",
483 locations.bridge_origin_relative_location(),
484 );
485 Error::<T, I>::FailedToReserveBridgeDeposit
486 })?;
487 deposit
488 };
489
490 Bridges::<T, I>::try_mutate(locations.bridge_id(), |bridge| match bridge {
492 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
493 None => {
494 *bridge = Some(BridgeOf::<T, I> {
495 bridge_origin_relative_location: Box::new(
496 locations.bridge_origin_relative_location().clone().into(),
497 ),
498 bridge_origin_universal_location: Box::new(
499 locations.bridge_origin_universal_location().clone().into(),
500 ),
501 bridge_destination_universal_location: Box::new(
502 locations.bridge_destination_universal_location().clone().into(),
503 ),
504 state: BridgeState::Opened,
505 bridge_owner_account,
506 deposit,
507 lane_id,
508 });
509 Ok(())
510 },
511 })?;
512 LaneToBridge::<T, I>::try_mutate(lane_id, |bridge| match bridge {
514 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
515 None => {
516 *bridge = Some(*locations.bridge_id());
517 Ok(())
518 },
519 })?;
520
521 if create_lanes {
522 let lanes_manager = LanesManagerOf::<T, I>::new();
524 lanes_manager
525 .create_inbound_lane(lane_id)
526 .map_err(Error::<T, I>::LanesManager)?;
527 lanes_manager
528 .create_outbound_lane(lane_id)
529 .map_err(Error::<T, I>::LanesManager)?;
530 }
531
532 log::trace!(
534 target: LOG_TARGET,
535 "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}",
536 locations.bridge_id(),
537 locations.bridge_origin_universal_location(),
538 locations.bridge_destination_universal_location(),
539 );
540
541 Self::deposit_event(Event::<T, I>::BridgeOpened {
543 bridge_id: *locations.bridge_id(),
544 bridge_deposit: deposit,
545 local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()),
546 remote_endpoint: Box::new(
547 locations.bridge_destination_universal_location().clone(),
548 ),
549 lane_id: lane_id.into(),
550 });
551
552 Ok(())
553 }
554 }
555
556 impl<T: Config<I>, I: 'static> Pallet<T, I> {
557 pub fn bridge_locations_from_origin(
561 origin: OriginFor<T>,
562 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
563 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
564 Self::bridge_locations(
565 T::OpenBridgeOrigin::ensure_origin(origin)?,
566 (*bridge_destination_universal_location)
567 .try_into()
568 .map_err(|_| Error::<T, I>::UnsupportedXcmVersion)?,
569 )
570 }
571
572 pub fn bridge_locations(
574 bridge_origin_relative_location: Location,
575 bridge_destination_universal_location: InteriorLocation,
576 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
577 BridgeLocations::bridge_locations(
578 T::UniversalLocation::get(),
579 bridge_origin_relative_location,
580 bridge_destination_universal_location,
581 Self::bridged_network_id()?,
582 )
583 .map_err(|e| {
584 log::trace!(
585 target: LOG_TARGET,
586 "bridge_locations error: {e:?}",
587 );
588 Error::<T, I>::BridgeLocations(e).into()
589 })
590 }
591
592 pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
594 Bridges::<T, I>::get(bridge_id)
595 }
596
597 pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
599 LaneToBridge::<T, I>::get(lane_id)
600 .and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
601 }
602 }
603
604 impl<T: Config<I>, I: 'static> Pallet<T, I> {
605 fn bridged_network_id() -> Result<NetworkId, sp_runtime::DispatchError> {
607 match T::BridgedNetwork::get().take_first_interior() {
608 Some(GlobalConsensus(network)) => Ok(network),
609 _ => Err(Error::<T, I>::BridgeLocations(
610 BridgeLocationsError::InvalidBridgeDestination,
611 )
612 .into()),
613 }
614 }
615 }
616
617 #[cfg(any(test, feature = "try-runtime", feature = "std"))]
618 impl<T: Config<I>, I: 'static> Pallet<T, I> {
619 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
621 use sp_std::collections::btree_set::BTreeSet;
622
623 let mut lanes = BTreeSet::new();
624
625 for (bridge_id, bridge) in Bridges::<T, I>::iter() {
627 lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?);
628 }
629 ensure!(
630 lanes.len() == Bridges::<T, I>::iter().count(),
631 "Invalid `Bridges` configuration, probably two bridges handle the same laneId!"
632 );
633 ensure!(
634 lanes.len() == LaneToBridge::<T, I>::iter().count(),
635 "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!"
636 );
637
638 Self::do_try_state_for_messages()
640 }
641
642 pub fn do_try_state_for_bridge(
644 bridge_id: BridgeId,
645 bridge: BridgeOf<T, I>,
646 ) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
647 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}");
648
649 ensure!(
651 Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
652 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
653 );
654
655 let lanes_manager = LanesManagerOf::<T, I>::new();
657 ensure!(
658 lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(),
659 "Inbound lane not found!",
660 );
661 ensure!(
662 lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(),
663 "Outbound lane not found!",
664 );
665
666 let bridge_origin_relative_location_as_latest: &Location =
668 bridge.bridge_origin_relative_location.try_as().map_err(|_| {
669 "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!"
670 })?;
671 let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location
672 .try_as()
673 .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
674 let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location
675 .try_as()
676 .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
677
678 ensure!(
680 bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest),
681 "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!"
682 );
683
684 ensure!(
686 T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account),
687 "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!"
688 );
689
690 Ok(bridge.lane_id)
691 }
692
693 pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
695 for lane_id in pallet_bridge_messages::InboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
697 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}...");
698 ensure!(
699 LaneToBridge::<T, I>::get(lane_id).is_some(),
700 "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"
701 );
702 }
703
704 for lane_id in pallet_bridge_messages::OutboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
706 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}...");
707 ensure!(
708 LaneToBridge::<T, I>::get(lane_id).is_some(),
709 "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"
710 );
711 }
712
713 Ok(())
714 }
715 }
716
717 #[pallet::storage]
719 pub type Bridges<T: Config<I>, I: 'static = ()> =
720 StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
721 #[pallet::storage]
723 pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
724 StorageMap<_, Identity, T::LaneId, BridgeId>;
725
726 #[pallet::genesis_config]
727 #[derive(DefaultNoBound)]
728 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
729 pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
735 #[serde(skip)]
737 pub _phantom: sp_std::marker::PhantomData<(T, I)>,
738 }
739
740 #[pallet::genesis_build]
741 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I>
742 where
743 T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
744 {
745 fn build(&self) {
746 for (
747 bridge_origin_relative_location,
748 bridge_destination_universal_location,
749 maybe_lane_id,
750 ) in &self.opened_bridges
751 {
752 let locations = Pallet::<T, I>::bridge_locations(
753 bridge_origin_relative_location.clone(),
754 bridge_destination_universal_location.clone().into(),
755 )
756 .expect("Invalid genesis configuration");
757
758 let lane_id = match maybe_lane_id {
759 Some(lane_id) => *lane_id,
760 None =>
761 locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"),
762 };
763
764 Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
765 .expect("Valid opened bridge!");
766 }
767 }
768 }
769
770 #[pallet::event]
771 #[pallet::generate_deposit(pub(super) fn deposit_event)]
772 pub enum Event<T: Config<I>, I: 'static = ()> {
773 BridgeOpened {
775 bridge_id: BridgeId,
777 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
779
780 local_endpoint: Box<InteriorLocation>,
782 remote_endpoint: Box<InteriorLocation>,
784 lane_id: T::LaneId,
786 },
787 ClosingBridge {
789 bridge_id: BridgeId,
791 lane_id: T::LaneId,
793 pruned_messages: MessageNonce,
795 enqueued_messages: MessageNonce,
797 },
798 BridgePruned {
801 bridge_id: BridgeId,
803 lane_id: T::LaneId,
805 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
807 pruned_messages: MessageNonce,
809 },
810 }
811
812 #[pallet::error]
813 pub enum Error<T, I = ()> {
814 BridgeLocations(BridgeLocationsError),
816 InvalidBridgeOriginAccount,
818 BridgeAlreadyExists,
820 TooManyBridgesForLocalOrigin,
822 BridgeAlreadyClosed,
824 LanesManager(LanesManagerError),
826 UnknownBridge,
828 FailedToReserveBridgeDeposit,
830 UnsupportedXcmVersion,
832 }
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838 use bp_messages::LaneIdType;
839 use mock::*;
840
841 use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
842 use frame_system::{EventRecord, Phase};
843 use sp_runtime::TryRuntimeError;
844
845 fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
846 let bridge_owner_account =
847 LocationToAccountId::convert_location(locations.bridge_origin_relative_location())
848 .unwrap();
849 assert_ok!(Balances::mint_into(&bridge_owner_account, balance));
850 bridge_owner_account
851 }
852
853 fn mock_open_bridge_from_with(
854 origin: RuntimeOrigin,
855 deposit: Balance,
856 with: InteriorLocation,
857 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
858 let locations =
859 XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
860 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
861 let bridge_owner_account =
862 fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
863 Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap();
864
865 let bridge = Bridge {
866 bridge_origin_relative_location: Box::new(
867 locations.bridge_origin_relative_location().clone().into(),
868 ),
869 bridge_origin_universal_location: Box::new(
870 locations.bridge_origin_universal_location().clone().into(),
871 ),
872 bridge_destination_universal_location: Box::new(
873 locations.bridge_destination_universal_location().clone().into(),
874 ),
875 state: BridgeState::Opened,
876 bridge_owner_account,
877 deposit,
878 lane_id,
879 };
880 Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
881 LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
882
883 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
884 lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
885 lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
886
887 assert_ok!(XcmOverBridge::do_try_state());
888
889 (bridge, *locations)
890 }
891
892 fn mock_open_bridge_from(
893 origin: RuntimeOrigin,
894 deposit: Balance,
895 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
896 mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
897 }
898
899 fn enqueue_message(lane: TestLaneIdType) {
900 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
901 lanes_manager
902 .active_outbound_lane(lane)
903 .unwrap()
904 .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
905 }
906
907 #[test]
908 fn open_bridge_fails_if_origin_is_not_allowed() {
909 run_test(|| {
910 assert_noop!(
911 XcmOverBridge::open_bridge(
912 OpenBridgeOrigin::disallowed_origin(),
913 Box::new(bridged_asset_hub_universal_location().into()),
914 ),
915 sp_runtime::DispatchError::BadOrigin,
916 );
917 })
918 }
919
920 #[test]
921 fn open_bridge_fails_if_origin_is_not_relative() {
922 run_test(|| {
923 assert_noop!(
924 XcmOverBridge::open_bridge(
925 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
926 Box::new(bridged_asset_hub_universal_location().into()),
927 ),
928 Error::<TestRuntime, ()>::BridgeLocations(
929 BridgeLocationsError::InvalidBridgeOrigin
930 ),
931 );
932
933 assert_noop!(
934 XcmOverBridge::open_bridge(
935 OpenBridgeOrigin::sibling_parachain_universal_origin(),
936 Box::new(bridged_asset_hub_universal_location().into()),
937 ),
938 Error::<TestRuntime, ()>::BridgeLocations(
939 BridgeLocationsError::InvalidBridgeOrigin
940 ),
941 );
942 })
943 }
944
945 #[test]
946 fn open_bridge_fails_if_destination_is_not_remote() {
947 run_test(|| {
948 assert_noop!(
949 XcmOverBridge::open_bridge(
950 OpenBridgeOrigin::parent_relay_chain_origin(),
951 Box::new(
952 [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)]
953 .into()
954 ),
955 ),
956 Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
957 );
958 });
959 }
960
961 #[test]
962 fn open_bridge_fails_if_outside_of_bridged_consensus() {
963 run_test(|| {
964 assert_noop!(
965 XcmOverBridge::open_bridge(
966 OpenBridgeOrigin::parent_relay_chain_origin(),
967 Box::new(
968 [
969 GlobalConsensus(NonBridgedRelayNetwork::get()),
970 Parachain(BRIDGED_ASSET_HUB_ID)
971 ]
972 .into()
973 ),
974 ),
975 Error::<TestRuntime, ()>::BridgeLocations(
976 BridgeLocationsError::UnreachableDestination
977 ),
978 );
979 });
980 }
981
982 #[test]
983 fn open_bridge_fails_if_origin_has_no_sovereign_account() {
984 run_test(|| {
985 assert_noop!(
986 XcmOverBridge::open_bridge(
987 OpenBridgeOrigin::origin_without_sovereign_account(),
988 Box::new(bridged_asset_hub_universal_location().into()),
989 ),
990 Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
991 );
992 });
993 }
994
995 #[test]
996 fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
997 run_test(|| {
998 assert_noop!(
999 XcmOverBridge::open_bridge(
1000 OpenBridgeOrigin::sibling_parachain_origin(),
1001 Box::new(bridged_asset_hub_universal_location().into()),
1002 ),
1003 Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
1004 );
1005 });
1006 }
1007
1008 #[test]
1009 fn open_bridge_fails_if_it_already_exists() {
1010 run_test(|| {
1011 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1012 let locations = XcmOverBridge::bridge_locations_from_origin(
1013 origin.clone(),
1014 Box::new(bridged_asset_hub_universal_location().into()),
1015 )
1016 .unwrap();
1017 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1018 fund_origin_sovereign_account(
1019 &locations,
1020 BridgeDeposit::get() + ExistentialDeposit::get(),
1021 );
1022
1023 Bridges::<TestRuntime, ()>::insert(
1024 locations.bridge_id(),
1025 Bridge {
1026 bridge_origin_relative_location: Box::new(
1027 locations.bridge_origin_relative_location().clone().into(),
1028 ),
1029 bridge_origin_universal_location: Box::new(
1030 locations.bridge_origin_universal_location().clone().into(),
1031 ),
1032 bridge_destination_universal_location: Box::new(
1033 locations.bridge_destination_universal_location().clone().into(),
1034 ),
1035 state: BridgeState::Opened,
1036 bridge_owner_account: [0u8; 32].into(),
1037 deposit: 0,
1038 lane_id,
1039 },
1040 );
1041
1042 assert_noop!(
1043 XcmOverBridge::open_bridge(
1044 origin,
1045 Box::new(bridged_asset_hub_universal_location().into()),
1046 ),
1047 Error::<TestRuntime, ()>::BridgeAlreadyExists,
1048 );
1049 })
1050 }
1051
1052 #[test]
1053 fn open_bridge_fails_if_its_lanes_already_exists() {
1054 run_test(|| {
1055 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1056 let locations = XcmOverBridge::bridge_locations_from_origin(
1057 origin.clone(),
1058 Box::new(bridged_asset_hub_universal_location().into()),
1059 )
1060 .unwrap();
1061 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1062 fund_origin_sovereign_account(
1063 &locations,
1064 BridgeDeposit::get() + ExistentialDeposit::get(),
1065 );
1066
1067 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1068
1069 lanes_manager.create_inbound_lane(lane_id).unwrap();
1070 assert_noop!(
1071 XcmOverBridge::open_bridge(
1072 origin.clone(),
1073 Box::new(bridged_asset_hub_universal_location().into()),
1074 ),
1075 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
1076 );
1077
1078 lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
1079 lanes_manager.create_outbound_lane(lane_id).unwrap();
1080 assert_noop!(
1081 XcmOverBridge::open_bridge(
1082 origin,
1083 Box::new(bridged_asset_hub_universal_location().into()),
1084 ),
1085 Error::<TestRuntime, ()>::LanesManager(
1086 LanesManagerError::OutboundLaneAlreadyExists
1087 ),
1088 );
1089 })
1090 }
1091
1092 #[test]
1093 fn open_bridge_works() {
1094 run_test(|| {
1095 let origins = [
1098 (OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1099 (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1100 ];
1101
1102 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1104 let existential_deposit = ExistentialDeposit::get();
1105 for (origin, expected_deposit) in origins {
1106 System::set_block_number(1);
1108 System::reset_events();
1109
1110 let xcm_version = xcm::latest::VERSION;
1112 let locations = XcmOverBridge::bridge_locations_from_origin(
1113 origin.clone(),
1114 Box::new(
1115 VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
1116 .into_version(xcm_version)
1117 .expect("valid conversion"),
1118 ),
1119 )
1120 .unwrap();
1121 let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
1122
1123 assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
1125 assert_eq!(
1126 lanes_manager.active_inbound_lane(lane_id).map(drop),
1127 Err(LanesManagerError::UnknownInboundLane)
1128 );
1129 assert_eq!(
1130 lanes_manager.active_outbound_lane(lane_id).map(drop),
1131 Err(LanesManagerError::UnknownOutboundLane)
1132 );
1133 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
1134
1135 let bridge_owner_account = fund_origin_sovereign_account(
1137 &locations,
1138 expected_deposit + existential_deposit,
1139 );
1140 assert_eq!(
1141 Balances::free_balance(&bridge_owner_account),
1142 expected_deposit + existential_deposit
1143 );
1144 assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
1145
1146 assert_ok!(XcmOverBridge::open_bridge(
1148 origin,
1149 Box::new(locations.bridge_destination_universal_location().clone().into()),
1150 ));
1151
1152 assert_eq!(
1154 Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
1155 Some(Bridge {
1156 bridge_origin_relative_location: Box::new(
1157 locations.bridge_origin_relative_location().clone().into()
1158 ),
1159 bridge_origin_universal_location: Box::new(
1160 locations.bridge_origin_universal_location().clone().into(),
1161 ),
1162 bridge_destination_universal_location: Box::new(
1163 locations.bridge_destination_universal_location().clone().into(),
1164 ),
1165 state: BridgeState::Opened,
1166 bridge_owner_account: bridge_owner_account.clone(),
1167 deposit: expected_deposit,
1168 lane_id
1169 }),
1170 );
1171 assert_eq!(
1172 lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()),
1173 Ok(LaneState::Opened)
1174 );
1175 assert_eq!(
1176 lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()),
1177 Ok(LaneState::Opened)
1178 );
1179 assert_eq!(
1180 LaneToBridge::<TestRuntime, ()>::get(lane_id),
1181 Some(*locations.bridge_id())
1182 );
1183 assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit);
1184 assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit);
1185
1186 assert_eq!(
1188 System::events().last(),
1189 Some(&EventRecord {
1190 phase: Phase::Initialization,
1191 event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
1192 bridge_id: *locations.bridge_id(),
1193 bridge_deposit: expected_deposit,
1194 local_endpoint: Box::new(
1195 locations.bridge_origin_universal_location().clone()
1196 ),
1197 remote_endpoint: Box::new(
1198 locations.bridge_destination_universal_location().clone()
1199 ),
1200 lane_id: lane_id.into()
1201 }),
1202 topics: vec![],
1203 }),
1204 );
1205
1206 assert_ok!(XcmOverBridge::do_try_state());
1208 }
1209 });
1210 }
1211
1212 #[test]
1213 fn close_bridge_fails_if_origin_is_not_allowed() {
1214 run_test(|| {
1215 assert_noop!(
1216 XcmOverBridge::close_bridge(
1217 OpenBridgeOrigin::disallowed_origin(),
1218 Box::new(bridged_asset_hub_universal_location().into()),
1219 0,
1220 ),
1221 sp_runtime::DispatchError::BadOrigin,
1222 );
1223 })
1224 }
1225
1226 #[test]
1227 fn close_bridge_fails_if_origin_is_not_relative() {
1228 run_test(|| {
1229 assert_noop!(
1230 XcmOverBridge::close_bridge(
1231 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
1232 Box::new(bridged_asset_hub_universal_location().into()),
1233 0,
1234 ),
1235 Error::<TestRuntime, ()>::BridgeLocations(
1236 BridgeLocationsError::InvalidBridgeOrigin
1237 ),
1238 );
1239
1240 assert_noop!(
1241 XcmOverBridge::close_bridge(
1242 OpenBridgeOrigin::sibling_parachain_universal_origin(),
1243 Box::new(bridged_asset_hub_universal_location().into()),
1244 0,
1245 ),
1246 Error::<TestRuntime, ()>::BridgeLocations(
1247 BridgeLocationsError::InvalidBridgeOrigin
1248 ),
1249 );
1250 })
1251 }
1252
1253 #[test]
1254 fn close_bridge_fails_if_its_lanes_are_unknown() {
1255 run_test(|| {
1256 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1257 let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0);
1258
1259 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1260 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge();
1261 assert_noop!(
1262 XcmOverBridge::close_bridge(
1263 origin.clone(),
1264 Box::new(locations.bridge_destination_universal_location().clone().into()),
1265 0,
1266 ),
1267 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
1268 );
1269 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1270
1271 let (_, locations) = mock_open_bridge_from(origin.clone(), 0);
1272 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1273 assert_noop!(
1274 XcmOverBridge::close_bridge(
1275 origin,
1276 Box::new(locations.bridge_destination_universal_location().clone().into()),
1277 0,
1278 ),
1279 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
1280 );
1281 });
1282 }
1283
1284 #[test]
1285 fn close_bridge_works() {
1286 run_test(|| {
1287 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1288 let expected_deposit = BridgeDeposit::get();
1289 let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit);
1290 System::set_block_number(1);
1291
1292 let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1294 let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1295
1296 for _ in 0..32 {
1298 enqueue_message(bridge.lane_id);
1299 }
1300
1301 assert_ok!(XcmOverBridge::close_bridge(
1303 origin.clone(),
1304 Box::new(locations.bridge_destination_universal_location().clone().into()),
1305 16,
1306 ),);
1307
1308 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1311 assert_eq!(
1312 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1313 Some(BridgeState::Closed)
1314 );
1315 assert_eq!(
1316 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1317 LaneState::Closed
1318 );
1319 assert_eq!(
1320 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1321 LaneState::Closed
1322 );
1323 assert_eq!(
1324 lanes_manager
1325 .any_state_outbound_lane(bridge.lane_id)
1326 .unwrap()
1327 .queued_messages()
1328 .checked_len(),
1329 Some(16)
1330 );
1331 assert_eq!(
1332 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1333 Some(*locations.bridge_id())
1334 );
1335 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1336 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1337 assert_eq!(
1338 System::events().last(),
1339 Some(&EventRecord {
1340 phase: Phase::Initialization,
1341 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1342 bridge_id: *locations.bridge_id(),
1343 lane_id: bridge.lane_id.into(),
1344 pruned_messages: 16,
1345 enqueued_messages: 16,
1346 }),
1347 topics: vec![],
1348 }),
1349 );
1350
1351 assert_ok!(XcmOverBridge::close_bridge(
1353 origin.clone(),
1354 Box::new(locations.bridge_destination_universal_location().clone().into()),
1355 8,
1356 ),);
1357
1358 assert_eq!(
1360 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1361 Some(BridgeState::Closed)
1362 );
1363 assert_eq!(
1364 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1365 LaneState::Closed
1366 );
1367 assert_eq!(
1368 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1369 LaneState::Closed
1370 );
1371 assert_eq!(
1372 lanes_manager
1373 .any_state_outbound_lane(bridge.lane_id)
1374 .unwrap()
1375 .queued_messages()
1376 .checked_len(),
1377 Some(8)
1378 );
1379 assert_eq!(
1380 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1381 Some(*locations.bridge_id())
1382 );
1383 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1384 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1385 assert_eq!(
1386 System::events().last(),
1387 Some(&EventRecord {
1388 phase: Phase::Initialization,
1389 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1390 bridge_id: *locations.bridge_id(),
1391 lane_id: bridge.lane_id.into(),
1392 pruned_messages: 8,
1393 enqueued_messages: 8,
1394 }),
1395 topics: vec![],
1396 }),
1397 );
1398
1399 assert_ok!(XcmOverBridge::close_bridge(
1402 origin,
1403 Box::new(locations.bridge_destination_universal_location().clone().into()),
1404 9,
1405 ),);
1406
1407 assert_eq!(
1409 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1410 None
1411 );
1412 assert_eq!(
1413 lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop),
1414 Err(LanesManagerError::UnknownInboundLane)
1415 );
1416 assert_eq!(
1417 lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop),
1418 Err(LanesManagerError::UnknownOutboundLane)
1419 );
1420 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
1421 assert_eq!(
1422 Balances::free_balance(&bridge.bridge_owner_account),
1423 free_balance + reserved_balance
1424 );
1425 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0);
1426 assert_eq!(
1427 System::events().last(),
1428 Some(&EventRecord {
1429 phase: Phase::Initialization,
1430 event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
1431 bridge_id: *locations.bridge_id(),
1432 lane_id: bridge.lane_id.into(),
1433 bridge_deposit: expected_deposit,
1434 pruned_messages: 8,
1435 }),
1436 topics: vec![],
1437 }),
1438 );
1439 });
1440 }
1441
1442 #[test]
1443 fn do_try_state_works() {
1444 let bridge_origin_relative_location = SiblingLocation::get();
1445 let bridge_origin_universal_location = SiblingUniversalLocation::get();
1446 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1447 let bridge_owner_account =
1448 LocationToAccountId::convert_location(&bridge_origin_relative_location)
1449 .expect("valid accountId");
1450 let bridge_owner_account_mismatch =
1451 LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
1452 let bridge_id = BridgeId::new(
1453 &bridge_origin_universal_location,
1454 &bridge_destination_universal_location,
1455 );
1456 let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
1457 let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
1458 let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
1459
1460 let test_bridge_state =
1461 |id,
1462 bridge,
1463 (lane_id, bridge_id),
1464 (inbound_lane_id, outbound_lane_id),
1465 expected_error: Option<TryRuntimeError>| {
1466 Bridges::<TestRuntime, ()>::insert(id, bridge);
1467 LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
1468
1469 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1470 lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
1471 lanes_manager.create_outbound_lane(outbound_lane_id).unwrap();
1472
1473 let result = XcmOverBridge::do_try_state();
1474 if let Some(e) = expected_error {
1475 assert_err!(result, e);
1476 } else {
1477 assert_ok!(result);
1478 }
1479 };
1480 let cleanup = |bridge_id, lane_ids| {
1481 Bridges::<TestRuntime, ()>::remove(bridge_id);
1482 for lane_id in lane_ids {
1483 LaneToBridge::<TestRuntime, ()>::remove(lane_id);
1484 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1485 if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
1486 lane.purge();
1487 }
1488 if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
1489 lane.purge();
1490 }
1491 }
1492 assert_ok!(XcmOverBridge::do_try_state());
1493 };
1494
1495 run_test(|| {
1496 test_bridge_state(
1498 bridge_id,
1499 Bridge {
1500 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1501 bridge_origin_relative_location.clone(),
1502 )),
1503 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1504 bridge_origin_universal_location.clone(),
1505 )),
1506 bridge_destination_universal_location: Box::new(
1507 VersionedInteriorLocation::from(
1508 bridge_destination_universal_location.clone(),
1509 ),
1510 ),
1511 state: BridgeState::Opened,
1512 bridge_owner_account: bridge_owner_account.clone(),
1513 deposit: Zero::zero(),
1514 lane_id,
1515 },
1516 (lane_id, bridge_id),
1517 (lane_id, lane_id),
1518 None,
1519 );
1520 cleanup(bridge_id, vec![lane_id]);
1521
1522 test_bridge_state(
1524 bridge_id,
1525 Bridge {
1526 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1527 bridge_origin_relative_location.clone(),
1528 )),
1529 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1530 bridge_origin_universal_location.clone(),
1531 )),
1532 bridge_destination_universal_location: Box::new(
1533 VersionedInteriorLocation::from(
1534 bridge_destination_universal_location.clone(),
1535 ),
1536 ),
1537 state: BridgeState::Opened,
1538 bridge_owner_account: bridge_owner_account.clone(),
1539 deposit: Zero::zero(),
1540 lane_id,
1541 },
1542 (lane_id, bridge_id_mismatch),
1543 (lane_id, lane_id),
1544 Some(TryRuntimeError::Other(
1545 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
1546 )),
1547 );
1548 cleanup(bridge_id, vec![lane_id]);
1549
1550 test_bridge_state(
1552 bridge_id,
1553 Bridge {
1554 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1555 bridge_origin_relative_location.clone(),
1556 )),
1557 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1558 bridge_origin_universal_location.clone(),
1559 )),
1560 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1561 bridge_destination_universal_location.clone(),
1562 )),
1563 state: BridgeState::Opened,
1564 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1565 deposit: Zero::zero(),
1566 lane_id,
1567 },
1568 (lane_id, bridge_id),
1569 (lane_id, lane_id),
1570 Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
1571 );
1572 cleanup(bridge_id, vec![lane_id]);
1573
1574 test_bridge_state(
1577 bridge_id_mismatch,
1578 Bridge {
1579 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1580 bridge_origin_relative_location.clone(),
1581 )),
1582 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1583 bridge_origin_universal_location.clone(),
1584 )),
1585 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1586 bridge_destination_universal_location.clone(),
1587 )),
1588 state: BridgeState::Opened,
1589 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1590 deposit: Zero::zero(),
1591 lane_id,
1592 },
1593 (lane_id, bridge_id_mismatch),
1594 (lane_id, lane_id),
1595 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!")),
1596 );
1597 cleanup(bridge_id_mismatch, vec![lane_id]);
1598
1599 test_bridge_state(
1601 bridge_id,
1602 Bridge {
1603 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1604 bridge_origin_relative_location.clone(),
1605 )),
1606 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1607 bridge_origin_universal_location.clone(),
1608 )),
1609 bridge_destination_universal_location: Box::new(
1610 VersionedInteriorLocation::from(
1611 bridge_destination_universal_location.clone(),
1612 ),
1613 ),
1614 state: BridgeState::Opened,
1615 bridge_owner_account: bridge_owner_account.clone(),
1616 deposit: Zero::zero(),
1617 lane_id,
1618 },
1619 (lane_id, bridge_id),
1620 (lane_id_mismatch, lane_id),
1621 Some(TryRuntimeError::Other("Inbound lane not found!")),
1622 );
1623 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1624
1625 test_bridge_state(
1627 bridge_id,
1628 Bridge {
1629 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1630 bridge_origin_relative_location.clone(),
1631 )),
1632 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1633 bridge_origin_universal_location.clone(),
1634 )),
1635 bridge_destination_universal_location: Box::new(
1636 VersionedInteriorLocation::from(
1637 bridge_destination_universal_location.clone(),
1638 ),
1639 ),
1640 state: BridgeState::Opened,
1641 bridge_owner_account: bridge_owner_account.clone(),
1642 deposit: Zero::zero(),
1643 lane_id,
1644 },
1645 (lane_id, bridge_id),
1646 (lane_id, lane_id_mismatch),
1647 Some(TryRuntimeError::Other("Outbound lane not found!")),
1648 );
1649 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1650
1651 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1653 assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1654 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1655 cleanup(bridge_id, vec![lane_id]);
1656
1657 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1659 assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1660 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1661 cleanup(bridge_id, vec![lane_id]);
1662 });
1663 }
1664
1665 #[test]
1666 fn ensure_encoding_compatibility() {
1667 use codec::Encode;
1668
1669 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1670 let may_prune_messages = 13;
1671
1672 assert_eq!(
1673 bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge {
1674 bridge_destination_universal_location: Box::new(
1675 bridge_destination_universal_location.clone().into()
1676 )
1677 }
1678 .encode(),
1679 Call::<TestRuntime, ()>::open_bridge {
1680 bridge_destination_universal_location: Box::new(
1681 bridge_destination_universal_location.clone().into()
1682 )
1683 }
1684 .encode()
1685 );
1686 assert_eq!(
1687 bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge {
1688 bridge_destination_universal_location: Box::new(
1689 bridge_destination_universal_location.clone().into()
1690 ),
1691 may_prune_messages,
1692 }
1693 .encode(),
1694 Call::<TestRuntime, ()>::close_bridge {
1695 bridge_destination_universal_location: Box::new(
1696 bridge_destination_universal_location.clone().into()
1697 ),
1698 may_prune_messages,
1699 }
1700 .encode()
1701 );
1702 }
1703}