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 .map_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 e
429 })
430 .ok()
431 .unwrap_or(BalanceOf::<ThisChainOf<T, I>>::zero());
432
433 log::trace!(
435 target: LOG_TARGET,
436 "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned",
437 locations.bridge_id(),
438 bridge.lane_id,
439 locations.bridge_origin_universal_location(),
440 locations.bridge_destination_universal_location(),
441 );
442
443 Self::deposit_event(Event::<T, I>::BridgePruned {
445 bridge_id: *locations.bridge_id(),
446 lane_id: bridge.lane_id.into(),
447 bridge_deposit: released_deposit,
448 pruned_messages,
449 });
450
451 Ok(())
452 }
453 }
454
455 impl<T: Config<I>, I: 'static> Pallet<T, I> {
456 pub fn do_open_bridge(
458 locations: Box<BridgeLocations>,
459 lane_id: T::LaneId,
460 create_lanes: bool,
461 ) -> Result<(), DispatchError> {
462 let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
464 locations.bridge_origin_relative_location(),
465 )
466 .ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
467 let deposit = if T::AllowWithoutBridgeDeposit::contains(
468 locations.bridge_origin_relative_location(),
469 ) {
470 BalanceOf::<ThisChainOf<T, I>>::zero()
471 } else {
472 let deposit = T::BridgeDeposit::get();
473 T::Currency::hold(
474 &HoldReason::BridgeDeposit.into(),
475 &bridge_owner_account,
476 deposit,
477 )
478 .map_err(|e| {
479 log::error!(
480 target: LOG_TARGET,
481 "Failed to hold bridge deposit: {deposit:?} \
482 from bridge_owner_account: {bridge_owner_account:?} derived from \
483 bridge_origin_relative_location: {:?} with error: {e:?}",
484 locations.bridge_origin_relative_location(),
485 );
486 Error::<T, I>::FailedToReserveBridgeDeposit
487 })?;
488 deposit
489 };
490
491 Bridges::<T, I>::try_mutate(locations.bridge_id(), |bridge| match bridge {
493 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
494 None => {
495 *bridge = Some(BridgeOf::<T, I> {
496 bridge_origin_relative_location: Box::new(
497 locations.bridge_origin_relative_location().clone().into(),
498 ),
499 bridge_origin_universal_location: Box::new(
500 locations.bridge_origin_universal_location().clone().into(),
501 ),
502 bridge_destination_universal_location: Box::new(
503 locations.bridge_destination_universal_location().clone().into(),
504 ),
505 state: BridgeState::Opened,
506 bridge_owner_account,
507 deposit,
508 lane_id,
509 });
510 Ok(())
511 },
512 })?;
513 LaneToBridge::<T, I>::try_mutate(lane_id, |bridge| match bridge {
515 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
516 None => {
517 *bridge = Some(*locations.bridge_id());
518 Ok(())
519 },
520 })?;
521
522 if create_lanes {
523 let lanes_manager = LanesManagerOf::<T, I>::new();
525 lanes_manager
526 .create_inbound_lane(lane_id)
527 .map_err(Error::<T, I>::LanesManager)?;
528 lanes_manager
529 .create_outbound_lane(lane_id)
530 .map_err(Error::<T, I>::LanesManager)?;
531 }
532
533 log::trace!(
535 target: LOG_TARGET,
536 "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}",
537 locations.bridge_id(),
538 locations.bridge_origin_universal_location(),
539 locations.bridge_destination_universal_location(),
540 );
541
542 Self::deposit_event(Event::<T, I>::BridgeOpened {
544 bridge_id: *locations.bridge_id(),
545 bridge_deposit: deposit,
546 local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()),
547 remote_endpoint: Box::new(
548 locations.bridge_destination_universal_location().clone(),
549 ),
550 lane_id: lane_id.into(),
551 });
552
553 Ok(())
554 }
555 }
556
557 impl<T: Config<I>, I: 'static> Pallet<T, I> {
558 pub fn bridge_locations_from_origin(
562 origin: OriginFor<T>,
563 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
564 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
565 Self::bridge_locations(
566 T::OpenBridgeOrigin::ensure_origin(origin)?,
567 (*bridge_destination_universal_location)
568 .try_into()
569 .map_err(|_| Error::<T, I>::UnsupportedXcmVersion)?,
570 )
571 }
572
573 pub fn bridge_locations(
575 bridge_origin_relative_location: Location,
576 bridge_destination_universal_location: InteriorLocation,
577 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
578 BridgeLocations::bridge_locations(
579 T::UniversalLocation::get(),
580 bridge_origin_relative_location,
581 bridge_destination_universal_location,
582 Self::bridged_network_id()?,
583 )
584 .map_err(|e| {
585 log::trace!(
586 target: LOG_TARGET,
587 "bridge_locations error: {e:?}",
588 );
589 Error::<T, I>::BridgeLocations(e).into()
590 })
591 }
592
593 pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
595 Bridges::<T, I>::get(bridge_id)
596 }
597
598 pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
600 LaneToBridge::<T, I>::get(lane_id)
601 .and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
602 }
603 }
604
605 impl<T: Config<I>, I: 'static> Pallet<T, I> {
606 fn bridged_network_id() -> Result<NetworkId, sp_runtime::DispatchError> {
608 match T::BridgedNetwork::get().take_first_interior() {
609 Some(GlobalConsensus(network)) => Ok(network),
610 _ => Err(Error::<T, I>::BridgeLocations(
611 BridgeLocationsError::InvalidBridgeDestination,
612 )
613 .into()),
614 }
615 }
616 }
617
618 #[cfg(any(test, feature = "try-runtime", feature = "std"))]
619 impl<T: Config<I>, I: 'static> Pallet<T, I> {
620 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
622 use sp_std::collections::btree_set::BTreeSet;
623
624 let mut lanes = BTreeSet::new();
625
626 for (bridge_id, bridge) in Bridges::<T, I>::iter() {
628 lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?);
629 }
630 ensure!(
631 lanes.len() == Bridges::<T, I>::iter().count(),
632 "Invalid `Bridges` configuration, probably two bridges handle the same laneId!"
633 );
634 ensure!(
635 lanes.len() == LaneToBridge::<T, I>::iter().count(),
636 "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!"
637 );
638
639 Self::do_try_state_for_messages()
641 }
642
643 pub fn do_try_state_for_bridge(
645 bridge_id: BridgeId,
646 bridge: BridgeOf<T, I>,
647 ) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
648 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}");
649
650 ensure!(
652 Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
653 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
654 );
655
656 let lanes_manager = LanesManagerOf::<T, I>::new();
658 ensure!(
659 lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(),
660 "Inbound lane not found!",
661 );
662 ensure!(
663 lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(),
664 "Outbound lane not found!",
665 );
666
667 let bridge_origin_relative_location_as_latest: &Location =
669 bridge.bridge_origin_relative_location.try_as().map_err(|_| {
670 "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!"
671 })?;
672 let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location
673 .try_as()
674 .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
675 let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location
676 .try_as()
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 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}...");
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 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}...");
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 = |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}