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 log::trace!(
309 target: LOG_TARGET,
310 "calculate_lane_id error: {e:?}",
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 log::trace!(
388 target: LOG_TARGET,
389 "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining",
390 locations.bridge_id(),
391 locations.bridge_origin_universal_location(),
392 locations.bridge_destination_universal_location(),
393 bridge.lane_id,
394 enqueued_messages,
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 log::error!(
425 target: LOG_TARGET,
426 "Failed to unreserve during the bridge {:?} closure with error: {e:?}",
427 locations.bridge_id(),
428 );
429 })
430 .ok()
431 .unwrap_or(BalanceOf::<ThisChainOf<T, I>>::zero());
432
433 log::trace!(
435 target: LOG_TARGET,
436 "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned",
437 locations.bridge_id(),
438 bridge.lane_id,
439 locations.bridge_origin_universal_location(),
440 locations.bridge_destination_universal_location(),
441 );
442
443 Self::deposit_event(Event::<T, I>::BridgePruned {
445 bridge_id: *locations.bridge_id(),
446 lane_id: bridge.lane_id.into(),
447 bridge_deposit: released_deposit,
448 pruned_messages,
449 });
450
451 Ok(())
452 }
453 }
454
455 impl<T: Config<I>, I: 'static> Pallet<T, I> {
456 pub fn do_open_bridge(
458 locations: Box<BridgeLocations>,
459 lane_id: T::LaneId,
460 create_lanes: bool,
461 ) -> Result<(), DispatchError> {
462 let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location(
464 locations.bridge_origin_relative_location(),
465 )
466 .ok_or(Error::<T, I>::InvalidBridgeOriginAccount)?;
467 let deposit = if T::AllowWithoutBridgeDeposit::contains(
468 locations.bridge_origin_relative_location(),
469 ) {
470 BalanceOf::<ThisChainOf<T, I>>::zero()
471 } else {
472 let deposit = T::BridgeDeposit::get();
473 T::Currency::hold(
474 &HoldReason::BridgeDeposit.into(),
475 &bridge_owner_account,
476 deposit,
477 )
478 .map_err(|e| {
479 log::error!(
480 target: LOG_TARGET,
481 "Failed to hold bridge deposit: {deposit:?} \
482 from bridge_owner_account: {bridge_owner_account:?} derived from \
483 bridge_origin_relative_location: {:?} with error: {e:?}",
484 locations.bridge_origin_relative_location(),
485 );
486 Error::<T, I>::FailedToReserveBridgeDeposit
487 })?;
488 deposit
489 };
490
491 Bridges::<T, I>::try_mutate(locations.bridge_id(), |bridge| match bridge {
493 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
494 None => {
495 *bridge = Some(BridgeOf::<T, I> {
496 bridge_origin_relative_location: Box::new(
497 locations.bridge_origin_relative_location().clone().into(),
498 ),
499 bridge_origin_universal_location: Box::new(
500 locations.bridge_origin_universal_location().clone().into(),
501 ),
502 bridge_destination_universal_location: Box::new(
503 locations.bridge_destination_universal_location().clone().into(),
504 ),
505 state: BridgeState::Opened,
506 bridge_owner_account,
507 deposit,
508 lane_id,
509 });
510 Ok(())
511 },
512 })?;
513 LaneToBridge::<T, I>::try_mutate(lane_id, |bridge| match bridge {
515 Some(_) => Err(Error::<T, I>::BridgeAlreadyExists),
516 None => {
517 *bridge = Some(*locations.bridge_id());
518 Ok(())
519 },
520 })?;
521
522 if create_lanes {
523 let lanes_manager = LanesManagerOf::<T, I>::new();
525 lanes_manager
526 .create_inbound_lane(lane_id)
527 .map_err(Error::<T, I>::LanesManager)?;
528 lanes_manager
529 .create_outbound_lane(lane_id)
530 .map_err(Error::<T, I>::LanesManager)?;
531 }
532
533 log::trace!(
535 target: LOG_TARGET,
536 "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}",
537 locations.bridge_id(),
538 locations.bridge_origin_universal_location(),
539 locations.bridge_destination_universal_location(),
540 );
541
542 Self::deposit_event(Event::<T, I>::BridgeOpened {
544 bridge_id: *locations.bridge_id(),
545 bridge_deposit: deposit,
546 local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()),
547 remote_endpoint: Box::new(
548 locations.bridge_destination_universal_location().clone(),
549 ),
550 lane_id: lane_id.into(),
551 });
552
553 Ok(())
554 }
555 }
556
557 impl<T: Config<I>, I: 'static> Pallet<T, I> {
558 pub fn bridge_locations_from_origin(
562 origin: OriginFor<T>,
563 bridge_destination_universal_location: Box<VersionedInteriorLocation>,
564 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
565 Self::bridge_locations(
566 T::OpenBridgeOrigin::ensure_origin(origin)?,
567 (*bridge_destination_universal_location)
568 .try_into()
569 .map_err(|_| Error::<T, I>::UnsupportedXcmVersion)?,
570 )
571 }
572
573 pub fn bridge_locations(
575 bridge_origin_relative_location: Location,
576 bridge_destination_universal_location: InteriorLocation,
577 ) -> Result<Box<BridgeLocations>, sp_runtime::DispatchError> {
578 BridgeLocations::bridge_locations(
579 T::UniversalLocation::get(),
580 bridge_origin_relative_location,
581 bridge_destination_universal_location,
582 Self::bridged_network_id()?,
583 )
584 .map_err(|e| {
585 log::trace!(
586 target: LOG_TARGET,
587 "bridge_locations error: {e:?}",
588 );
589 Error::<T, I>::BridgeLocations(e).into()
590 })
591 }
592
593 pub fn bridge(bridge_id: &BridgeId) -> Option<BridgeOf<T, I>> {
595 Bridges::<T, I>::get(bridge_id)
596 }
597
598 pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf<T, I>)> {
600 LaneToBridge::<T, I>::get(lane_id)
601 .and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge)))
602 }
603 }
604
605 impl<T: Config<I>, I: 'static> Pallet<T, I> {
606 fn bridged_network_id() -> Result<NetworkId, sp_runtime::DispatchError> {
608 match T::BridgedNetwork::get().take_first_interior() {
609 Some(GlobalConsensus(network)) => Ok(network),
610 _ => Err(Error::<T, I>::BridgeLocations(
611 BridgeLocationsError::InvalidBridgeDestination,
612 )
613 .into()),
614 }
615 }
616 }
617
618 #[cfg(any(test, feature = "try-runtime", feature = "std"))]
619 impl<T: Config<I>, I: 'static> Pallet<T, I> {
620 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
622 use sp_std::collections::btree_set::BTreeSet;
623
624 let mut lanes = BTreeSet::new();
625
626 for (bridge_id, bridge) in Bridges::<T, I>::iter() {
628 lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?);
629 }
630 ensure!(
631 lanes.len() == Bridges::<T, I>::iter().count(),
632 "Invalid `Bridges` configuration, probably two bridges handle the same laneId!"
633 );
634 ensure!(
635 lanes.len() == LaneToBridge::<T, I>::iter().count(),
636 "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!"
637 );
638
639 Self::do_try_state_for_messages()
641 }
642
643 pub fn do_try_state_for_bridge(
645 bridge_id: BridgeId,
646 bridge: BridgeOf<T, I>,
647 ) -> Result<T::LaneId, sp_runtime::TryRuntimeError> {
648 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}");
649
650 ensure!(
652 Some(bridge_id) == LaneToBridge::<T, I>::get(bridge.lane_id),
653 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!"
654 );
655
656 let lanes_manager = LanesManagerOf::<T, I>::new();
658 ensure!(
659 lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(),
660 "Inbound lane not found!",
661 );
662 ensure!(
663 lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(),
664 "Outbound lane not found!",
665 );
666
667 let bridge_origin_relative_location_as_latest: &Location = &(*bridge.bridge_origin_relative_location).try_into()
669 .map_err(|_| "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!")?;
670 let bridge_origin_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_origin_universal_location).try_into()
671 .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
672 let bridge_destination_universal_location_as_latest: &InteriorLocation = &(*bridge.bridge_destination_universal_location).try_into()
673 .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?;
674
675 ensure!(
677 bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest),
678 "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!"
679 );
680
681 ensure!(
683 T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account),
684 "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!"
685 );
686
687 Ok(bridge.lane_id)
688 }
689
690 pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> {
692 for lane_id in pallet_bridge_messages::InboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
694 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}...");
695 ensure!(
696 LaneToBridge::<T, I>::get(lane_id).is_some(),
697 "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"
698 );
699 }
700
701 for lane_id in pallet_bridge_messages::OutboundLanes::<T, T::BridgeMessagesPalletInstance>::iter_keys() {
703 log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}...");
704 ensure!(
705 LaneToBridge::<T, I>::get(lane_id).is_some(),
706 "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"
707 );
708 }
709
710 Ok(())
711 }
712 }
713
714 #[pallet::storage]
716 pub type Bridges<T: Config<I>, I: 'static = ()> =
717 StorageMap<_, Identity, BridgeId, BridgeOf<T, I>>;
718 #[pallet::storage]
720 pub type LaneToBridge<T: Config<I>, I: 'static = ()> =
721 StorageMap<_, Identity, T::LaneId, BridgeId>;
722
723 #[pallet::genesis_config]
724 #[derive(DefaultNoBound)]
725 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
726 pub opened_bridges: Vec<(Location, InteriorLocation, Option<T::LaneId>)>,
732 #[serde(skip)]
734 pub _phantom: sp_std::marker::PhantomData<(T, I)>,
735 }
736
737 #[pallet::genesis_build]
738 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I>
739 where
740 T: frame_system::Config<AccountId = AccountIdOf<ThisChainOf<T, I>>>,
741 {
742 fn build(&self) {
743 for (
744 bridge_origin_relative_location,
745 bridge_destination_universal_location,
746 maybe_lane_id,
747 ) in &self.opened_bridges
748 {
749 let locations = Pallet::<T, I>::bridge_locations(
750 bridge_origin_relative_location.clone(),
751 bridge_destination_universal_location.clone().into(),
752 )
753 .expect("Invalid genesis configuration");
754
755 let lane_id = match maybe_lane_id {
756 Some(lane_id) => *lane_id,
757 None =>
758 locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"),
759 };
760
761 Pallet::<T, I>::do_open_bridge(locations, lane_id, true)
762 .expect("Valid opened bridge!");
763 }
764 }
765 }
766
767 #[pallet::event]
768 #[pallet::generate_deposit(pub(super) fn deposit_event)]
769 pub enum Event<T: Config<I>, I: 'static = ()> {
770 BridgeOpened {
772 bridge_id: BridgeId,
774 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
776
777 local_endpoint: Box<InteriorLocation>,
779 remote_endpoint: Box<InteriorLocation>,
781 lane_id: T::LaneId,
783 },
784 ClosingBridge {
786 bridge_id: BridgeId,
788 lane_id: T::LaneId,
790 pruned_messages: MessageNonce,
792 enqueued_messages: MessageNonce,
794 },
795 BridgePruned {
798 bridge_id: BridgeId,
800 lane_id: T::LaneId,
802 bridge_deposit: BalanceOf<ThisChainOf<T, I>>,
804 pruned_messages: MessageNonce,
806 },
807 }
808
809 #[pallet::error]
810 pub enum Error<T, I = ()> {
811 BridgeLocations(BridgeLocationsError),
813 InvalidBridgeOriginAccount,
815 BridgeAlreadyExists,
817 TooManyBridgesForLocalOrigin,
819 BridgeAlreadyClosed,
821 LanesManager(LanesManagerError),
823 UnknownBridge,
825 FailedToReserveBridgeDeposit,
827 UnsupportedXcmVersion,
829 }
830}
831
832#[cfg(test)]
833mod tests {
834 use super::*;
835 use bp_messages::LaneIdType;
836 use mock::*;
837
838 use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec};
839 use frame_system::{EventRecord, Phase};
840 use sp_runtime::TryRuntimeError;
841
842 fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId {
843 let bridge_owner_account =
844 LocationToAccountId::convert_location(locations.bridge_origin_relative_location())
845 .unwrap();
846 assert_ok!(Balances::mint_into(&bridge_owner_account, balance));
847 bridge_owner_account
848 }
849
850 fn mock_open_bridge_from_with(
851 origin: RuntimeOrigin,
852 deposit: Balance,
853 with: InteriorLocation,
854 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
855 let locations =
856 XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
857 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
858 let bridge_owner_account =
859 fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
860 Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap();
861
862 let bridge = Bridge {
863 bridge_origin_relative_location: Box::new(
864 locations.bridge_origin_relative_location().clone().into(),
865 ),
866 bridge_origin_universal_location: Box::new(
867 locations.bridge_origin_universal_location().clone().into(),
868 ),
869 bridge_destination_universal_location: Box::new(
870 locations.bridge_destination_universal_location().clone().into(),
871 ),
872 state: BridgeState::Opened,
873 bridge_owner_account,
874 deposit,
875 lane_id,
876 };
877 Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
878 LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
879
880 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
881 lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
882 lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
883
884 assert_ok!(XcmOverBridge::do_try_state());
885
886 (bridge, *locations)
887 }
888
889 fn mock_open_bridge_from(
890 origin: RuntimeOrigin,
891 deposit: Balance,
892 ) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
893 mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
894 }
895
896 fn enqueue_message(lane: TestLaneIdType) {
897 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
898 lanes_manager
899 .active_outbound_lane(lane)
900 .unwrap()
901 .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
902 }
903
904 #[test]
905 fn open_bridge_fails_if_origin_is_not_allowed() {
906 run_test(|| {
907 assert_noop!(
908 XcmOverBridge::open_bridge(
909 OpenBridgeOrigin::disallowed_origin(),
910 Box::new(bridged_asset_hub_universal_location().into()),
911 ),
912 sp_runtime::DispatchError::BadOrigin,
913 );
914 })
915 }
916
917 #[test]
918 fn open_bridge_fails_if_origin_is_not_relative() {
919 run_test(|| {
920 assert_noop!(
921 XcmOverBridge::open_bridge(
922 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
923 Box::new(bridged_asset_hub_universal_location().into()),
924 ),
925 Error::<TestRuntime, ()>::BridgeLocations(
926 BridgeLocationsError::InvalidBridgeOrigin
927 ),
928 );
929
930 assert_noop!(
931 XcmOverBridge::open_bridge(
932 OpenBridgeOrigin::sibling_parachain_universal_origin(),
933 Box::new(bridged_asset_hub_universal_location().into()),
934 ),
935 Error::<TestRuntime, ()>::BridgeLocations(
936 BridgeLocationsError::InvalidBridgeOrigin
937 ),
938 );
939 })
940 }
941
942 #[test]
943 fn open_bridge_fails_if_destination_is_not_remote() {
944 run_test(|| {
945 assert_noop!(
946 XcmOverBridge::open_bridge(
947 OpenBridgeOrigin::parent_relay_chain_origin(),
948 Box::new(
949 [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)]
950 .into()
951 ),
952 ),
953 Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
954 );
955 });
956 }
957
958 #[test]
959 fn open_bridge_fails_if_outside_of_bridged_consensus() {
960 run_test(|| {
961 assert_noop!(
962 XcmOverBridge::open_bridge(
963 OpenBridgeOrigin::parent_relay_chain_origin(),
964 Box::new(
965 [
966 GlobalConsensus(NonBridgedRelayNetwork::get()),
967 Parachain(BRIDGED_ASSET_HUB_ID)
968 ]
969 .into()
970 ),
971 ),
972 Error::<TestRuntime, ()>::BridgeLocations(
973 BridgeLocationsError::UnreachableDestination
974 ),
975 );
976 });
977 }
978
979 #[test]
980 fn open_bridge_fails_if_origin_has_no_sovereign_account() {
981 run_test(|| {
982 assert_noop!(
983 XcmOverBridge::open_bridge(
984 OpenBridgeOrigin::origin_without_sovereign_account(),
985 Box::new(bridged_asset_hub_universal_location().into()),
986 ),
987 Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
988 );
989 });
990 }
991
992 #[test]
993 fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
994 run_test(|| {
995 assert_noop!(
996 XcmOverBridge::open_bridge(
997 OpenBridgeOrigin::sibling_parachain_origin(),
998 Box::new(bridged_asset_hub_universal_location().into()),
999 ),
1000 Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
1001 );
1002 });
1003 }
1004
1005 #[test]
1006 fn open_bridge_fails_if_it_already_exists() {
1007 run_test(|| {
1008 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1009 let locations = XcmOverBridge::bridge_locations_from_origin(
1010 origin.clone(),
1011 Box::new(bridged_asset_hub_universal_location().into()),
1012 )
1013 .unwrap();
1014 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1015 fund_origin_sovereign_account(
1016 &locations,
1017 BridgeDeposit::get() + ExistentialDeposit::get(),
1018 );
1019
1020 Bridges::<TestRuntime, ()>::insert(
1021 locations.bridge_id(),
1022 Bridge {
1023 bridge_origin_relative_location: Box::new(
1024 locations.bridge_origin_relative_location().clone().into(),
1025 ),
1026 bridge_origin_universal_location: Box::new(
1027 locations.bridge_origin_universal_location().clone().into(),
1028 ),
1029 bridge_destination_universal_location: Box::new(
1030 locations.bridge_destination_universal_location().clone().into(),
1031 ),
1032 state: BridgeState::Opened,
1033 bridge_owner_account: [0u8; 32].into(),
1034 deposit: 0,
1035 lane_id,
1036 },
1037 );
1038
1039 assert_noop!(
1040 XcmOverBridge::open_bridge(
1041 origin,
1042 Box::new(bridged_asset_hub_universal_location().into()),
1043 ),
1044 Error::<TestRuntime, ()>::BridgeAlreadyExists,
1045 );
1046 })
1047 }
1048
1049 #[test]
1050 fn open_bridge_fails_if_its_lanes_already_exists() {
1051 run_test(|| {
1052 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1053 let locations = XcmOverBridge::bridge_locations_from_origin(
1054 origin.clone(),
1055 Box::new(bridged_asset_hub_universal_location().into()),
1056 )
1057 .unwrap();
1058 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
1059 fund_origin_sovereign_account(
1060 &locations,
1061 BridgeDeposit::get() + ExistentialDeposit::get(),
1062 );
1063
1064 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1065
1066 lanes_manager.create_inbound_lane(lane_id).unwrap();
1067 assert_noop!(
1068 XcmOverBridge::open_bridge(
1069 origin.clone(),
1070 Box::new(bridged_asset_hub_universal_location().into()),
1071 ),
1072 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
1073 );
1074
1075 lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
1076 lanes_manager.create_outbound_lane(lane_id).unwrap();
1077 assert_noop!(
1078 XcmOverBridge::open_bridge(
1079 origin,
1080 Box::new(bridged_asset_hub_universal_location().into()),
1081 ),
1082 Error::<TestRuntime, ()>::LanesManager(
1083 LanesManagerError::OutboundLaneAlreadyExists
1084 ),
1085 );
1086 })
1087 }
1088
1089 #[test]
1090 fn open_bridge_works() {
1091 run_test(|| {
1092 let origins = [
1095 (OpenBridgeOrigin::parent_relay_chain_origin(), 0),
1096 (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()),
1097 ];
1098
1099 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1101 let existential_deposit = ExistentialDeposit::get();
1102 for (origin, expected_deposit) in origins {
1103 System::set_block_number(1);
1105 System::reset_events();
1106
1107 let xcm_version = xcm::latest::VERSION;
1109 let locations = XcmOverBridge::bridge_locations_from_origin(
1110 origin.clone(),
1111 Box::new(
1112 VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
1113 .into_version(xcm_version)
1114 .expect("valid conversion"),
1115 ),
1116 )
1117 .unwrap();
1118 let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
1119
1120 assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
1122 assert_eq!(
1123 lanes_manager.active_inbound_lane(lane_id).map(drop),
1124 Err(LanesManagerError::UnknownInboundLane)
1125 );
1126 assert_eq!(
1127 lanes_manager.active_outbound_lane(lane_id).map(drop),
1128 Err(LanesManagerError::UnknownOutboundLane)
1129 );
1130 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
1131
1132 let bridge_owner_account = fund_origin_sovereign_account(
1134 &locations,
1135 expected_deposit + existential_deposit,
1136 );
1137 assert_eq!(
1138 Balances::free_balance(&bridge_owner_account),
1139 expected_deposit + existential_deposit
1140 );
1141 assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
1142
1143 assert_ok!(XcmOverBridge::open_bridge(
1145 origin,
1146 Box::new(locations.bridge_destination_universal_location().clone().into()),
1147 ));
1148
1149 assert_eq!(
1151 Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
1152 Some(Bridge {
1153 bridge_origin_relative_location: Box::new(
1154 locations.bridge_origin_relative_location().clone().into()
1155 ),
1156 bridge_origin_universal_location: Box::new(
1157 locations.bridge_origin_universal_location().clone().into(),
1158 ),
1159 bridge_destination_universal_location: Box::new(
1160 locations.bridge_destination_universal_location().clone().into(),
1161 ),
1162 state: BridgeState::Opened,
1163 bridge_owner_account: bridge_owner_account.clone(),
1164 deposit: expected_deposit,
1165 lane_id
1166 }),
1167 );
1168 assert_eq!(
1169 lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()),
1170 Ok(LaneState::Opened)
1171 );
1172 assert_eq!(
1173 lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()),
1174 Ok(LaneState::Opened)
1175 );
1176 assert_eq!(
1177 LaneToBridge::<TestRuntime, ()>::get(lane_id),
1178 Some(*locations.bridge_id())
1179 );
1180 assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit);
1181 assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit);
1182
1183 assert_eq!(
1185 System::events().last(),
1186 Some(&EventRecord {
1187 phase: Phase::Initialization,
1188 event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
1189 bridge_id: *locations.bridge_id(),
1190 bridge_deposit: expected_deposit,
1191 local_endpoint: Box::new(
1192 locations.bridge_origin_universal_location().clone()
1193 ),
1194 remote_endpoint: Box::new(
1195 locations.bridge_destination_universal_location().clone()
1196 ),
1197 lane_id: lane_id.into()
1198 }),
1199 topics: vec![],
1200 }),
1201 );
1202
1203 assert_ok!(XcmOverBridge::do_try_state());
1205 }
1206 });
1207 }
1208
1209 #[test]
1210 fn close_bridge_fails_if_origin_is_not_allowed() {
1211 run_test(|| {
1212 assert_noop!(
1213 XcmOverBridge::close_bridge(
1214 OpenBridgeOrigin::disallowed_origin(),
1215 Box::new(bridged_asset_hub_universal_location().into()),
1216 0,
1217 ),
1218 sp_runtime::DispatchError::BadOrigin,
1219 );
1220 })
1221 }
1222
1223 #[test]
1224 fn close_bridge_fails_if_origin_is_not_relative() {
1225 run_test(|| {
1226 assert_noop!(
1227 XcmOverBridge::close_bridge(
1228 OpenBridgeOrigin::parent_relay_chain_universal_origin(),
1229 Box::new(bridged_asset_hub_universal_location().into()),
1230 0,
1231 ),
1232 Error::<TestRuntime, ()>::BridgeLocations(
1233 BridgeLocationsError::InvalidBridgeOrigin
1234 ),
1235 );
1236
1237 assert_noop!(
1238 XcmOverBridge::close_bridge(
1239 OpenBridgeOrigin::sibling_parachain_universal_origin(),
1240 Box::new(bridged_asset_hub_universal_location().into()),
1241 0,
1242 ),
1243 Error::<TestRuntime, ()>::BridgeLocations(
1244 BridgeLocationsError::InvalidBridgeOrigin
1245 ),
1246 );
1247 })
1248 }
1249
1250 #[test]
1251 fn close_bridge_fails_if_its_lanes_are_unknown() {
1252 run_test(|| {
1253 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1254 let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0);
1255
1256 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1257 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge();
1258 assert_noop!(
1259 XcmOverBridge::close_bridge(
1260 origin.clone(),
1261 Box::new(locations.bridge_destination_universal_location().clone().into()),
1262 0,
1263 ),
1264 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
1265 );
1266 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1267
1268 let (_, locations) = mock_open_bridge_from(origin.clone(), 0);
1269 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge();
1270 assert_noop!(
1271 XcmOverBridge::close_bridge(
1272 origin,
1273 Box::new(locations.bridge_destination_universal_location().clone().into()),
1274 0,
1275 ),
1276 Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
1277 );
1278 });
1279 }
1280
1281 #[test]
1282 fn close_bridge_works() {
1283 run_test(|| {
1284 let origin = OpenBridgeOrigin::parent_relay_chain_origin();
1285 let expected_deposit = BridgeDeposit::get();
1286 let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit);
1287 System::set_block_number(1);
1288
1289 let free_balance = Balances::free_balance(&bridge.bridge_owner_account);
1291 let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account);
1292
1293 for _ in 0..32 {
1295 enqueue_message(bridge.lane_id);
1296 }
1297
1298 assert_ok!(XcmOverBridge::close_bridge(
1300 origin.clone(),
1301 Box::new(locations.bridge_destination_universal_location().clone().into()),
1302 16,
1303 ),);
1304
1305 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1308 assert_eq!(
1309 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1310 Some(BridgeState::Closed)
1311 );
1312 assert_eq!(
1313 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1314 LaneState::Closed
1315 );
1316 assert_eq!(
1317 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1318 LaneState::Closed
1319 );
1320 assert_eq!(
1321 lanes_manager
1322 .any_state_outbound_lane(bridge.lane_id)
1323 .unwrap()
1324 .queued_messages()
1325 .checked_len(),
1326 Some(16)
1327 );
1328 assert_eq!(
1329 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1330 Some(*locations.bridge_id())
1331 );
1332 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1333 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1334 assert_eq!(
1335 System::events().last(),
1336 Some(&EventRecord {
1337 phase: Phase::Initialization,
1338 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1339 bridge_id: *locations.bridge_id(),
1340 lane_id: bridge.lane_id.into(),
1341 pruned_messages: 16,
1342 enqueued_messages: 16,
1343 }),
1344 topics: vec![],
1345 }),
1346 );
1347
1348 assert_ok!(XcmOverBridge::close_bridge(
1350 origin.clone(),
1351 Box::new(locations.bridge_destination_universal_location().clone().into()),
1352 8,
1353 ),);
1354
1355 assert_eq!(
1357 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1358 Some(BridgeState::Closed)
1359 );
1360 assert_eq!(
1361 lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(),
1362 LaneState::Closed
1363 );
1364 assert_eq!(
1365 lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(),
1366 LaneState::Closed
1367 );
1368 assert_eq!(
1369 lanes_manager
1370 .any_state_outbound_lane(bridge.lane_id)
1371 .unwrap()
1372 .queued_messages()
1373 .checked_len(),
1374 Some(8)
1375 );
1376 assert_eq!(
1377 LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
1378 Some(*locations.bridge_id())
1379 );
1380 assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance);
1381 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance);
1382 assert_eq!(
1383 System::events().last(),
1384 Some(&EventRecord {
1385 phase: Phase::Initialization,
1386 event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
1387 bridge_id: *locations.bridge_id(),
1388 lane_id: bridge.lane_id.into(),
1389 pruned_messages: 8,
1390 enqueued_messages: 8,
1391 }),
1392 topics: vec![],
1393 }),
1394 );
1395
1396 assert_ok!(XcmOverBridge::close_bridge(
1399 origin,
1400 Box::new(locations.bridge_destination_universal_location().clone().into()),
1401 9,
1402 ),);
1403
1404 assert_eq!(
1406 Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
1407 None
1408 );
1409 assert_eq!(
1410 lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop),
1411 Err(LanesManagerError::UnknownInboundLane)
1412 );
1413 assert_eq!(
1414 lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop),
1415 Err(LanesManagerError::UnknownOutboundLane)
1416 );
1417 assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
1418 assert_eq!(
1419 Balances::free_balance(&bridge.bridge_owner_account),
1420 free_balance + reserved_balance
1421 );
1422 assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0);
1423 assert_eq!(
1424 System::events().last(),
1425 Some(&EventRecord {
1426 phase: Phase::Initialization,
1427 event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
1428 bridge_id: *locations.bridge_id(),
1429 lane_id: bridge.lane_id.into(),
1430 bridge_deposit: expected_deposit,
1431 pruned_messages: 8,
1432 }),
1433 topics: vec![],
1434 }),
1435 );
1436 });
1437 }
1438
1439 #[test]
1440 fn do_try_state_works() {
1441 let bridge_origin_relative_location = SiblingLocation::get();
1442 let bridge_origin_universal_location = SiblingUniversalLocation::get();
1443 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1444 let bridge_owner_account =
1445 LocationToAccountId::convert_location(&bridge_origin_relative_location)
1446 .expect("valid accountId");
1447 let bridge_owner_account_mismatch =
1448 LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
1449 let bridge_id = BridgeId::new(
1450 &bridge_origin_universal_location,
1451 &bridge_destination_universal_location,
1452 );
1453 let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
1454 let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
1455 let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
1456
1457 let test_bridge_state =
1458 |id,
1459 bridge,
1460 (lane_id, bridge_id),
1461 (inbound_lane_id, outbound_lane_id),
1462 expected_error: Option<TryRuntimeError>| {
1463 Bridges::<TestRuntime, ()>::insert(id, bridge);
1464 LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
1465
1466 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1467 lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
1468 lanes_manager.create_outbound_lane(outbound_lane_id).unwrap();
1469
1470 let result = XcmOverBridge::do_try_state();
1471 if let Some(e) = expected_error {
1472 assert_err!(result, e);
1473 } else {
1474 assert_ok!(result);
1475 }
1476 };
1477 let cleanup = |bridge_id, lane_ids| {
1478 Bridges::<TestRuntime, ()>::remove(bridge_id);
1479 for lane_id in lane_ids {
1480 LaneToBridge::<TestRuntime, ()>::remove(lane_id);
1481 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1482 if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
1483 lane.purge();
1484 }
1485 if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
1486 lane.purge();
1487 }
1488 }
1489 assert_ok!(XcmOverBridge::do_try_state());
1490 };
1491
1492 run_test(|| {
1493 test_bridge_state(
1495 bridge_id,
1496 Bridge {
1497 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1498 bridge_origin_relative_location.clone(),
1499 )),
1500 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1501 bridge_origin_universal_location.clone(),
1502 )),
1503 bridge_destination_universal_location: Box::new(
1504 VersionedInteriorLocation::from(
1505 bridge_destination_universal_location.clone(),
1506 ),
1507 ),
1508 state: BridgeState::Opened,
1509 bridge_owner_account: bridge_owner_account.clone(),
1510 deposit: Zero::zero(),
1511 lane_id,
1512 },
1513 (lane_id, bridge_id),
1514 (lane_id, lane_id),
1515 None,
1516 );
1517 cleanup(bridge_id, vec![lane_id]);
1518
1519 test_bridge_state(
1521 bridge_id,
1522 Bridge {
1523 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1524 bridge_origin_relative_location.clone(),
1525 )),
1526 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1527 bridge_origin_universal_location.clone(),
1528 )),
1529 bridge_destination_universal_location: Box::new(
1530 VersionedInteriorLocation::from(
1531 bridge_destination_universal_location.clone(),
1532 ),
1533 ),
1534 state: BridgeState::Opened,
1535 bridge_owner_account: bridge_owner_account.clone(),
1536 deposit: Zero::zero(),
1537 lane_id,
1538 },
1539 (lane_id, bridge_id_mismatch),
1540 (lane_id, lane_id),
1541 Some(TryRuntimeError::Other(
1542 "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
1543 )),
1544 );
1545 cleanup(bridge_id, vec![lane_id]);
1546
1547 test_bridge_state(
1549 bridge_id,
1550 Bridge {
1551 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1552 bridge_origin_relative_location.clone(),
1553 )),
1554 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1555 bridge_origin_universal_location.clone(),
1556 )),
1557 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1558 bridge_destination_universal_location.clone(),
1559 )),
1560 state: BridgeState::Opened,
1561 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1562 deposit: Zero::zero(),
1563 lane_id,
1564 },
1565 (lane_id, bridge_id),
1566 (lane_id, lane_id),
1567 Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
1568 );
1569 cleanup(bridge_id, vec![lane_id]);
1570
1571 test_bridge_state(
1574 bridge_id_mismatch,
1575 Bridge {
1576 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1577 bridge_origin_relative_location.clone(),
1578 )),
1579 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1580 bridge_origin_universal_location.clone(),
1581 )),
1582 bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
1583 bridge_destination_universal_location.clone(),
1584 )),
1585 state: BridgeState::Opened,
1586 bridge_owner_account: bridge_owner_account_mismatch.clone(),
1587 deposit: Zero::zero(),
1588 lane_id,
1589 },
1590 (lane_id, bridge_id_mismatch),
1591 (lane_id, lane_id),
1592 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!")),
1593 );
1594 cleanup(bridge_id_mismatch, vec![lane_id]);
1595
1596 test_bridge_state(
1598 bridge_id,
1599 Bridge {
1600 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1601 bridge_origin_relative_location.clone(),
1602 )),
1603 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1604 bridge_origin_universal_location.clone(),
1605 )),
1606 bridge_destination_universal_location: Box::new(
1607 VersionedInteriorLocation::from(
1608 bridge_destination_universal_location.clone(),
1609 ),
1610 ),
1611 state: BridgeState::Opened,
1612 bridge_owner_account: bridge_owner_account.clone(),
1613 deposit: Zero::zero(),
1614 lane_id,
1615 },
1616 (lane_id, bridge_id),
1617 (lane_id_mismatch, lane_id),
1618 Some(TryRuntimeError::Other("Inbound lane not found!")),
1619 );
1620 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1621
1622 test_bridge_state(
1624 bridge_id,
1625 Bridge {
1626 bridge_origin_relative_location: Box::new(VersionedLocation::from(
1627 bridge_origin_relative_location.clone(),
1628 )),
1629 bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
1630 bridge_origin_universal_location.clone(),
1631 )),
1632 bridge_destination_universal_location: Box::new(
1633 VersionedInteriorLocation::from(
1634 bridge_destination_universal_location.clone(),
1635 ),
1636 ),
1637 state: BridgeState::Opened,
1638 bridge_owner_account: bridge_owner_account.clone(),
1639 deposit: Zero::zero(),
1640 lane_id,
1641 },
1642 (lane_id, bridge_id),
1643 (lane_id, lane_id_mismatch),
1644 Some(TryRuntimeError::Other("Outbound lane not found!")),
1645 );
1646 cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
1647
1648 test_bridge_state(
1650 bridge_id,
1651 Bridge {
1652 bridge_origin_relative_location: Box::new(
1653 VersionedLocation::from(bridge_origin_relative_location.clone())
1654 .into_version(XCM_VERSION - 1)
1655 .unwrap(),
1656 ),
1657 bridge_origin_universal_location: Box::new(
1658 VersionedInteriorLocation::from(bridge_origin_universal_location.clone())
1659 .into_version(XCM_VERSION - 1)
1660 .unwrap(),
1661 ),
1662 bridge_destination_universal_location: Box::new(
1663 VersionedInteriorLocation::from(
1664 bridge_destination_universal_location.clone(),
1665 )
1666 .into_version(XCM_VERSION - 1)
1667 .unwrap(),
1668 ),
1669 state: BridgeState::Opened,
1670 bridge_owner_account: bridge_owner_account.clone(),
1671 deposit: Zero::zero(),
1672 lane_id,
1673 },
1674 (lane_id, bridge_id),
1675 (lane_id, lane_id),
1676 None,
1677 );
1678 cleanup(bridge_id, vec![lane_id]);
1679
1680 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1682 assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1683 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1684 cleanup(bridge_id, vec![lane_id]);
1685
1686 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1688 assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1689 assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1690 cleanup(bridge_id, vec![lane_id]);
1691 });
1692 }
1693
1694 #[test]
1695 fn ensure_encoding_compatibility() {
1696 use codec::Encode;
1697
1698 let bridge_destination_universal_location = BridgedUniversalDestination::get();
1699 let may_prune_messages = 13;
1700
1701 assert_eq!(
1702 bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge {
1703 bridge_destination_universal_location: Box::new(
1704 bridge_destination_universal_location.clone().into()
1705 )
1706 }
1707 .encode(),
1708 Call::<TestRuntime, ()>::open_bridge {
1709 bridge_destination_universal_location: Box::new(
1710 bridge_destination_universal_location.clone().into()
1711 )
1712 }
1713 .encode()
1714 );
1715 assert_eq!(
1716 bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge {
1717 bridge_destination_universal_location: Box::new(
1718 bridge_destination_universal_location.clone().into()
1719 ),
1720 may_prune_messages,
1721 }
1722 .encode(),
1723 Call::<TestRuntime, ()>::close_bridge {
1724 bridge_destination_universal_location: Box::new(
1725 bridge_destination_universal_location.clone().into()
1726 ),
1727 may_prune_messages,
1728 }
1729 .encode()
1730 );
1731 }
1732}