diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 612a9e4364b75..743bab6d13764 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -833,16 +833,26 @@ impl_runtime_apis! { lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, - ) -> Vec> { + ) -> Vec> { bridge_runtime_common::messages_api::outbound_message_details::< Runtime, WithRialtoMessagesInstance, - WithRialtoMessageBridge, - xcm_config::OutboundXcmWeigher, >(lane, begin, end) } } + impl bp_rialto::FromRialtoInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoMessagesInstance, + >(lane, messages) + } + } + impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi for Runtime { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, @@ -860,16 +870,26 @@ impl_runtime_apis! { lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, - ) -> Vec> { + ) -> Vec> { bridge_runtime_common::messages_api::outbound_message_details::< Runtime, WithRialtoParachainMessagesInstance, - WithRialtoParachainMessageBridge, - xcm_config::OutboundXcmWeigher, >(lane, begin, end) } } + impl bp_rialto_parachain::FromRialtoParachainInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + >(lane, messages) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/bridges/bin/millau/runtime/src/xcm_config.rs b/bridges/bin/millau/runtime/src/xcm_config.rs index a274b25924a5d..5ba8d97e65e6d 100644 --- a/bridges/bin/millau/runtime/src/xcm_config.rs +++ b/bridges/bin/millau/runtime/src/xcm_config.rs @@ -122,8 +122,6 @@ pub type Barrier = ( AllowKnownQueryResponses, ); -/// Outbound XCM weigher type. -pub type OutboundXcmWeigher = xcm_builder::FixedWeightBounds; /// XCM weigher type. pub type XcmWeigher = xcm_builder::FixedWeightBounds; diff --git a/bridges/bin/rialto-parachain/runtime/src/lib.rs b/bridges/bin/rialto-parachain/runtime/src/lib.rs index 0ebd0dc306ecf..1f8a0a2581e6a 100644 --- a/bridges/bin/rialto-parachain/runtime/src/lib.rs +++ b/bridges/bin/rialto-parachain/runtime/src/lib.rs @@ -383,8 +383,6 @@ pub type Barrier = ( // ^^^ Parent & its unit plurality gets free execution ); -/// Outbound XCM weigher type. -pub type OutboundXcmWeigher = FixedWeightBounds; /// XCM weigher type. pub type XcmWeigher = FixedWeightBounds; @@ -708,16 +706,26 @@ impl_runtime_apis! { lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, - ) -> Vec> { + ) -> Vec> { bridge_runtime_common::messages_api::outbound_message_details::< Runtime, WithMillauMessagesInstance, - WithMillauMessageBridge, - OutboundXcmWeigher, >(lane, begin, end) } } + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn dispatch_benchmark( diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 82eb57f40ffa1..9b7dc70394ac5 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -893,15 +893,25 @@ impl_runtime_apis! { lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, - ) -> Vec> { + ) -> Vec> { bridge_runtime_common::messages_api::outbound_message_details::< Runtime, WithMillauMessagesInstance, - WithMillauMessageBridge, - xcm_config::OutboundXcmWeigher, >(lane, begin, end) } } + + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } } #[cfg(test)] diff --git a/bridges/bin/rialto/runtime/src/xcm_config.rs b/bridges/bin/rialto/runtime/src/xcm_config.rs index 65ffc54a9f680..8a230101f3065 100644 --- a/bridges/bin/rialto/runtime/src/xcm_config.rs +++ b/bridges/bin/rialto/runtime/src/xcm_config.rs @@ -122,8 +122,6 @@ pub type Barrier = ( AllowKnownQueryResponses, ); -/// Outbound XCM weigher type. -pub type OutboundXcmWeigher = xcm_builder::FixedWeightBounds; /// Incoming XCM weigher type. pub type XcmWeigher = xcm_builder::FixedWeightBounds; diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs index 4f5a175e24046..68465fa16e417 100644 --- a/bridges/bin/runtime-common/src/messages_api.rs +++ b/bridges/bin/runtime-common/src/messages_api.rs @@ -16,68 +16,54 @@ //! Helpers for implementing various message-related runtime API mthods. -use crate::messages::{target::FromBridgedChainMessagePayload, MessageBridge}; - -use bp_messages::{LaneId, MessageDetails, MessageKey, MessageNonce}; -use codec::Decode; -use frame_support::weights::Weight; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use sp_std::vec::Vec; /// Implementation of the `To*OutboundLaneApi::message_details`. -pub fn outbound_message_details( +pub fn outbound_message_details( lane: LaneId, begin: MessageNonce, end: MessageNonce, -) -> Vec> +) -> Vec> where Runtime: pallet_bridge_messages::Config, MessagesPalletInstance: 'static, - BridgeConfig: MessageBridge, - XcmWeigher: xcm_executor::traits::WeightBounds<()>, { (begin..=end) .filter_map(|nonce| { let message_data = pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; - Some(MessageDetails { + Some(OutboundMessageDetails { nonce, - // this shall match the similar code in the `FromBridgedChainMessageDispatch` - if we have failed - // to decode or estimate dispatch weight, we'll just return 0 to disable actual execution - dispatch_weight: compute_message_weight::( - MessageKey { lane_id: lane, nonce }, - &message_data.payload, - ).unwrap_or(0), + // dispatch message weight is always zero at the source chain, since we're paying for + // dispatch at the target chain + dispatch_weight: 0, size: message_data.payload.len() as _, delivery_and_dispatch_fee: message_data.fee, + // we're delivering XCM messages here, so fee is always paid at the target chain dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtTargetChain, }) }) .collect() } -// at the source chain we don't know the type of target chain `Call` => `()` is used (it is -// similarly currently used in Polkadot codebase) -fn compute_message_weight>( - message_key: MessageKey, - encoded_payload: &[u8], -) -> Result { - let mut payload = FromBridgedChainMessagePayload::<()>::decode(&mut &encoded_payload[..]) - .map_err(|e| { - log::debug!( - target: "runtime::bridge-dispatch", - "Failed to decode outbound XCM message {:?}: {:?}", - message_key, - e, - ); - })?; - let weight = XcmWeigher::weight(&mut payload.xcm.1); - let weight = weight.map_err(|e| { - log::debug!( - target: "runtime::bridge-dispatch", - "Failed to compute dispatch weight of outbound XCM message {:?}: {:?}", - message_key, - e, - ); - })?; - Ok(weight) +/// Implementation of the `To*InboundLaneApi::message_details`. +pub fn inbound_message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, +) -> Vec +where + Runtime: pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, +{ + messages + .into_iter() + .map(|(payload, details)| { + pallet_bridge_messages::Pallet::::inbound_message_data( + lane, payload, details, + ) + }) + .collect() } diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index 5c3398ff705f4..a41ea3d1d95a8 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -56,9 +56,10 @@ use bp_messages::{ target_chain::{ DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, - total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, - MessageNonce, OperatingMode, OutboundLaneData, Parameter as MessagesParameter, - UnrewardedRelayer, UnrewardedRelayersState, + total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, + MessageData, MessageKey, MessageNonce, MessagePayload, OperatingMode, OutboundLaneData, + OutboundMessageDetails, Parameter as MessagesParameter, UnrewardedRelayer, + UnrewardedRelayersState, }; use bp_runtime::{ChainId, Size}; use codec::{Decode, Encode}; @@ -156,7 +157,7 @@ pub mod pallet { /// Payload type of inbound messages. This payload is dispatched on this chain. type InboundPayload: Decode; /// Message fee type of inbound messages. This fee is paid on the bridged chain. - type InboundMessageFee: Decode; + type InboundMessageFee: Decode + Zero; /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the /// bridged chain. type InboundRelayer: Parameter; @@ -762,6 +763,22 @@ pub mod pallet { ) -> Option> { OutboundMessages::::get(MessageKey { lane_id: lane, nonce }) } + + /// Prepare data, related to given inbound message. + pub fn inbound_message_data( + lane: LaneId, + payload: MessagePayload, + outbound_details: OutboundMessageDetails, + ) -> InboundMessageDetails { + let mut dispatch_message = DispatchMessage { + key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, + data: MessageData { payload, fee: outbound_details.delivery_and_dispatch_fee } + .into(), + }; + InboundMessageDetails { + dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), + } + } } } @@ -2332,4 +2349,25 @@ mod tests { bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, ); } + + #[test] + fn inbound_message_details_works() { + run_test(|| { + assert_eq!( + Pallet::::inbound_message_data( + TEST_LANE_ID, + REGULAR_PAYLOAD.encode(), + OutboundMessageDetails { + nonce: 0, + dispatch_weight: 0, + size: 0, + delivery_and_dispatch_fee: 0, + dispatch_fee_payment: + bp_runtime::messages::DispatchFeePayment::AtTargetChain, + }, + ), + InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, + ); + }); + } } diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/primitives/chain-kusama/src/lib.rs index a0a5990ca08ad..880fe5ac1bd00 100644 --- a/bridges/primitives/chain-kusama/src/lib.rs +++ b/bridges/primitives/chain-kusama/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; @@ -105,6 +107,9 @@ pub const TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToKusamaOutboundLaneApi::message_details` runtime method. pub const TO_KUSAMA_MESSAGE_DETAILS_METHOD: &str = "ToKusamaOutboundLaneApi_message_details"; +/// Name of the `FromKusamaInboundLaneApi::message_details` runtime method. +pub const FROM_KUSAMA_MESSAGE_DETAILS_METHOD: &str = "FromKusamaInboundLaneApi_message_details"; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Kusama headers. /// @@ -143,6 +148,21 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Kusama chain. + /// + /// This API is implemented by runtimes that are receiving messages from Kusama chain, not the + /// Kusama runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromKusamaInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs index 6cc417756a606..b91ec86e3febb 100644 --- a/bridges/primitives/chain-millau/src/lib.rs +++ b/bridges/primitives/chain-millau/src/lib.rs @@ -20,7 +20,9 @@ mod millau_hash; -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, @@ -294,6 +296,9 @@ pub const TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToMillauOutboundLaneApi::message_details` runtime method. pub const TO_MILLAU_MESSAGE_DETAILS_METHOD: &str = "ToMillauOutboundLaneApi_message_details"; +/// Name of the `FromMillauInboundLaneApi::message_details` runtime method. +pub const FROM_MILLAU_MESSAGE_DETAILS_METHOD: &str = "FromMillauInboundLaneApi_message_details"; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Millau headers. /// @@ -332,7 +337,22 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Millau chain. + /// + /// This API is implemented by runtimes that are receiving messages from Millau chain, not the + /// Millau runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromMillauInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/primitives/chain-polkadot/src/lib.rs index d95e29c8b0ce8..e16a23e1d148f 100644 --- a/bridges/primitives/chain-polkadot/src/lib.rs +++ b/bridges/primitives/chain-polkadot/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; @@ -105,6 +107,10 @@ pub const TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToPolkadotOutboundLaneApi::message_details` runtime method. pub const TO_POLKADOT_MESSAGE_DETAILS_METHOD: &str = "ToPolkadotOutboundLaneApi_message_details"; +/// Name of the `FromPolkadotInboundLaneApi::message_details` runtime method. +pub const FROM_POLKADOT_MESSAGE_DETAILS_METHOD: &str = + "FromPolkadotOutboundLaneApi_message_details"; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Polkadot headers. /// @@ -143,6 +149,21 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Polkadot chain. + /// + /// This API is implemented by runtimes that are receiving messages from Polkadot chain, not the + /// Polkadot runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromPolkadotInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-rialto-parachain/src/lib.rs b/bridges/primitives/chain-rialto-parachain/src/lib.rs index 598286b2f3ab3..d42bfaf4110b4 100644 --- a/bridges/primitives/chain-rialto-parachain/src/lib.rs +++ b/bridges/primitives/chain-rialto-parachain/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, @@ -210,6 +212,10 @@ pub const TO_RIALTO_PARACHAIN_ESTIMATE_MESSAGE_FEE_METHOD: &str = pub const TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD: &str = "ToRialtoParachainOutboundLaneApi_message_details"; +/// Name of the `FromRialtoParachainInboundLaneApi::message_details` runtime method. +pub const FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD: &str = + "FromRialtoParachainInboundLaneApi_message_details"; + // We use this to get the account on RialtoParachain (target) which is derived from Millau's // (source) account. We do this so we can fund the derived account on RialtoParachain at Genesis to // it can pay transaction fees. @@ -261,7 +267,22 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by RialtoParachain chain. + /// + /// This API is implemented by runtimes that are receiving messages from RialtoParachain chain, not the + /// RialtoParachain runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromRialtoParachainInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs index ca3381a25fd38..9d598b8d22fed 100644 --- a/bridges/primitives/chain-rialto/src/lib.rs +++ b/bridges/primitives/chain-rialto/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, @@ -250,6 +252,9 @@ pub const TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToRialtoOutboundLaneApi::message_details` runtime method. pub const TO_RIALTO_MESSAGE_DETAILS_METHOD: &str = "ToRialtoOutboundLaneApi_message_details"; +/// Name of the `FromRialtoInboundLaneApi::message_details` runtime method. +pub const FROM_RIALTO_MESSAGE_DETAILS_METHOD: &str = "FromRialtoInboundLaneApi_message_details"; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Rialto headers. /// @@ -288,7 +293,22 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Rialto chain. + /// + /// This API is implemented by runtimes that are receiving messages from Rialto chain, not the + /// Rialto runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromRialtoInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index 007301a7cec6c..a0f545a95c21e 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use frame_support::weights::{ Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; @@ -89,6 +91,9 @@ pub const TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToRococoOutboundLaneApi::message_details` runtime method. pub const TO_ROCOCO_MESSAGE_DETAILS_METHOD: &str = "ToRococoOutboundLaneApi_message_details"; +/// Name of the `FromRococoInboundLaneApi::message_details` runtime method. +pub const FROM_ROCOCO_MESSAGE_DETAILS_METHOD: &str = "FromRococoInboundLaneApi_message_details"; + /// Existential deposit on Rococo. pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 100; @@ -139,6 +144,21 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Rococo chain. + /// + /// This API is implemented by runtimes that are receiving messages from Rococo chain, not the + /// Rococo runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromRococoInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/chain-wococo/src/lib.rs b/bridges/primitives/chain-wococo/src/lib.rs index f39543114c78b..69039355923c4 100644 --- a/bridges/primitives/chain-wococo/src/lib.rs +++ b/bridges/primitives/chain-wococo/src/lib.rs @@ -18,7 +18,9 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; use sp_runtime::FixedU128; use sp_std::prelude::*; @@ -58,6 +60,9 @@ pub const TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD: &str = /// Name of the `ToWococoOutboundLaneApi::message_details` runtime method. pub const TO_WOCOCO_MESSAGE_DETAILS_METHOD: &str = "ToWococoOutboundLaneApi_message_details"; +/// Name of the `FromWococoInboundLaneApi::message_details` runtime method. +pub const FROM_WOCOCO_MESSAGE_DETAILS_METHOD: &str = "FromWococoInboundLaneApi_message_details"; + sp_api::decl_runtime_apis! { /// API for querying information about the finalized Wococo headers. /// @@ -96,6 +101,21 @@ sp_api::decl_runtime_apis! { lane: LaneId, begin: MessageNonce, end: MessageNonce, - ) -> Vec>; + ) -> Vec>; + } + + /// Inbound message lane API for messages sent by Wococo chain. + /// + /// This API is implemented by runtimes that are receiving messages from Wococo chain, not the + /// Wococo runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait FromWococoInboundLaneApi { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; } } diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index cef28ecb3679c..35df6d01f1cbe 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -176,12 +176,15 @@ impl InboundLaneData { } } -/// Message details, returned by runtime APIs. +/// Outbound message details, returned by runtime APIs. #[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] -pub struct MessageDetails { +pub struct OutboundMessageDetails { /// Nonce assigned to the message. pub nonce: MessageNonce, - /// Message dispatch weight, declared by the submitter. + /// Message dispatch weight. + /// + /// Depending on messages pallet configuration, it may be declared by the message submitter, + /// computed automatically or just be zero if dispatch fee is paid at the target chain. pub dispatch_weight: Weight, /// Size of the encoded message. pub size: u32, @@ -191,6 +194,18 @@ pub struct MessageDetails { pub dispatch_fee_payment: DispatchFeePayment, } +/// Inbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +pub struct InboundMessageDetails { + /// Computed message dispatch weight. + /// + /// Runtime API guarantees that it will match the value, returned by + /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime + /// has failed to decode the message, it will be zero - that's because `undecodable` + /// message cannot be dispatched. + pub dispatch_weight: Weight, +} + /// Bit vector of message dispatch results. pub type DispatchResultsBitVec = BitVec; diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs index 1ee0d7d631089..a5686e4abea73 100644 --- a/bridges/primitives/messages/src/target_chain.rs +++ b/bridges/primitives/messages/src/target_chain.rs @@ -93,8 +93,9 @@ pub trait MessageDispatch { /// Estimate dispatch weight. /// - /// This function must: (1) be instant and (2) return correct upper bound - /// of dispatch weight. + /// This function must return correct upper bound of dispatch weight. The return value + /// of this function is expected to match return value of the corresponding + /// `FromInboundLaneApi::message_details().dispatch_weight` call. fn dispatch_weight(message: &mut DispatchMessage) -> Weight; /// Called when inbound message is received. diff --git a/bridges/relays/client-kusama/src/lib.rs b/bridges/relays/client-kusama/src/lib.rs index e228f2dc24de4..8011cbc564666 100644 --- a/bridges/relays/client-kusama/src/lib.rs +++ b/bridges/relays/client-kusama/src/lib.rs @@ -79,6 +79,8 @@ impl ChainWithMessages for Kusama { bp_kusama::WITH_KUSAMA_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_kusama::FROM_KUSAMA_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-millau/src/lib.rs b/bridges/relays/client-millau/src/lib.rs index a2d18d538520c..1fa4708368a36 100644 --- a/bridges/relays/client-millau/src/lib.rs +++ b/bridges/relays/client-millau/src/lib.rs @@ -63,6 +63,8 @@ impl ChainWithMessages for Millau { bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::FROM_MILLAU_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-polkadot/src/lib.rs b/bridges/relays/client-polkadot/src/lib.rs index d4ada45e36cc8..745bbc44fb6fb 100644 --- a/bridges/relays/client-polkadot/src/lib.rs +++ b/bridges/relays/client-polkadot/src/lib.rs @@ -80,6 +80,8 @@ impl ChainWithMessages for Polkadot { bp_polkadot::WITH_POLKADOT_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_polkadot::FROM_POLKADOT_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-rialto-parachain/src/lib.rs b/bridges/relays/client-rialto-parachain/src/lib.rs index f70815c03ef8b..9f54363d9c4fc 100644 --- a/bridges/relays/client-rialto-parachain/src/lib.rs +++ b/bridges/relays/client-rialto-parachain/src/lib.rs @@ -88,6 +88,8 @@ impl ChainWithMessages for RialtoParachain { bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-rialto/src/lib.rs b/bridges/relays/client-rialto/src/lib.rs index 70fcf4450922d..7ddaaabe3611c 100644 --- a/bridges/relays/client-rialto/src/lib.rs +++ b/bridges/relays/client-rialto/src/lib.rs @@ -85,6 +85,8 @@ impl ChainWithMessages for Rialto { bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::FROM_RIALTO_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs index f63041df9eddb..161b719516ae9 100644 --- a/bridges/relays/client-rococo/src/lib.rs +++ b/bridges/relays/client-rococo/src/lib.rs @@ -82,6 +82,8 @@ impl ChainWithMessages for Rococo { bp_rococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rococo::FROM_ROCOCO_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index f4a2fcf262fd1..d7f441b73ac2c 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -105,6 +105,10 @@ pub trait ChainWithMessages: Chain { /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + /// Name of the `FromInboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + /// Additional weight of the dispatch fee payment if dispatch is paid at the target chain /// and this `ChainWithMessages` is the target chain. const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight; diff --git a/bridges/relays/client-wococo/src/lib.rs b/bridges/relays/client-wococo/src/lib.rs index 485ca1bd62f4f..eb20e40f483e6 100644 --- a/bridges/relays/client-wococo/src/lib.rs +++ b/bridges/relays/client-wococo/src/lib.rs @@ -82,6 +82,8 @@ impl ChainWithMessages for Wococo { bp_wococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_wococo::FROM_WOCOCO_MESSAGE_DETAILS_METHOD; const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index 9c447c6b8354c..7dc582dd5762d 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -31,8 +31,10 @@ use async_std::sync::Arc; use async_trait::async_trait; use bp_messages::{ storage_keys::{operating_mode_key, outbound_lane_data_key}, - LaneId, MessageNonce, OperatingMode, OutboundLaneData, UnrewardedRelayersState, + InboundMessageDetails, LaneId, MessageData, MessageNonce, OperatingMode, OutboundLaneData, + OutboundMessageDetails, UnrewardedRelayersState, }; +use bp_runtime::messages::DispatchFeePayment; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; @@ -54,7 +56,7 @@ use relay_substrate_client::{ use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; use sp_core::{Bytes, Pair}; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; -use std::ops::RangeInclusive; +use std::{collections::HashMap, ops::RangeInclusive}; /// Intermediate message proof returned by the source Substrate node. Includes everything /// required to submit to the target node: cumulative dispatch weight of bundled messages and @@ -199,11 +201,97 @@ where ) .await?; - make_message_details_map::( + let mut messages = make_message_details_map::( Decode::decode(&mut &encoded_response.0[..]) .map_err(SubstrateError::ResponseParseFailed)?, nonces, - ) + )?; + + // prepare arguments of the inbound message details call (if we need it) + let mut messages_to_refine = HashMap::new(); + for (message_nonce, message) in &messages { + if message.dispatch_fee_payment != DispatchFeePayment::AtTargetChain { + continue + } + + // for pay-at-target messages we may want to ask target chain for + // refined dispatch weight + let message_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + *message_nonce, + ); + let message_data: MessageData> = + self.source_client.storage_value(message_key, Some(id.1)).await?.ok_or_else( + || { + SubstrateError::Custom(format!( + "Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}", + P::TargetChain::NAME, + self.lane_id, + message_nonce, + P::SourceChain::NAME, + id, + )) + }, + )?; + let message_payload = message_data.payload; + messages_to_refine.insert( + *message_nonce, + ( + message_payload, + OutboundMessageDetails { + nonce: *message_nonce, + dispatch_weight: message.dispatch_weight, + size: message.size, + delivery_and_dispatch_fee: message.reward, + dispatch_fee_payment: DispatchFeePayment::AtTargetChain, + }, + ), + ); + } + + // request inbound message details from the target client + if !messages_to_refine.is_empty() { + let refined_messages_encoded = self + .target_client + .state_call( + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD.into(), + Bytes((self.lane_id, messages_to_refine.values().collect::>()).encode()), + None, + ) + .await?; + let refined_messages = + Vec::::decode(&mut &refined_messages_encoded.0[..]) + .map_err(SubstrateError::ResponseParseFailed)?; + if refined_messages.len() != messages_to_refine.len() { + return Err(SubstrateError::Custom(format!( + "Call of {} at {} has returned {} entries instead of expected {}", + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD, + P::TargetChain::NAME, + refined_messages.len(), + messages_to_refine.len(), + ))) + } + + for (nonce, refined_message) in messages_to_refine.keys().zip(refined_messages) { + let message = messages + .get_mut(nonce) + .expect("`messages_to_refine` is a subset of `messages`; qed"); + log::trace!( + target: "bridge", + "Refined weight of {}->{} message {:?}/{}: at-source: {}, at-target: {}", + P::SourceChain::NAME, + P::TargetChain::NAME, + self.lane_id, + nonce, + message.dispatch_weight, + refined_message.dispatch_weight, + ); + message.dispatch_weight = refined_message.dispatch_weight; + } + } + + Ok(messages) } async fn prove_messages( @@ -483,7 +571,7 @@ where } fn make_message_details_map( - weights: Vec>, + weights: Vec>, nonces: RangeInclusive, ) -> Result, SubstrateError> { let make_missing_nonce_error = |expected_nonce| { @@ -558,10 +646,10 @@ mod tests { fn message_details_from_rpc( nonces: RangeInclusive, - ) -> Vec> { + ) -> Vec> { nonces .into_iter() - .map(|nonce| bp_messages::MessageDetails { + .map(|nonce| bp_messages::OutboundMessageDetails { nonce, dispatch_weight: 0, size: 0,