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")) + } +}