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