diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs
index d9c87049077a..ed486c04abcc 100644
--- a/bridges/bin/runtime-common/src/lib.rs
+++ b/bridges/bin/runtime-common/src/lib.rs
@@ -28,6 +28,7 @@ pub mod messages;
pub mod messages_api;
pub mod messages_benchmarking;
pub mod messages_call_ext;
+pub mod messages_xcm_extension;
pub mod parachains_benchmarking;
pub mod refund_relayer_extension;
@@ -37,6 +38,8 @@ mod mock;
#[cfg(feature = "integrity-test")]
pub mod integrity;
+const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch";
+
/// A duplication of the `FilterCall` trait.
///
/// We need this trait in order to be able to implement it for the messages pallet,
diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs
index 4eb44ea85ed4..4493ac66742f 100644
--- a/bridges/bin/runtime-common/src/messages.rs
+++ b/bridges/bin/runtime-common/src/messages.rs
@@ -497,7 +497,7 @@ pub mod target {
let weight = XcmWeigher::weight(&mut payload.xcm.1);
let weight = weight.unwrap_or_else(|e| {
log::debug!(
- target: "runtime::bridge-dispatch",
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Failed to compute dispatch weight of incoming XCM message {:?}/{}: {:?}",
message.key.lane_id,
message.key.nonce,
@@ -525,7 +525,7 @@ pub mod target {
let FromBridgedChainMessagePayload { xcm: (location, xcm), weight: weight_limit } =
message.data.payload?;
log::trace!(
- target: "runtime::bridge-dispatch",
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Going to execute message {:?} (weight limit: {:?}): {:?} {:?}",
message_id,
weight_limit,
@@ -551,7 +551,7 @@ pub mod target {
match xcm_outcome {
Ok(outcome) => {
log::trace!(
- target: "runtime::bridge-dispatch",
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Incoming message {:?} dispatched with result: {:?}",
message_id,
outcome,
@@ -560,7 +560,7 @@ pub mod target {
Ok(_weight) => (),
Err(e) => {
log::error!(
- target: "runtime::bridge-dispatch",
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Incoming message {:?} was not dispatched, error: {:?}",
message_id,
e,
@@ -570,7 +570,7 @@ pub mod target {
},
Err(e) => {
log::error!(
- target: "runtime::bridge-dispatch",
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
"Incoming message {:?} was not dispatched, codec error: {:?}",
message_id,
e,
diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs
new file mode 100644
index 000000000000..7163d8a53576
--- /dev/null
+++ b/bridges/bin/runtime-common/src/messages_xcm_extension.rs
@@ -0,0 +1,181 @@
+// Copyright 2023 Parity Technologies (UK) Ltd.
+// This file is part of Parity Bridges Common.
+
+// Parity Bridges Common is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity Bridges Common is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity Bridges Common. If not, see .
+
+//! Module provides utilities for easier XCM handling, e.g:
+//! [`XcmExecutor`] -> [`MessageSender`] ->
+//! |
+//!
+//! |
+//! [`XcmRouter`] <- [`MessageDispatch`] <-
+
+use bp_messages::{
+ source_chain::MessagesBridge,
+ target_chain::{DispatchMessage, MessageDispatch},
+ LaneId,
+};
+use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain};
+use codec::{Decode, Encode};
+use frame_support::{dispatch::Weight, traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound};
+use scale_info::TypeInfo;
+use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError};
+
+/// Plain "XCM" payload, which we transfer through bridge
+pub type XcmAsPlainPayload = sp_std::prelude::Vec;
+
+/// Message dispatch result type for single message
+#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)]
+pub enum XcmBlobMessageDispatchResult {
+ InvalidPayload,
+ Dispatched,
+ NotDispatched(#[codec(skip)] &'static str),
+}
+
+/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages
+pub struct XcmBlobMessageDispatch<
+ SourceBridgeHubChain,
+ TargetBridgeHubChain,
+ DispatchBlob,
+ DispatchBlobWeigher,
+> {
+ _marker: sp_std::marker::PhantomData<(
+ SourceBridgeHubChain,
+ TargetBridgeHubChain,
+ DispatchBlob,
+ DispatchBlobWeigher,
+ )>,
+}
+
+impl<
+ SourceBridgeHubChain: Chain,
+ TargetBridgeHubChain: Chain,
+ BlobDispatcher: DispatchBlob,
+ DispatchBlobWeigher: Get,
+ > MessageDispatch>
+ for XcmBlobMessageDispatch<
+ SourceBridgeHubChain,
+ TargetBridgeHubChain,
+ BlobDispatcher,
+ DispatchBlobWeigher,
+ >
+{
+ type DispatchPayload = XcmAsPlainPayload;
+ type DispatchLevelResult = XcmBlobMessageDispatchResult;
+
+ fn dispatch_weight(_message: &mut DispatchMessage) -> Weight {
+ DispatchBlobWeigher::get()
+ }
+
+ fn dispatch(
+ _relayer_account: &AccountIdOf,
+ message: DispatchMessage,
+ ) -> MessageDispatchResult {
+ let payload = match message.data.payload {
+ Ok(payload) => payload,
+ Err(e) => {
+ log::error!(
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
+ "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}",
+ e,
+ message.key.nonce
+ );
+ return MessageDispatchResult {
+ // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629
+ unspent_weight: Weight::zero(),
+ dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
+ }
+ },
+ };
+ let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) {
+ Ok(_) => {
+ log::debug!(
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
+ "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}",
+ message.key.nonce
+ );
+ XcmBlobMessageDispatchResult::Dispatched
+ },
+ Err(e) => {
+ let e = match e {
+ DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable",
+ DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding",
+ DispatchBlobError::UnsupportedLocationVersion =>
+ "DispatchBlobError::UnsupportedLocationVersion",
+ DispatchBlobError::UnsupportedXcmVersion =>
+ "DispatchBlobError::UnsupportedXcmVersion",
+ DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError",
+ DispatchBlobError::NonUniversalDestination =>
+ "DispatchBlobError::NonUniversalDestination",
+ DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal",
+ };
+ log::error!(
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
+ "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}",
+ e, message.key.nonce
+ );
+ XcmBlobMessageDispatchResult::NotDispatched(e)
+ },
+ };
+ MessageDispatchResult {
+ // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629
+ unspent_weight: Weight::zero(),
+ dispatch_level_result,
+ }
+ }
+}
+
+/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from
+/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`].
+pub trait XcmBlobHauler {
+ /// Runtime message sender adapter.
+ type MessageSender: MessagesBridge;
+
+ /// Runtime message sender origin, which is used by [`MessageSender`].
+ type MessageSenderOrigin;
+ /// Our location within the Consensus Universe.
+ fn message_sender_origin() -> Self::MessageSenderOrigin;
+
+ /// Return message lane (as "point-to-point link") used to deliver XCM messages.
+ fn xcm_lane() -> LaneId;
+}
+
+/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`MessageSender`] and makes sure that
+/// XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed.
+pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData);
+impl> HaulBlob
+ for XcmBlobHaulerAdapter
+{
+ fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> {
+ let lane = H::xcm_lane();
+ let result = H::MessageSender::send_message(H::message_sender_origin(), lane, blob);
+ let result = result
+ .map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256));
+ match &result {
+ Ok(result) => log::info!(
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
+ "haul_blob result - ok: {:?} on lane: {:?}",
+ result,
+ lane
+ ),
+ Err(error) => log::error!(
+ target: crate::LOG_TARGET_BRIDGE_DISPATCH,
+ "haul_blob result - error: {:?} on lane: {:?}",
+ error,
+ lane
+ ),
+ };
+ result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError"))
+ }
+}