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 = &(*bridge.bridge_origin_relative_location).try_into()
668 .map_err(|_| "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!")?;
669 let bridge_origin_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_origin_universal_location).try_into()
670 .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
671 let bridge_destination_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_destination_universal_location).try_into()
672 .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
673
674 ensure!(
676 bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest),
677 "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!"
678 );
679
680 ensure!(
682 T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account),
683 "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!"
684 );
685
686 Ok(bridge.lane_id)
687 }
688
689 pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
691 for lane_id in pallet_bridge_messages::InboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
693 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}...");
694 ensure!(
695 LaneToBridge::<T, I>::get(lane_id).is_some(),
696 "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"
697 );
698 }
699
700 for lane_id in pallet_bridge_messages::OutboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
702 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}...");
703 ensure!(
704 LaneToBridge::<T, I>::get(lane_id).is_some(),
705 "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"
706 );
707 }
708
709 Ok(())
710 }
711 }
712
713 #[pallet::storage]
715 pub type Bridges<T: Config<I>, I: 'static = ()> =
716 StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
717 #[pallet::storage]
719 pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
720 StorageMap<_, Identity, T::LaneId, BridgeId>;
721
722 #[pallet::genesis_config]
723 #[derive(DefaultNoBound)]
724 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
725 pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
731 #[serde(skip)]
733 pub _phantom: sp_std::marker::PhantomData<(T, I)>,
734 }
735
736 #[pallet::genesis_build]
737 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I>
738 where
739 T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
740 {
741 fn build(&self) {
742 for (
743 bridge_origin_relative_location,
744 bridge_destination_universal_location,
745 maybe_lane_id,
746 ) in &self.opened_bridges
747 {
748 let locations = Pallet::<T, I>::bridge_locations(
749 bridge_origin_relative_location.clone(),
750 bridge_destination_universal_location.clone().into(),
751 )
752 .expect("Invalid genesis configuration");
753
754 let lane_id = match maybe_lane_id {
755 Some(lane_id) => *lane_id,
756 None =>
757 locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"),
758 };
759
760 Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
761 .expect("Valid opened bridge!");
762 }
763 }
764 }
765
766 #[pallet::event]
767 #[pallet::generate_deposit(pub(super) fn deposit_event)]
768 pub enum Event<T: Config<I>, I: 'static = ()> {
769 BridgeOpened {
771 bridge_id: BridgeId,
773 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
775
776 local_endpoint: Box<InteriorLocation>,
778 remote_endpoint: Box<InteriorLocation>,
780 lane_id: T::LaneId,
782 },
783 ClosingBridge {
785 bridge_id: BridgeId,
787 lane_id: T::LaneId,
789 pruned_messages: MessageNonce,
791 enqueued_messages: MessageNonce,
793 },
794 BridgePruned {
797 bridge_id: BridgeId,
799 lane_id: T::LaneId,
801 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
803 pruned_messages: MessageNonce,
805 },
806 }
807
808 #[pallet::error]
809 pub enum Error<T, I = ()> {
810 BridgeLocations(BridgeLocationsError),
812 InvalidBridgeOriginAccount,
814 BridgeAlreadyExists,
816 TooManyBridgesForLocalOrigin,
818 BridgeAlreadyClosed,
820 LanesManager(LanesManagerError),
822 UnknownBridge,
824 FailedToReserveBridgeDeposit,
826 UnsupportedXcmVersion,
828 }
829}
830
831#[cfg(test)]
832mod tests {
833 use super::*;
834 use bp_messages::LaneIdType;
835 use mock::*;
836
837 use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
838 use frame_system::{EventRecord, Phase};
839 use sp_runtime::TryRuntimeError;
840
841 fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
842 let bridge_owner_account =
843 LocationToAccountId::convert_location(locations.bridge_origin_relative_location())
844 .unwrap();
845 assert_ok!(Balances::mint_into(&bridge_owner_account, balance));
846 bridge_owner_account
847 }
848
849 fn mock_open_bridge_from_with(
850 origin: RuntimeOrigin,
851 deposit: Balance,
852 with: InteriorLocation,
853 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
854 let locations =
855 XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
856 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
857 let bridge_owner_account =
858 fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
859 Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap();
860
861 let bridge = Bridge {
862 bridge_origin_relative_location: Box::new(
863 locations.bridge_origin_relative_location().clone().into(),
864 ),
865 bridge_origin_universal_location: Box::new(
866 locations.bridge_origin_universal_location().clone().into(),
867 ),
868 bridge_destination_universal_location: Box::new(
869 locations.bridge_destination_universal_location().clone().into(),
870 ),
871 state: BridgeState::Opened,
872 bridge_owner_account,
873 deposit,
874 lane_id,
875 };
876 Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
877 LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
878
879 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
880 lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
881 lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
882
883 assert_ok!(XcmOverBridge::do_try_state());
884
885 (bridge, *locations)
886 }
887
888 fn mock_open_bridge_from(
889 origin: RuntimeOrigin,
890 deposit: Balance,
891 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
892 mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
893 }
894
895 fn enqueue_message(lane: TestLaneIdType) {
896 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
897 lanes_manager
898 .active_outbound_lane(lane)
899 .unwrap()
900 .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
901 }
902
903 #[test]
904 fn open_bridge_fails_if_origin_is_not_allowed() {
905 run_test(|| {
906 assert_noop!(
907 XcmOverBridge::open_bridge(
908 OpenBridgeOrigin::disallowed_origin(),
909 Box::new(bridged_asset_hub_universal_location().into()),
910 ),
911 sp_runtime::DispatchError::BadOrigin,
912 );
913 })
914 }
915
916 #[test]
917 fn open_bridge_fails_if_origin_is_not_relative() {
918 run_test(|| {
919 assert_noop!(
920 XcmOverBridge::open_bridge(
921 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
922 Box::new(bridged_asset_hub_universal_location().into()),
923 ),
924 Error::<TestRuntime, ()>::BridgeLocations(
925 BridgeLocationsError::InvalidBridgeOrigin
926 ),
927 );
928
929 assert_noop!(
930 XcmOverBridge::open_bridge(
931 OpenBridgeOrigin::sibling_parachain_universal_origin(),
932 Box::new(bridged_asset_hub_universal_location().into()),
933 ),
934 Error::<TestRuntime, ()>::BridgeLocations(
935 BridgeLocationsError::InvalidBridgeOrigin
936 ),
937 );
938 })
939 }
940
941 #[test]
942 fn open_bridge_fails_if_destination_is_not_remote() {
943 run_test(|| {
944 assert_noop!(
945 XcmOverBridge::open_bridge(
946 OpenBridgeOrigin::parent_relay_chain_origin(),
947 Box::new(
948 [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)]
949 .into()
950 ),
951 ),
952 Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
953 );
954 });
955 }
956
957 #[test]
958 fn open_bridge_fails_if_outside_of_bridged_consensus() {
959 run_test(|| {
960 assert_noop!(
961 XcmOverBridge::open_bridge(
962 OpenBridgeOrigin::parent_relay_chain_origin(),
963 Box::new(
964 [
965 GlobalConsensus(NonBridgedRelayNetwork::get()),
966 Parachain(BRIDGED_ASSET_HUB_ID)
967 ]
968 .into()
969 ),
970 ),
971 Error::<TestRuntime, ()>::BridgeLocations(
972 BridgeLocationsError::UnreachableDestination
973 ),
974 );
975 });
976 }
977
978 #[test]
979 fn open_bridge_fails_if_origin_has_no_sovereign_account() {
980 run_test(|| {
981 assert_noop!(
982 XcmOverBridge::open_bridge(
983 OpenBridgeOrigin::origin_without_sovereign_account(),
984 Box::new(bridged_asset_hub_universal_location().into()),
985 ),
986 Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
987 );
988 });
989 }
990
991 #[test]
992 fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
993 run_test(|| {
994 assert_noop!(
995 XcmOverBridge::open_bridge(
996 OpenBridgeOrigin::sibling_parachain_origin(),
997 Box::new(bridged_asset_hub_universal_location().into()),
998 ),
999 Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
1000 );
1001 });
1002 }
1003
1004 #[test]
1005 fn open_bridge_fails_if_it_already_exists() {
1006 run_test(|| {
1007 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1008 let locations = XcmOverBridge::bridge_locations_from_origin(
1009 origin.clone(),
1010 Box::new(bridged_asset_hub_universal_location().into()),
1011 )
1012 .unwrap();
1013 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1014 fund_origin_sovereign_account(
1015 &locations,
1016 BridgeDeposit::get() + ExistentialDeposit::get(),
1017 );
1018
1019 Bridges::<TestRuntime, ()>::insert(
1020 locations.bridge_id(),
1021 Bridge {
1022 bridge_origin_relative_location: Box::new(
1023 locations.bridge_origin_relative_location().clone().into(),
1024 ),
1025 bridge_origin_universal_location: Box::new(
1026 locations.bridge_origin_universal_location().clone().into(),
1027 ),
1028 bridge_destination_universal_location: Box::new(
1029 locations.bridge_destination_universal_location().clone().into(),
1030 ),
1031 state: BridgeState::Opened,
1032 bridge_owner_account: [0u8; 32].into(),
1033 deposit: 0,
1034 lane_id,
1035 },
1036 );
1037
1038 assert_noop!(
1039 XcmOverBridge::open_bridge(
1040 origin,
1041 Box::new(bridged_asset_hub_universal_location().into()),
1042 ),
1043 Error::<TestRuntime, ()>::BridgeAlreadyExists,
1044 );
1045 })
1046 }
1047
1048 #[test]
1049 fn open_bridge_fails_if_its_lanes_already_exists() {
1050 run_test(|| {
1051 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1052 let locations = XcmOverBridge::bridge_locations_from_origin(
1053 origin.clone(),
1054 Box::new(bridged_asset_hub_universal_location().into()),
1055 )
1056 .unwrap();
1057 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1058 fund_origin_sovereign_account(
1059 &locations,
1060 BridgeDeposit::get() + ExistentialDeposit::get(),
1061 );
1062
1063 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1064
1065 lanes_manager.create_inbound_lane(lane_id).unwrap();
1066 assert_noop!(
1067 XcmOverBridge::open_bridge(
1068 origin.clone(),
1069 Box::new(bridged_asset_hub_universal_location().into()),
1070 ),
1071 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
1072 );
1073
1074 lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
1075 lanes_manager.create_outbound_lane(lane_id).unwrap();
1076 assert_noop!(
1077 XcmOverBridge::open_bridge(
1078 origin,
1079 Box::new(bridged_asset_hub_universal_location().into()),
1080 ),
1081 Error::<TestRuntime, ()>::LanesManager(
1082 LanesManagerError::OutboundLaneAlreadyExists
1083 ),
1084 );
1085 })
1086 }
1087
1088 #[test]
1089 fn open_bridge_works() {
1090 run_test(|| {
1091 let origins = [
1094 (OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1095 (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1096 ];
1097
1098 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1100 let existential_deposit = ExistentialDeposit::get();
1101 for (origin, expected_deposit) in origins {
1102 System::set_block_number(1);
1104 System::reset_events();
1105
1106 let xcm_version = xcm::latest::VERSION;
1108 let locations = XcmOverBridge::bridge_locations_from_origin(
1109 origin.clone(),
1110 Box::new(
1111 VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
1112 .into_version(xcm_version)
1113 .expect("valid conversion"),
1114 ),
1115 )
1116 .unwrap();
1117 let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
1118
1119 assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
1121 assert_eq!(
1122 lanes_manager.active_inbound_lane(lane_id).map(drop),
1123 Err(LanesManagerError::UnknownInboundLane)
1124 );
1125 assert_eq!(
1126 lanes_manager.active_outbound_lane(lane_id).map(drop),
1127 Err(LanesManagerError::UnknownOutboundLane)
1128 );
1129 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
1130
1131 let bridge_owner_account = fund_origin_sovereign_account(
1133 &locations,
1134 expected_deposit + existential_deposit,
1135 );
1136 assert_eq!(
1137 Balances::free_balance(&bridge_owner_account),
1138 expected_deposit + existential_deposit
1139 );
1140 assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
1141
1142 assert_ok!(XcmOverBridge::open_bridge(
1144 origin,
1145 Box::new(locations.bridge_destination_universal_location().clone().into()),
1146 ));
1147
1148 assert_eq!(
1150 Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
1151 Some(Bridge {
1152 bridge_origin_relative_location: Box::new(
1153 locations.bridge_origin_relative_location().clone().into()
1154 ),
1155 bridge_origin_universal_location: Box::new(
1156 locations.bridge_origin_universal_location().clone().into(),
1157 ),
1158 bridge_destination_universal_location: Box::new(
1159 locations.bridge_destination_universal_location().clone().into(),
1160 ),
1161 state: BridgeState::Opened,
1162 bridge_owner_account: bridge_owner_account.clone(),
1163 deposit: expected_deposit,
1164 lane_id
1165 }),
1166 );
1167 assert_eq!(
1168 lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()),
1169 Ok(LaneState::Opened)
1170 );
1171 assert_eq!(
1172 lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()),
1173 Ok(LaneState::Opened)
1174 );
1175 assert_eq!(
1176 LaneToBridge::<TestRuntime, ()>::get(lane_id),
1177 Some(*locations.bridge_id())
1178 );
1179 assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit);
1180 assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit);
1181
1182 assert_eq!(
1184 System::events().last(),
1185 Some(&EventRecord {
1186 phase: Phase::Initialization,
1187 event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
1188 bridge_id: *locations.bridge_id(),
1189 bridge_deposit: expected_deposit,
1190 local_endpoint: Box::new(
1191 locations.bridge_origin_universal_location().clone()
1192 ),
1193 remote_endpoint: Box::new(
1194 locations.bridge_destination_universal_location().clone()
1195 ),
1196 lane_id: lane_id.into()
1197 }),
1198 topics: vec![],
1199 }),
1200 );
1201
1202 assert_ok!(XcmOverBridge::do_try_state());
1204 }
1205 });
1206 }
1207
1208 #[test]
1209 fn close_bridge_fails_if_origin_is_not_allowed() {
1210 run_test(|| {
1211 assert_noop!(
1212 XcmOverBridge::close_bridge(
1213 OpenBridgeOrigin::disallowed_origin(),
1214 Box::new(bridged_asset_hub_universal_location().into()),
1215 0,
1216 ),
1217 sp_runtime::DispatchError::BadOrigin,
1218 );
1219 })
1220 }
1221
1222 #[test]
1223 fn close_bridge_fails_if_origin_is_not_relative() {
1224 run_test(|| {
1225 assert_noop!(
1226 XcmOverBridge::close_bridge(
1227 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
1228 Box::new(bridged_asset_hub_universal_location().into()),
1229 0,
1230 ),
1231 Error::<TestRuntime, ()>::BridgeLocations(
1232 BridgeLocationsError::InvalidBridgeOrigin
1233 ),
1234 );
1235
1236 assert_noop!(
1237 XcmOverBridge::close_bridge(
1238 OpenBridgeOrigin::sibling_parachain_universal_origin(),
1239 Box::new(bridged_asset_hub_universal_location().into()),
1240 0,
1241 ),
1242 Error::<TestRuntime, ()>::BridgeLocations(
1243 BridgeLocationsError::InvalidBridgeOrigin
1244 ),
1245 );
1246 })
1247 }
1248
1249 #[test]
1250 fn close_bridge_fails_if_its_lanes_are_unknown() {
1251 run_test(|| {
1252 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1253 let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0);
1254
1255 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1256 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge();
1257 assert_noop!(
1258 XcmOverBridge::close_bridge(
1259 origin.clone(),
1260 Box::new(locations.bridge_destination_universal_location().clone().into()),
1261 0,
1262 ),
1263 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
1264 );
1265 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1266
1267 let (_, locations) = mock_open_bridge_from(origin.clone(), 0);
1268 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1269 assert_noop!(
1270 XcmOverBridge::close_bridge(
1271 origin,
1272 Box::new(locations.bridge_destination_universal_location().clone().into()),
1273 0,
1274 ),
1275 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
1276 );
1277 });
1278 }
1279
1280 #[test]
1281 fn close_bridge_works() {
1282 run_test(|| {
1283 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1284 let expected_deposit = BridgeDeposit::get();
1285 let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit);
1286 System::set_block_number(1);
1287
1288 let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1290 let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1291
1292 for _ in 0..32 {
1294 enqueue_message(bridge.lane_id);
1295 }
1296
1297 assert_ok!(XcmOverBridge::close_bridge(
1299 origin.clone(),
1300 Box::new(locations.bridge_destination_universal_location().clone().into()),
1301 16,
1302 ),);
1303
1304 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1307 assert_eq!(
1308 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1309 Some(BridgeState::Closed)
1310 );
1311 assert_eq!(
1312 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1313 LaneState::Closed
1314 );
1315 assert_eq!(
1316 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1317 LaneState::Closed
1318 );
1319 assert_eq!(
1320 lanes_manager
1321 .any_state_outbound_lane(bridge.lane_id)
1322 .unwrap()
1323 .queued_messages()
1324 .checked_len(),
1325 Some(16)
1326 );
1327 assert_eq!(
1328 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1329 Some(*locations.bridge_id())
1330 );
1331 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1332 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1333 assert_eq!(
1334 System::events().last(),
1335 Some(&EventRecord {
1336 phase: Phase::Initialization,
1337 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1338 bridge_id: *locations.bridge_id(),
1339 lane_id: bridge.lane_id.into(),
1340 pruned_messages: 16,
1341 enqueued_messages: 16,
1342 }),
1343 topics: vec![],
1344 }),
1345 );
1346
1347 assert_ok!(XcmOverBridge::close_bridge(
1349 origin.clone(),
1350 Box::new(locations.bridge_destination_universal_location().clone().into()),
1351 8,
1352 ),);
1353
1354 assert_eq!(
1356 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1357 Some(BridgeState::Closed)
1358 );
1359 assert_eq!(
1360 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1361 LaneState::Closed
1362 );
1363 assert_eq!(
1364 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1365 LaneState::Closed
1366 );
1367 assert_eq!(
1368 lanes_manager
1369 .any_state_outbound_lane(bridge.lane_id)
1370 .unwrap()
1371 .queued_messages()
1372 .checked_len(),
1373 Some(8)
1374 );
1375 assert_eq!(
1376 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1377 Some(*locations.bridge_id())
1378 );
1379 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1380 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1381 assert_eq!(
1382 System::events().last(),
1383 Some(&EventRecord {
1384 phase: Phase::Initialization,
1385 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1386 bridge_id: *locations.bridge_id(),
1387 lane_id: bridge.lane_id.into(),
1388 pruned_messages: 8,
1389 enqueued_messages: 8,
1390 }),
1391 topics: vec![],
1392 }),
1393 );
1394
1395 assert_ok!(XcmOverBridge::close_bridge(
1398 origin,
1399 Box::new(locations.bridge_destination_universal_location().clone().into()),
1400 9,
1401 ),);
1402
1403 assert_eq!(
1405 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1406 None
1407 );
1408 assert_eq!(
1409 lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop),
1410 Err(LanesManagerError::UnknownInboundLane)
1411 );
1412 assert_eq!(
1413 lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop),
1414 Err(LanesManagerError::UnknownOutboundLane)
1415 );
1416 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
1417 assert_eq!(
1418 Balances::free_balance(&bridge.bridge_owner_account),
1419 free_balance + reserved_balance
1420 );
1421 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0);
1422 assert_eq!(
1423 System::events().last(),
1424 Some(&EventRecord {
1425 phase: Phase::Initialization,
1426 event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
1427 bridge_id: *locations.bridge_id(),
1428 lane_id: bridge.lane_id.into(),
1429 bridge_deposit: expected_deposit,
1430 pruned_messages: 8,
1431 }),
1432 topics: vec![],
1433 }),
1434 );
1435 });
1436 }
1437
1438 #[test]
1439 fn do_try_state_works() {
1440 let bridge_origin_relative_location = SiblingLocation::get();
1441 let bridge_origin_universal_location = SiblingUniversalLocation::get();
1442 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1443 let bridge_owner_account =
1444 LocationToAccountId::convert_location(&bridge_origin_relative_location)
1445 .expect("valid accountId");
1446 let bridge_owner_account_mismatch =
1447 LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
1448 let bridge_id = BridgeId::new(
1449 &bridge_origin_universal_location,
1450 &bridge_destination_universal_location,
1451 );
1452 let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
1453 let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
1454 let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
1455
1456 let test_bridge_state =
1457 |id,
1458 bridge,
1459 (lane_id, bridge_id),
1460 (inbound_lane_id, outbound_lane_id),
1461 expected_error: Option<TryRuntimeError>| {
1462 Bridges::<TestRuntime, ()>::insert(id, bridge);
1463 LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
1464
1465 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1466 lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
1467 lanes_manager.create_outbound_lane(outbound_lane_id).unwrap();
1468
1469 let result = XcmOverBridge::do_try_state();
1470 if let Some(e) = expected_error {
1471 assert_err!(result, e);
1472 } else {
1473 assert_ok!(result);
1474 }
1475 };
1476 let cleanup = |bridge_id, lane_ids| {
1477 Bridges::<TestRuntime, ()>::remove(bridge_id);
1478 for lane_id in lane_ids {
1479 LaneToBridge::<TestRuntime, ()>::remove(lane_id);
1480 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1481 if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
1482 lane.purge();
1483 }
1484 if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
1485 lane.purge();
1486 }
1487 }
1488 assert_ok!(XcmOverBridge::do_try_state());
1489 };
1490
1491 run_test(|| {
1492 test_bridge_state(
1494 bridge_id,
1495 Bridge {
1496 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1497 bridge_origin_relative_location.clone(),
1498 )),
1499 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1500 bridge_origin_universal_location.clone(),
1501 )),
1502 bridge_destination_universal_location: Box::new(
1503 VersionedInteriorLocation::from(
1504 bridge_destination_universal_location.clone(),
1505 ),
1506 ),
1507 state: BridgeState::Opened,
1508 bridge_owner_account: bridge_owner_account.clone(),
1509 deposit: Zero::zero(),
1510 lane_id,
1511 },
1512 (lane_id, bridge_id),
1513 (lane_id, lane_id),
1514 None,
1515 );
1516 cleanup(bridge_id, vec![lane_id]);
1517
1518 test_bridge_state(
1520 bridge_id,
1521 Bridge {
1522 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1523 bridge_origin_relative_location.clone(),
1524 )),
1525 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1526 bridge_origin_universal_location.clone(),
1527 )),
1528 bridge_destination_universal_location: Box::new(
1529 VersionedInteriorLocation::from(
1530 bridge_destination_universal_location.clone(),
1531 ),
1532 ),
1533 state: BridgeState::Opened,
1534 bridge_owner_account: bridge_owner_account.clone(),
1535 deposit: Zero::zero(),
1536 lane_id,
1537 },
1538 (lane_id, bridge_id_mismatch),
1539 (lane_id, lane_id),
1540 Some(TryRuntimeError::Other(
1541 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
1542 )),
1543 );
1544 cleanup(bridge_id, vec![lane_id]);
1545
1546 test_bridge_state(
1548 bridge_id,
1549 Bridge {
1550 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1551 bridge_origin_relative_location.clone(),
1552 )),
1553 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1554 bridge_origin_universal_location.clone(),
1555 )),
1556 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1557 bridge_destination_universal_location.clone(),
1558 )),
1559 state: BridgeState::Opened,
1560 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1561 deposit: Zero::zero(),
1562 lane_id,
1563 },
1564 (lane_id, bridge_id),
1565 (lane_id, lane_id),
1566 Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
1567 );
1568 cleanup(bridge_id, vec![lane_id]);
1569
1570 test_bridge_state(
1573 bridge_id_mismatch,
1574 Bridge {
1575 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1576 bridge_origin_relative_location.clone(),
1577 )),
1578 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1579 bridge_origin_universal_location.clone(),
1580 )),
1581 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1582 bridge_destination_universal_location.clone(),
1583 )),
1584 state: BridgeState::Opened,
1585 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1586 deposit: Zero::zero(),
1587 lane_id,
1588 },
1589 (lane_id, bridge_id_mismatch),
1590 (lane_id, lane_id),
1591 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!")),
1592 );
1593 cleanup(bridge_id_mismatch, vec![lane_id]);
1594
1595 test_bridge_state(
1597 bridge_id,
1598 Bridge {
1599 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1600 bridge_origin_relative_location.clone(),
1601 )),
1602 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1603 bridge_origin_universal_location.clone(),
1604 )),
1605 bridge_destination_universal_location: Box::new(
1606 VersionedInteriorLocation::from(
1607 bridge_destination_universal_location.clone(),
1608 ),
1609 ),
1610 state: BridgeState::Opened,
1611 bridge_owner_account: bridge_owner_account.clone(),
1612 deposit: Zero::zero(),
1613 lane_id,
1614 },
1615 (lane_id, bridge_id),
1616 (lane_id_mismatch, lane_id),
1617 Some(TryRuntimeError::Other("Inbound lane not found!")),
1618 );
1619 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1620
1621 test_bridge_state(
1623 bridge_id,
1624 Bridge {
1625 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1626 bridge_origin_relative_location.clone(),
1627 )),
1628 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1629 bridge_origin_universal_location.clone(),
1630 )),
1631 bridge_destination_universal_location: Box::new(
1632 VersionedInteriorLocation::from(
1633 bridge_destination_universal_location.clone(),
1634 ),
1635 ),
1636 state: BridgeState::Opened,
1637 bridge_owner_account: bridge_owner_account.clone(),
1638 deposit: Zero::zero(),
1639 lane_id,
1640 },
1641 (lane_id, bridge_id),
1642 (lane_id, lane_id_mismatch),
1643 Some(TryRuntimeError::Other("Outbound lane not found!")),
1644 );
1645 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1646
1647 test_bridge_state(
1649 bridge_id,
1650 Bridge {
1651 bridge_origin_relative_location: Box::new(
1652 VersionedLocation::from(bridge_origin_relative_location.clone())
1653 .into_version(XCM_VERSION - 1)
1654 .unwrap(),
1655 ),
1656 bridge_origin_universal_location: Box::new(
1657 VersionedInteriorLocation::from(bridge_origin_universal_location.clone())
1658 .into_version(XCM_VERSION - 1)
1659 .unwrap(),
1660 ),
1661 bridge_destination_universal_location: Box::new(
1662 VersionedInteriorLocation::from(
1663 bridge_destination_universal_location.clone(),
1664 )
1665 .into_version(XCM_VERSION - 1)
1666 .unwrap(),
1667 ),
1668 state: BridgeState::Opened,
1669 bridge_owner_account: bridge_owner_account.clone(),
1670 deposit: Zero::zero(),
1671 lane_id,
1672 },
1673 (lane_id, bridge_id),
1674 (lane_id, lane_id),
1675 None,
1676 );
1677 cleanup(bridge_id, vec![lane_id]);
1678
1679 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1681 assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1682 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1683 cleanup(bridge_id, vec![lane_id]);
1684
1685 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1687 assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1688 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1689 cleanup(bridge_id, vec![lane_id]);
1690 });
1691 }
1692
1693 #[test]
1694 fn ensure_encoding_compatibility() {
1695 use codec::Encode;
1696
1697 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1698 let may_prune_messages = 13;
1699
1700 assert_eq!(
1701 bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge {
1702 bridge_destination_universal_location: Box::new(
1703 bridge_destination_universal_location.clone().into()
1704 )
1705 }
1706 .encode(),
1707 Call::<TestRuntime, ()>::open_bridge {
1708 bridge_destination_universal_location: Box::new(
1709 bridge_destination_universal_location.clone().into()
1710 )
1711 }
1712 .encode()
1713 );
1714 assert_eq!(
1715 bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge {
1716 bridge_destination_universal_location: Box::new(
1717 bridge_destination_universal_location.clone().into()
1718 ),
1719 may_prune_messages,
1720 }
1721 .encode(),
1722 Call::<TestRuntime, ()>::close_bridge {
1723 bridge_destination_universal_location: Box::new(
1724 bridge_destination_universal_location.clone().into()
1725 ),
1726 may_prune_messages,
1727 }
1728 .encode()
1729 );
1730 }
1731}