diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 3d68d8ed16ae1459f7ac2dd39f1df80ecb44131e..e23412a97ebcb3e6f2999d0f48d555e53d02503e 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -132,11 +132,13 @@ pub use routing::{ mod transactional; pub use transactional::FrameTransactionalProcessor; +#[allow(deprecated)] +pub use universal_exports::UnpaidLocalExporter; mod universal_exports; pub use universal_exports::{ ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError, - ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable, - NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter, + ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, LocalExporter, NetworkExportTable, + NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidRemoteExporter, }; mod weight; diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs index ea584bf9d485a25c38ce0b673259b6ed8df7368f..5e930fe575c2b07c3c8d172f478d7f20ff519e76 100644 --- a/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/local_para_para.rs @@ -28,7 +28,7 @@ parameter_types! { type TheBridge = TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation, ()>>; type Router = TestTopic< - UnpaidLocalExporter< + LocalExporter< HaulBlobExporter<TheBridge, RemoteNetwork, AlwaysLatest, Price>, UniversalLocation, >, diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs index 38ffe2532d580a017efc21043e7313028700c046..a41f09721812a99f71fdcb58345e265472b163c6 100644 --- a/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs @@ -28,7 +28,7 @@ parameter_types! { type TheBridge = TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation, ()>>; type Router = TestTopic< - UnpaidLocalExporter< + LocalExporter< HaulBlobExporter<TheBridge, RemoteNetwork, AlwaysLatest, Price>, UniversalLocation, >, diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs index 767575e7f2dd9efa29cc1441a8cc2bf2cdaf3d19..90ad9921d65a13b8b5590507d0a7c36ba79b063a 100644 --- a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs @@ -209,7 +209,7 @@ impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> S let origin = Local::get().relative_to(&Remote::get()); AllowUnpaidFrom::set(vec![origin.clone()]); set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>); - // The we execute it: + // Then we execute it: let mut id = fake_id(); let outcome = XcmExecutor::<TestConfig>::prepare_and_execute( origin, diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 6b3c3adf737dbb6e938d59383c5100041c493d52..e215aea3ab6858a0bcecd5b952cb4fd1384f52a9 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -16,6 +16,8 @@ //! Traits and utilities to help with origin mutation and bridging. +#![allow(deprecated)] + use crate::InspectMessageQueues; use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; @@ -58,6 +60,8 @@ pub fn ensure_is_remote( /// that the message sending cannot be abused in any way. /// /// This is only useful when the local chain has bridging capabilities. +#[deprecated(note = "Will be removed after July 2025; It uses hard-coded channel `0`, \ + use `xcm_builder::LocalExporter` directly instead.")] pub struct UnpaidLocalExporter<Exporter, UniversalLocation>( PhantomData<(Exporter, UniversalLocation)>, ); @@ -100,6 +104,54 @@ impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm fn ensure_successful_delivery(_: Option<Location>) {} } +/// Implementation of `SendXcm` which uses the given `ExportXcm` implementation in order to forward +/// the message over a bridge. +/// +/// This is only useful when the local chain has bridging capabilities. +pub struct LocalExporter<Exporter, UniversalLocation>(PhantomData<(Exporter, UniversalLocation)>); +impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm + for LocalExporter<Exporter, UniversalLocation> +{ + type Ticket = Exporter::Ticket; + + fn validate( + dest: &mut Option<Location>, + msg: &mut Option<Xcm<()>>, + ) -> SendResult<Exporter::Ticket> { + // This `clone` ensures that `dest` is not consumed in any case. + let d = dest.clone().take().ok_or(MissingArgument)?; + let universal_source = UniversalLocation::get(); + let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|_| NotApplicable)?; + let (remote_network, remote_location) = devolved; + let xcm = msg.take().ok_or(MissingArgument)?; + + let hash = + (Some(Location::here()), &remote_location).using_encoded(sp_io::hashing::blake2_128); + let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); + + validate_export::<Exporter>( + remote_network, + channel, + universal_source, + remote_location, + xcm.clone(), + ) + .inspect_err(|err| { + if let NotApplicable = err { + // We need to make sure that msg is not consumed in case of `NotApplicable`. + *msg = Some(xcm); + } + }) + } + + fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> { + Exporter::deliver(ticket) + } + + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful_delivery(_: Option<Location>) {} +} + pub trait ExporterFor { /// Return the locally-routable bridge (if any) capable of forwarding `message` to the /// `remote_location` on the remote `network`, together with the payment which is required. @@ -703,9 +755,9 @@ mod tests { let local_dest: Location = (Parent, Parachain(5678)).into(); assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err()); - // UnpaidLocalExporter + // LocalExporter ensure_validate_does_not_consume_dest_or_msg::< - UnpaidLocalExporter<RoutableBridgeExporter, UniversalLocation>, + LocalExporter<RoutableBridgeExporter, UniversalLocation>, >(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result)); // 2. check with not applicable from the inner router (using `NotApplicableBridgeSender`) @@ -713,14 +765,14 @@ mod tests { (Parent, Parent, DifferentRemote::get(), RemoteDestination::get()).into(); assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok()); - // UnpaidLocalExporter + // LocalExporter ensure_validate_does_not_consume_dest_or_msg::< - UnpaidLocalExporter<NotApplicableBridgeExporter, UniversalLocation>, + LocalExporter<NotApplicableBridgeExporter, UniversalLocation>, >(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result)); // 3. Ok - deliver // UnpaidRemoteExporter - assert_ok!(send_xcm::<UnpaidLocalExporter<RoutableBridgeExporter, UniversalLocation>>( + assert_ok!(send_xcm::<LocalExporter<RoutableBridgeExporter, UniversalLocation>>( remote_dest, Xcm::default() )); diff --git a/prdoc/pr_7126.prdoc b/prdoc/pr_7126.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1a86af1b2d1da8ade29829a01f83753637e915dd --- /dev/null +++ b/prdoc/pr_7126.prdoc @@ -0,0 +1,7 @@ +title: 'xcm: Fixes for `UnpaidLocalExporter`' +doc: +- audience: Runtime Dev + description: This PR deprecates `UnpaidLocalExporter` in favor of the new `LocalExporter`. First, the name is misleading, as it can be used in both paid and unpaid scenarios. Second, it contains a hard-coded channel 0, whereas `LocalExporter` uses the same algorithm as `xcm-exporter`. +crates: +- name: staging-xcm-builder + bump: minor