1#![allow(deprecated)]
20
21use crate::InspectMessageQueues;
22use alloc::{vec, vec::Vec};
23use codec::{Decode, Encode};
24use core::{convert::TryInto, marker::PhantomData};
25use frame_support::{ensure, traits::Get};
26use xcm::prelude::*;
27use xcm_executor::traits::{validate_export, ExportXcm};
28use SendError::*;
29
30pub fn ensure_is_remote(
35 universal_local: impl Into<InteriorLocation>,
36 dest: impl Into<Location>,
37) -> Result<(NetworkId, InteriorLocation), Location> {
38 let dest = dest.into();
39 let universal_local = universal_local.into();
40 let local_net = match universal_local.global_consensus() {
41 Ok(x) => x,
42 Err(_) => return Err(dest),
43 };
44 let universal_destination: InteriorLocation = universal_local
45 .into_location()
46 .appended_with(dest.clone())
47 .map_err(|x| x.1)?
48 .try_into()?;
49 let (remote_dest, remote_net) = match universal_destination.split_first() {
50 (d, Some(GlobalConsensus(n))) if n != local_net => (d, n),
51 _ => return Err(dest),
52 };
53 Ok((remote_net, remote_dest))
54}
55
56#[deprecated(note = "Will be removed after July 2025; It uses hard-coded channel `0`, \
64 use `xcm_builder::LocalExporter` directly instead.")]
65pub struct UnpaidLocalExporter<Exporter, UniversalLocation>(
66 PhantomData<(Exporter, UniversalLocation)>,
67);
68impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
69 for UnpaidLocalExporter<Exporter, UniversalLocation>
70{
71 type Ticket = Exporter::Ticket;
72
73 fn validate(
74 dest: &mut Option<Location>,
75 msg: &mut Option<Xcm<()>>,
76 ) -> SendResult<Exporter::Ticket> {
77 let d = dest.clone().ok_or(MissingArgument)?;
79 let universal_source = UniversalLocation::get();
80 let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|error| {
81 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
82 NotApplicable
83 })?;
84 let (remote_network, remote_location) = devolved;
85 let xcm = msg.take().ok_or(MissingArgument)?;
86
87 validate_export::<Exporter>(
88 remote_network,
89 0,
90 universal_source,
91 remote_location,
92 xcm.clone(),
93 )
94 .inspect_err(|err| {
95 if let NotApplicable = err {
96 *msg = Some(xcm);
98 }
99 })
100 }
101
102 fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> {
103 Exporter::deliver(ticket)
104 }
105
106 #[cfg(feature = "runtime-benchmarks")]
107 fn ensure_successful_delivery(_: Option<Location>) {}
108}
109
110pub struct LocalExporter<Exporter, UniversalLocation>(PhantomData<(Exporter, UniversalLocation)>);
115impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
116 for LocalExporter<Exporter, UniversalLocation>
117{
118 type Ticket = Exporter::Ticket;
119
120 fn validate(
121 dest: &mut Option<Location>,
122 msg: &mut Option<Xcm<()>>,
123 ) -> SendResult<Exporter::Ticket> {
124 let d = dest.clone().ok_or(MissingArgument)?;
126 let universal_source = UniversalLocation::get();
127 let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|error| {
128 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
129 NotApplicable
130 })?;
131 let (remote_network, remote_location) = devolved;
132 let xcm = msg.take().ok_or(MissingArgument)?;
133
134 let hash =
135 (Some(Location::here()), &remote_location).using_encoded(sp_io::hashing::blake2_128);
136 let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
137
138 validate_export::<Exporter>(
139 remote_network,
140 channel,
141 universal_source,
142 remote_location,
143 xcm.clone(),
144 )
145 .inspect_err(|err| {
146 if let NotApplicable = err {
147 *msg = Some(xcm);
149 }
150 })
151 }
152
153 fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> {
154 Exporter::deliver(ticket)
155 }
156
157 #[cfg(feature = "runtime-benchmarks")]
158 fn ensure_successful_delivery(_: Option<Location>) {}
159}
160
161pub trait ExporterFor {
162 fn exporter_for(
169 network: &NetworkId,
170 remote_location: &InteriorLocation,
171 message: &Xcm<()>,
172 ) -> Option<(Location, Option<Asset>)>;
173}
174
175#[impl_trait_for_tuples::impl_for_tuples(30)]
176impl ExporterFor for Tuple {
177 fn exporter_for(
178 network: &NetworkId,
179 remote_location: &InteriorLocation,
180 message: &Xcm<()>,
181 ) -> Option<(Location, Option<Asset>)> {
182 for_tuples!( #(
183 if let Some(r) = Tuple::exporter_for(network, remote_location, message) {
184 return Some(r);
185 }
186 )* );
187 None
188 }
189}
190
191pub struct NetworkExportTableItem {
193 pub remote_network: NetworkId,
195 pub remote_location_filter: Option<Vec<InteriorLocation>>,
200 pub bridge: Location,
203 pub payment: Option<Asset>,
206}
207
208impl NetworkExportTableItem {
209 pub fn new(
210 remote_network: NetworkId,
211 remote_location_filter: Option<Vec<InteriorLocation>>,
212 bridge: Location,
213 payment: Option<Asset>,
214 ) -> Self {
215 Self { remote_network, remote_location_filter, bridge, payment }
216 }
217}
218
219pub struct NetworkExportTable<T>(core::marker::PhantomData<T>);
223impl<T: Get<Vec<NetworkExportTableItem>>> ExporterFor for NetworkExportTable<T> {
224 fn exporter_for(
225 network: &NetworkId,
226 remote_location: &InteriorLocation,
227 _: &Xcm<()>,
228 ) -> Option<(Location, Option<Asset>)> {
229 T::get()
230 .into_iter()
231 .find(|item| {
232 &item.remote_network == network &&
233 item.remote_location_filter
234 .as_ref()
235 .map(|filters| filters.iter().any(|filter| filter == remote_location))
236 .unwrap_or(true)
237 })
238 .map(|item| (item.bridge, item.payment))
239 }
240}
241
242pub struct UnpaidRemoteExporter<Bridges, Router, UniversalLocation>(
262 PhantomData<(Bridges, Router, UniversalLocation)>,
263);
264impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
265 for UnpaidRemoteExporter<Bridges, Router, UniversalLocation>
266{
267 type Ticket = Router::Ticket;
268
269 fn validate(
270 dest: &mut Option<Location>,
271 msg: &mut Option<Xcm<()>>,
272 ) -> SendResult<Router::Ticket> {
273 let d = dest.clone().ok_or(MissingArgument)?;
275 let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|error| {
276 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
277 NotApplicable
278 })?;
279 let (remote_network, remote_location) = devolved;
280 let xcm = msg.take().ok_or(MissingArgument)?;
281
282 let Some((bridge, maybe_payment)) =
284 Bridges::exporter_for(&remote_network, &remote_location, &xcm)
285 else {
286 *msg = Some(xcm);
288 return Err(NotApplicable)
289 };
290
291 let maybe_forward_id = match xcm.last() {
294 Some(SetTopic(t)) => Some(*t),
295 _ => None,
296 };
297
298 let mut message = Xcm(vec![
302 UnpaidExecution { weight_limit: Unlimited, check_origin: None },
303 ExportMessage {
304 network: remote_network,
305 destination: remote_location,
306 xcm: xcm.clone(),
307 },
308 ]);
309 if let Some(forward_id) = maybe_forward_id {
310 message.0.push(SetTopic(forward_id));
311 }
312 let (v, mut cost) = validate_send::<Router>(bridge, message).inspect_err(|err| {
313 if let NotApplicable = err {
314 *msg = Some(xcm);
316 }
317 })?;
318 if let Some(bridge_payment) = maybe_payment {
319 cost.push(bridge_payment);
320 }
321 Ok((v, cost))
322 }
323
324 fn deliver(validation: Self::Ticket) -> Result<XcmHash, SendError> {
325 Router::deliver(validation)
326 }
327
328 #[cfg(feature = "runtime-benchmarks")]
329 fn ensure_successful_delivery(location: Option<Location>) {
330 Router::ensure_successful_delivery(location);
331 }
332}
333
334impl<Bridges, Router, UniversalLocation> InspectMessageQueues
335 for UnpaidRemoteExporter<Bridges, Router, UniversalLocation>
336{
337 fn clear_messages() {}
338
339 fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
342 Vec::new()
343 }
344}
345
346pub struct SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>(
362 PhantomData<(Bridges, Router, UniversalLocation)>,
363);
364impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
365 for SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>
366{
367 type Ticket = Router::Ticket;
368
369 fn validate(
370 dest: &mut Option<Location>,
371 msg: &mut Option<Xcm<()>>,
372 ) -> SendResult<Router::Ticket> {
373 let d = dest.clone().ok_or(MissingArgument)?;
375 let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|error| {
376 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
377 NotApplicable
378 })?;
379 let (remote_network, remote_location) = devolved;
380 let xcm = msg.take().ok_or(MissingArgument)?;
381
382 let Some((bridge, maybe_payment)) =
384 Bridges::exporter_for(&remote_network, &remote_location, &xcm)
385 else {
386 *msg = Some(xcm);
388 return Err(NotApplicable)
389 };
390
391 let maybe_forward_id = match xcm.last() {
394 Some(SetTopic(t)) => Some(*t),
395 _ => None,
396 };
397
398 let local_from_bridge = UniversalLocation::get().invert_target(&bridge).map_err(|_| {
399 tracing::debug!(target: "xcm::universal_exports", "Failed to invert bridge location");
400 Unroutable
401 })?;
402 let export_instruction = ExportMessage {
403 network: remote_network,
404 destination: remote_location,
405 xcm: xcm.clone(),
406 };
407
408 let mut message = Xcm(if let Some(ref payment) = maybe_payment {
409 let fees =
410 payment.clone().reanchored(&bridge, &UniversalLocation::get()).map_err(|_| {
411 tracing::debug!(target: "xcm::universal_exports", "Failed to reanchor payment");
412 Unroutable
413 })?;
414 vec![
415 WithdrawAsset(fees.clone().into()),
416 BuyExecution { fees, weight_limit: Unlimited },
417 SetAppendix(Xcm(vec![DepositAsset {
421 assets: AllCounted(1).into(),
422 beneficiary: local_from_bridge,
423 }])),
424 export_instruction,
425 ]
426 } else {
427 vec![export_instruction]
428 });
429 if let Some(forward_id) = maybe_forward_id {
430 message.0.push(SetTopic(forward_id));
431 }
432
433 let (v, mut cost) = validate_send::<Router>(bridge, message).inspect_err(|err| {
436 if let NotApplicable = err {
437 *msg = Some(xcm);
439 }
440 })?;
441 if let Some(bridge_payment) = maybe_payment {
442 cost.push(bridge_payment);
443 }
444 Ok((v, cost))
445 }
446
447 fn deliver(ticket: Router::Ticket) -> Result<XcmHash, SendError> {
448 Router::deliver(ticket)
449 }
450
451 #[cfg(feature = "runtime-benchmarks")]
452 fn ensure_successful_delivery(location: Option<Location>) {
453 Router::ensure_successful_delivery(location);
454 }
455}
456
457impl<Bridges, Router, UniversalLocation> InspectMessageQueues
458 for SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>
459{
460 fn clear_messages() {}
461
462 fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
465 Vec::new()
466 }
467}
468
469pub trait DispatchBlob {
470 fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError>;
480}
481
482pub trait HaulBlob {
483 fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError>;
485}
486
487#[derive(Clone, Copy, Debug, PartialEq, Eq)]
488pub enum HaulBlobError {
489 Transport(&'static str),
492}
493
494impl From<HaulBlobError> for SendError {
495 fn from(err: HaulBlobError) -> Self {
496 match err {
497 HaulBlobError::Transport(reason) => SendError::Transport(reason),
498 }
499 }
500}
501
502#[derive(Clone, Encode, Decode)]
503pub struct BridgeMessage {
504 pub universal_dest: VersionedInteriorLocation,
508 pub message: VersionedXcm<()>,
509}
510
511#[derive(Clone, Copy, Debug, PartialEq, Eq)]
512pub enum DispatchBlobError {
513 Unbridgable,
514 InvalidEncoding,
515 UnsupportedLocationVersion,
516 UnsupportedXcmVersion,
517 RoutingError,
518 NonUniversalDestination,
519 WrongGlobal,
520}
521
522pub struct BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>(
523 PhantomData<(Router, OurPlace, OurPlaceBridgeInstance)>,
524);
525impl<
526 Router: SendXcm,
527 OurPlace: Get<InteriorLocation>,
528 OurPlaceBridgeInstance: Get<Option<InteriorLocation>>,
529 > DispatchBlob for BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>
530{
531 fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError> {
532 let our_universal = OurPlace::get();
533 let our_global = our_universal.global_consensus().map_err(|()| {
534 tracing::debug!(target: "xcm::universal_exports", "Failed to get global consensus");
535 DispatchBlobError::Unbridgable
536 })?;
537 let BridgeMessage { universal_dest, message } =
538 Decode::decode(&mut &blob[..]).map_err(|error| {
539 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to decode blob");
540 DispatchBlobError::InvalidEncoding
541 })?;
542 let universal_dest: InteriorLocation = universal_dest.try_into().map_err(|_| {
543 tracing::debug!(target: "xcm::universal_exports", "Failed to convert universal destination");
544 DispatchBlobError::UnsupportedLocationVersion
545 })?;
546 let intended_global = universal_dest
549 .global_consensus()
550 .map_err(|()| {
551 tracing::debug!(target: "xcm::universal_exports", "Failed to get global consensus from universal destination");
552 DispatchBlobError::NonUniversalDestination })?;
553 ensure!(intended_global == our_global, DispatchBlobError::WrongGlobal);
554 let dest = universal_dest.relative_to(&our_universal);
555 let mut message: Xcm<()> = message.try_into().map_err(|_| {
556 tracing::debug!(target: "xcm::universal_exports", "Failed to convert message");
557 DispatchBlobError::UnsupportedXcmVersion
558 })?;
559
560 if let Some(bridge_instance) = OurPlaceBridgeInstance::get() {
565 message.0.insert(0, DescendOrigin(bridge_instance));
566 }
567
568 send_xcm::<Router>(dest, message).map_err(|error| {
569 tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to send XCM");
570 DispatchBlobError::RoutingError
571 })?;
572 Ok(())
573 }
574}
575
576pub struct HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>(
577 PhantomData<(Bridge, BridgedNetwork, DestinationVersion, Price)>,
578);
579impl<
590 Bridge: HaulBlob,
591 BridgedNetwork: Get<Location>,
592 DestinationVersion: GetVersion,
593 Price: Get<Assets>,
594 > ExportXcm for HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>
595{
596 type Ticket = (Vec<u8>, XcmHash);
597
598 fn validate(
599 network: NetworkId,
600 _channel: u32,
601 universal_source: &mut Option<InteriorLocation>,
602 destination: &mut Option<InteriorLocation>,
603 message: &mut Option<Xcm<()>>,
604 ) -> Result<((Vec<u8>, XcmHash), Assets), SendError> {
605 let (bridged_network, bridged_network_location_parents) = {
606 let Location { parents, interior: mut junctions } = BridgedNetwork::get();
607 match junctions.take_first() {
608 Some(GlobalConsensus(network)) => (network, parents),
609 _ => return Err(NotApplicable),
610 }
611 };
612 ensure!(&network == &bridged_network, NotApplicable);
613 let dest = destination.take().ok_or(SendError::MissingArgument)?;
615
616 let (universal_dest, version) =
619 match dest.pushed_front_with(GlobalConsensus(bridged_network)) {
620 Ok(d) => {
621 let version = DestinationVersion::get_version_for(&Location::from(
622 AncestorThen(bridged_network_location_parents, d.clone()),
623 ))
624 .ok_or(SendError::DestinationUnsupported)?;
625 (d, version)
626 },
627 Err((dest, _)) => {
628 *destination = Some(dest);
629 return Err(NotApplicable)
630 },
631 };
632
633 let (local_net, local_sub) = universal_source
635 .take()
636 .ok_or(SendError::MissingArgument)?
637 .split_global()
638 .map_err(|()| {
639 tracing::debug!(target: "xcm::universal_exports", "Failed to split global consensus");
640 SendError::Unroutable
641 })?;
642 let mut message = message.take().ok_or(SendError::MissingArgument)?;
643 let maybe_id = match message.last() {
644 Some(SetTopic(t)) => Some(*t),
645 _ => None,
646 };
647 message.0.insert(0, UniversalOrigin(GlobalConsensus(local_net)));
648 if local_sub != Here {
649 message.0.insert(1, DescendOrigin(local_sub));
650 }
651
652 let message = VersionedXcm::from(message).into_version(version).map_err(|()| {
656 tracing::debug!(target: "xcm::universal_exports", "Failed to convert message to versioned XCM");
657 SendError::DestinationUnsupported
658 })?;
659 let universal_dest = VersionedInteriorLocation::from(universal_dest)
660 .into_version(version)
661 .map_err(|()| {
662 tracing::debug!(target: "xcm::universal_exports", "Failed to convert destination to versioned location");
663 SendError::DestinationUnsupported })?;
664
665 let id = maybe_id.unwrap_or_else(|| message.using_encoded(sp_io::hashing::blake2_256));
666 let blob = BridgeMessage { universal_dest, message }.encode();
667 Ok(((blob, id), Price::get()))
668 }
669
670 fn deliver((blob, id): (Vec<u8>, XcmHash)) -> Result<XcmHash, SendError> {
671 Bridge::haul_blob(blob)?;
672 Ok(id)
673 }
674}
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679 use frame_support::{
680 assert_err, assert_ok,
681 traits::{Contains, Equals},
682 };
683
684 #[test]
685 fn ensure_is_remote_works() {
686 let x = ensure_is_remote(Polkadot, (Parent, Kusama, Parachain(1000)));
688 assert_eq!(x, Ok((Kusama, Parachain(1000).into())));
689
690 let x = ensure_is_remote((Kusama, Parachain(1000)), (Parent, Parent, Polkadot));
692 assert_eq!(x, Ok((Polkadot, Here)));
693
694 let x = ensure_is_remote(Polkadot, Parachain(1000));
696 assert_eq!(x, Err(Parachain(1000).into()));
697
698 let x = ensure_is_remote(Polkadot, (Parent, Polkadot, Parachain(1000)));
700 assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into()));
701
702 let x = ensure_is_remote((), (Parent, Polkadot, Parachain(1000)));
704 assert_eq!(x, Err((Parent, Polkadot, Parachain(1000)).into()));
705 }
706
707 pub struct OkFor<Filter>(PhantomData<Filter>);
708 impl<Filter: Contains<Location>> SendXcm for OkFor<Filter> {
709 type Ticket = ();
710
711 fn validate(
712 destination: &mut Option<Location>,
713 _message: &mut Option<Xcm<()>>,
714 ) -> SendResult<Self::Ticket> {
715 if let Some(d) = destination.as_ref() {
716 if Filter::contains(&d) {
717 return Ok(((), Assets::new()))
718 }
719 }
720 Err(NotApplicable)
721 }
722
723 fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
724 Ok([0; 32])
725 }
726
727 #[cfg(feature = "runtime-benchmarks")]
728 fn ensure_successful_delivery(_: Option<Location>) {}
729 }
730 impl<Filter: Contains<(NetworkId, InteriorLocation)>> ExportXcm for OkFor<Filter> {
731 type Ticket = ();
732
733 fn validate(
734 network: NetworkId,
735 _: u32,
736 _: &mut Option<InteriorLocation>,
737 destination: &mut Option<InteriorLocation>,
738 _: &mut Option<Xcm<()>>,
739 ) -> SendResult<Self::Ticket> {
740 if let Some(d) = destination.as_ref() {
741 if Filter::contains(&(network, d.clone())) {
742 return Ok(((), Assets::new()))
743 }
744 }
745 Err(NotApplicable)
746 }
747
748 fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
749 Ok([1; 32])
750 }
751 }
752
753 fn ensure_validate_does_not_consume_dest_or_msg<S: SendXcm>(
756 dest: Location,
757 assert_result: impl Fn(SendResult<S::Ticket>),
758 ) {
759 let mut dest_wrapper = Some(dest.clone());
760 let msg = Xcm::<()>::new();
761 let mut msg_wrapper = Some(msg.clone());
762
763 assert_result(S::validate(&mut dest_wrapper, &mut msg_wrapper));
764
765 assert_eq!(Some(dest), dest_wrapper);
767 assert_eq!(Some(msg), msg_wrapper);
768 }
769
770 #[test]
771 fn local_exporters_works() {
772 frame_support::parameter_types! {
773 pub Local: NetworkId = ByGenesis([0; 32]);
774 pub UniversalLocation: InteriorLocation = [GlobalConsensus(Local::get()), Parachain(1234)].into();
775 pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
776 pub RemoteDestination: Junction = Parachain(9657);
777 pub RoutableBridgeFilter: (NetworkId, InteriorLocation) = (DifferentRemote::get(), RemoteDestination::get().into());
778 }
779 type RoutableBridgeExporter = OkFor<Equals<RoutableBridgeFilter>>;
780 type NotApplicableBridgeExporter = OkFor<()>;
781 assert_ok!(validate_export::<RoutableBridgeExporter>(
782 DifferentRemote::get(),
783 0,
784 UniversalLocation::get(),
785 RemoteDestination::get().into(),
786 Xcm::default()
787 ));
788 assert_err!(
789 validate_export::<NotApplicableBridgeExporter>(
790 DifferentRemote::get(),
791 0,
792 UniversalLocation::get(),
793 RemoteDestination::get().into(),
794 Xcm::default()
795 ),
796 NotApplicable
797 );
798
799 let local_dest: Location = (Parent, Parachain(5678)).into();
801 assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
802
803 ensure_validate_does_not_consume_dest_or_msg::<
805 LocalExporter<RoutableBridgeExporter, UniversalLocation>,
806 >(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
807
808 let remote_dest: Location =
810 (Parent, Parent, DifferentRemote::get(), RemoteDestination::get()).into();
811 assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
812
813 ensure_validate_does_not_consume_dest_or_msg::<
815 LocalExporter<NotApplicableBridgeExporter, UniversalLocation>,
816 >(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
817
818 assert_ok!(send_xcm::<LocalExporter<RoutableBridgeExporter, UniversalLocation>>(
821 remote_dest,
822 Xcm::default()
823 ));
824 }
825
826 #[test]
827 fn remote_exporters_works() {
828 frame_support::parameter_types! {
829 pub Local: NetworkId = ByGenesis([0; 32]);
830 pub UniversalLocation: InteriorLocation = [GlobalConsensus(Local::get()), Parachain(1234)].into();
831 pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
832 pub RoutableBridge: Location = Location::new(1, Parachain(9657));
833 pub NotApplicableBridgeTable: Vec<NetworkExportTableItem> = vec![];
835 pub RoutableBridgeTable: Vec<NetworkExportTableItem> = vec![
837 NetworkExportTableItem::new(
838 DifferentRemote::get(),
839 None,
840 RoutableBridge::get(),
841 None
842 )
843 ];
844 }
845 type RoutableBridgeSender = OkFor<Equals<RoutableBridge>>;
846 type NotApplicableBridgeSender = OkFor<()>;
847 assert_ok!(validate_send::<RoutableBridgeSender>(RoutableBridge::get(), Xcm::default()));
848 assert_err!(
849 validate_send::<NotApplicableBridgeSender>(RoutableBridge::get(), Xcm::default()),
850 NotApplicable
851 );
852
853 let local_dest: Location = (Parent, Parachain(5678)).into();
855 assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
856
857 ensure_validate_does_not_consume_dest_or_msg::<
859 UnpaidRemoteExporter<
860 NetworkExportTable<RoutableBridgeTable>,
861 RoutableBridgeSender,
862 UniversalLocation,
863 >,
864 >(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
865 ensure_validate_does_not_consume_dest_or_msg::<
867 SovereignPaidRemoteExporter<
868 NetworkExportTable<RoutableBridgeTable>,
869 RoutableBridgeSender,
870 UniversalLocation,
871 >,
872 >(local_dest, |result| assert_eq!(Err(NotApplicable), result));
873
874 let remote_dest: Location = (Parent, Parent, DifferentRemote::get()).into();
876 assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
877
878 ensure_validate_does_not_consume_dest_or_msg::<
880 UnpaidRemoteExporter<
881 NetworkExportTable<NotApplicableBridgeTable>,
882 RoutableBridgeSender,
883 UniversalLocation,
884 >,
885 >(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
886 ensure_validate_does_not_consume_dest_or_msg::<
888 SovereignPaidRemoteExporter<
889 NetworkExportTable<NotApplicableBridgeTable>,
890 RoutableBridgeSender,
891 UniversalLocation,
892 >,
893 >(remote_dest, |result| assert_eq!(Err(NotApplicable), result));
894
895 let remote_dest: Location = (Parent, Parent, DifferentRemote::get()).into();
897 assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
898
899 ensure_validate_does_not_consume_dest_or_msg::<
901 UnpaidRemoteExporter<
902 NetworkExportTable<RoutableBridgeTable>,
903 NotApplicableBridgeSender,
904 UniversalLocation,
905 >,
906 >(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
907 ensure_validate_does_not_consume_dest_or_msg::<
909 SovereignPaidRemoteExporter<
910 NetworkExportTable<RoutableBridgeTable>,
911 NotApplicableBridgeSender,
912 UniversalLocation,
913 >,
914 >(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
915
916 assert_ok!(send_xcm::<
919 UnpaidRemoteExporter<
920 NetworkExportTable<RoutableBridgeTable>,
921 RoutableBridgeSender,
922 UniversalLocation,
923 >,
924 >(remote_dest.clone(), Xcm::default()));
925 assert_ok!(send_xcm::<
927 SovereignPaidRemoteExporter<
928 NetworkExportTable<RoutableBridgeTable>,
929 RoutableBridgeSender,
930 UniversalLocation,
931 >,
932 >(remote_dest, Xcm::default()));
933 }
934
935 #[test]
936 fn network_export_table_works() {
937 frame_support::parameter_types! {
938 pub NetworkA: NetworkId = ByGenesis([0; 32]);
939 pub Parachain1000InNetworkA: InteriorLocation = [Parachain(1000)].into();
940 pub Parachain2000InNetworkA: InteriorLocation = [Parachain(2000)].into();
941
942 pub NetworkB: NetworkId = ByGenesis([1; 32]);
943
944 pub BridgeToALocation: Location = Location::new(1, [Parachain(1234)]);
945 pub BridgeToBLocation: Location = Location::new(1, [Parachain(4321)]);
946
947 pub PaymentForNetworkAAndParachain2000: Asset = (Location::parent(), 150).into();
948
949 pub BridgeTable: alloc::vec::Vec<NetworkExportTableItem> = alloc::vec![
950 NetworkExportTableItem::new(
952 NetworkA::get(),
953 Some(vec![Parachain1000InNetworkA::get()]),
954 BridgeToALocation::get(),
955 None
956 ),
957 NetworkExportTableItem::new(
959 NetworkA::get(),
960 Some(vec![Parachain2000InNetworkA::get()]),
961 BridgeToALocation::get(),
962 Some(PaymentForNetworkAAndParachain2000::get())
963 ),
964 NetworkExportTableItem::new(
966 NetworkB::get(),
967 None,
968 BridgeToBLocation::get(),
969 None
970 )
971 ];
972 }
973
974 let test_data: Vec<(NetworkId, InteriorLocation, Option<(Location, Option<Asset>)>)> = vec![
975 (NetworkA::get(), [Parachain(1000)].into(), Some((BridgeToALocation::get(), None))),
976 (NetworkA::get(), [Parachain(1000), GeneralIndex(1)].into(), None),
977 (
978 NetworkA::get(),
979 [Parachain(2000)].into(),
980 Some((BridgeToALocation::get(), Some(PaymentForNetworkAAndParachain2000::get()))),
981 ),
982 (NetworkA::get(), [Parachain(2000), GeneralIndex(1)].into(), None),
983 (NetworkA::get(), [Parachain(3000)].into(), None),
984 (NetworkB::get(), [Parachain(1000)].into(), Some((BridgeToBLocation::get(), None))),
985 (NetworkB::get(), [Parachain(2000)].into(), Some((BridgeToBLocation::get(), None))),
986 (NetworkB::get(), [Parachain(3000)].into(), Some((BridgeToBLocation::get(), None))),
987 ];
988
989 for (network, remote_location, expected_result) in test_data {
990 assert_eq!(
991 NetworkExportTable::<BridgeTable>::exporter_for(
992 &network,
993 &remote_location,
994 &Xcm::default()
995 ),
996 expected_result,
997 "expected_result: {:?} not matched for network: {:?} and remote_location: {:?}",
998 expected_result,
999 network,
1000 remote_location,
1001 )
1002 }
1003 }
1004}