diff --git a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index 31dc51e9c2..9932d16a42 100644 --- a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -127,11 +127,11 @@ impl SubstrateMessageLane for MillauMessagesToRialto { /// Millau node as messages source. type MillauSourceClient = - SubstrateMessagesSource; + SubstrateMessagesSource; /// Rialto node as messages target. type RialtoTargetClient = - SubstrateMessagesTarget; + SubstrateMessagesTarget; /// Run Millau-to-Rialto messages sync. pub async fn run( diff --git a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 89f9dd7e99..f472703993 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -127,11 +127,11 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { /// Rialto node as messages source. type RialtoSourceClient = - SubstrateMessagesSource; + SubstrateMessagesSource; /// Millau node as messages target. type MillauTargetClient = - SubstrateMessagesTarget; + SubstrateMessagesTarget; /// Run Rialto-to-Millau messages sync. pub async fn run( diff --git a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index be5f91116e..f8da60cf17 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -124,12 +124,20 @@ impl SubstrateMessageLane for RococoMessagesToWococo { } /// Rococo node as messages source. -type RococoSourceClient = - SubstrateMessagesSource; +type RococoSourceClient = SubstrateMessagesSource< + Rococo, + Wococo, + RococoMessagesToWococo, + relay_rococo_client::runtime::WithWococoMessagesInstance, +>; /// Wococo node as messages target. -type WococoTargetClient = - SubstrateMessagesTarget; +type WococoTargetClient = SubstrateMessagesTarget< + Rococo, + Wococo, + RococoMessagesToWococo, + relay_wococo_client::runtime::WithRococoMessagesInstance, +>; /// Run Rococo-to-Wococo messages sync. pub async fn run( diff --git a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index b696801569..2e65328831 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -124,12 +124,20 @@ impl SubstrateMessageLane for WococoMessagesToRococo { } /// Wococo node as messages source. -type WococoSourceClient = - SubstrateMessagesSource; +type WococoSourceClient = SubstrateMessagesSource< + Wococo, + Rococo, + WococoMessagesToRococo, + relay_wococo_client::runtime::WithRococoMessagesInstance, +>; /// Rococo node as messages target. -type RococoTargetClient = - SubstrateMessagesTarget; +type RococoTargetClient = SubstrateMessagesTarget< + Wococo, + Rococo, + WococoMessagesToRococo, + relay_rococo_client::runtime::WithWococoMessagesInstance, +>; /// Run Wococo-to-Rococo messages sync. pub async fn run( diff --git a/bridges/relays/bin-substrate/src/messages_source.rs b/bridges/relays/bin-substrate/src/messages_source.rs index 88c8b529dc..2271d3e4f1 100644 --- a/bridges/relays/bin-substrate/src/messages_source.rs +++ b/bridges/relays/bin-substrate/src/messages_source.rs @@ -19,12 +19,15 @@ //! chain. use crate::messages_lane::SubstrateMessageLane; +use crate::messages_target::SubstrateMessagesReceivingProof; use crate::on_demand_headers::OnDemandHeadersRelay; use async_trait::async_trait; -use bp_messages::{LaneId, MessageNonce}; +use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; use bp_runtime::{messages::DispatchFeePayment, ChainId}; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use bridge_runtime_common::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, +}; use codec::{Decode, Encode}; use frame_support::{traits::Instance, weights::Weight}; use messages_relay::{ @@ -33,6 +36,7 @@ use messages_relay::{ ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, SourceClientState, }, }; +use num_traits::{Bounded, Zero}; use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf}; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; @@ -45,23 +49,23 @@ use std::{marker::PhantomData, ops::RangeInclusive}; pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); /// Substrate client as Substrate messages source. -pub struct SubstrateMessagesSource { - client: Client, +pub struct SubstrateMessagesSource { + client: Client, lane: P, lane_id: LaneId, instance: ChainId, - target_to_source_headers_relay: Option>, + target_to_source_headers_relay: Option>, _phantom: PhantomData, } -impl SubstrateMessagesSource { +impl SubstrateMessagesSource { /// Create new Substrate headers source. pub fn new( - client: Client, + client: Client, lane: P, lane_id: LaneId, instance: ChainId, - target_to_source_headers_relay: Option>, + target_to_source_headers_relay: Option>, ) -> Self { SubstrateMessagesSource { client, @@ -74,7 +78,7 @@ impl SubstrateMessagesSource { } } -impl Clone for SubstrateMessagesSource { +impl Clone for SubstrateMessagesSource { fn clone(&self) -> Self { Self { client: self.client.clone(), @@ -88,9 +92,10 @@ impl Clone for SubstrateMessagesSource RelayClient for SubstrateMessagesSource +impl RelayClient for SubstrateMessagesSource where - C: Chain, + SC: Chain, + TC: Chain, P: SubstrateMessageLane, I: Send + Sync + Instance, { @@ -102,20 +107,22 @@ where } #[async_trait] -impl SourceClient

for SubstrateMessagesSource +impl SourceClient

for SubstrateMessagesSource where - C: Chain, - C::Header: DeserializeOwned, - C::Index: DeserializeOwned, - C::BlockNumber: BlockNumberBase, + SC: Chain, + SC::Hash: Copy, + SC::BlockNumber: Copy, + SC::Balance: Decode + Bounded, + SC::Header: DeserializeOwned, + SC::Index: DeserializeOwned, + SC::BlockNumber: BlockNumberBase, + TC: Chain, P: SubstrateMessageLane< - MessagesProof = SubstrateMessagesProof, - SourceChainBalance = C::Balance, - SourceHeaderNumber = ::Number, - SourceHeaderHash = ::Hash, - SourceChain = C, + MessagesProof = SubstrateMessagesProof, + MessagesReceivingProof = SubstrateMessagesReceivingProof, + SourceChain = SC, + TargetChain = TC, >, - P::TargetChain: Chain, P::TargetHeaderNumber: Decode, P::TargetHeaderHash: Decode, I: Send + Sync + Instance, @@ -180,7 +187,7 @@ where ) .await?; - make_message_details_map::( + make_message_details_map::( Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?, nonces, ) @@ -242,10 +249,40 @@ where } async fn estimate_confirmation_transaction(&self) -> P::SourceChainBalance { - num_traits::Zero::zero() // TODO: https://github.com/paritytech/parity-bridges-common/issues/997 + self.client + .estimate_extrinsic_fee(self.lane.make_messages_receiving_proof_transaction( + Zero::zero(), + HeaderId(Default::default(), Default::default()), + prepare_dummy_messages_delivery_proof::(), + )) + .await + .unwrap_or_else(|_| SC::Balance::max_value()) } } +/// Prepare 'dummy' messages delivery proof that will compose the delivery confirmation transaction. +/// +/// We don't care about proof actually being the valid proof, because its validity doesn't +/// affect the call weight - we only care about its size. +fn prepare_dummy_messages_delivery_proof() -> SubstrateMessagesReceivingProof { + let single_message_confirmation_size = + bp_messages::InboundLaneData::<()>::encoded_size_hint(SC::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1, 1) + .unwrap_or(u32::MAX); + let proof_size = TC::STORAGE_PROOF_OVERHEAD.saturating_add(single_message_confirmation_size); + ( + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + }, + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: vec![vec![0; proof_size as usize]], + lane: Default::default(), + }, + ) +} + pub async fn read_client_state( self_client: &Client, best_finalized_header_id_method_name: &str, @@ -295,7 +332,7 @@ fn make_message_details_map( ) -> Result, SubstrateError> { let make_missing_nonce_error = |expected_nonce| { Err(SubstrateError::Custom(format!( - "Missing nonce {} in messages_dispatch_weight call result. Expected all nonces from {:?}", + "Missing nonce {} in message_details call result. Expected all nonces from {:?}", expected_nonce, nonces, ))) }; @@ -346,8 +383,7 @@ fn make_message_details_map( MessageDetails { dispatch_weight: details.dispatch_weight, size: details.size as _, - // TODO: https://github.com/paritytech/parity-bridges-common/issues/997 - reward: num_traits::Zero::zero(), + reward: details.delivery_and_dispatch_fee, dispatch_fee_payment: DispatchFeePayment::AtSourceChain, }, ); @@ -361,6 +397,9 @@ fn make_message_details_map( #[cfg(test)] mod tests { use super::*; + use bp_runtime::messages::DispatchFeePayment; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; fn message_details_from_rpc( nonces: RangeInclusive, @@ -469,4 +508,16 @@ mod tests { Err(SubstrateError::Custom(_)) )); } + + #[test] + fn prepare_dummy_messages_delivery_proof_works() { + let expected_minimal_size = Rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE + Millau::STORAGE_PROOF_OVERHEAD; + let dummy_proof = prepare_dummy_messages_delivery_proof::(); + assert!( + dummy_proof.1.encode().len() as u32 > expected_minimal_size, + "Expected proof size at least {}. Got: {}", + expected_minimal_size, + dummy_proof.1.encode().len(), + ); + } } diff --git a/bridges/relays/bin-substrate/src/messages_target.rs b/bridges/relays/bin-substrate/src/messages_target.rs index f74efbe61b..71f51c1e05 100644 --- a/bridges/relays/bin-substrate/src/messages_target.rs +++ b/bridges/relays/bin-substrate/src/messages_target.rs @@ -19,24 +19,27 @@ //! chain. use crate::messages_lane::SubstrateMessageLane; -use crate::messages_source::read_client_state; +use crate::messages_source::{read_client_state, SubstrateMessagesProof}; use crate::on_demand_headers::OnDemandHeadersRelay; use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; use bp_runtime::ChainId; -use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; +use bridge_runtime_common::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, +}; use codec::{Decode, Encode}; use frame_support::{traits::Instance, weights::Weight}; use messages_relay::{ message_lane::{SourceHeaderIdOf, TargetHeaderIdOf}, message_lane_loop::{TargetClient, TargetClientState}, }; +use num_traits::{Bounded, One, Zero}; use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf}; -use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase}; +use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; -use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; -use std::{marker::PhantomData, ops::RangeInclusive}; +use sp_runtime::{traits::Header as HeaderT, DeserializeOwned, FixedPointNumber, FixedU128}; +use std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. pub type SubstrateMessagesReceivingProof = ( @@ -45,23 +48,23 @@ pub type SubstrateMessagesReceivingProof = ( ); /// Substrate client as Substrate messages target. -pub struct SubstrateMessagesTarget { - client: Client, +pub struct SubstrateMessagesTarget { + client: Client, lane: P, lane_id: LaneId, instance: ChainId, - source_to_target_headers_relay: Option>, + source_to_target_headers_relay: Option>, _phantom: PhantomData, } -impl SubstrateMessagesTarget { +impl SubstrateMessagesTarget { /// Create new Substrate headers target. pub fn new( - client: Client, + client: Client, lane: P, lane_id: LaneId, instance: ChainId, - source_to_target_headers_relay: Option>, + source_to_target_headers_relay: Option>, ) -> Self { SubstrateMessagesTarget { client, @@ -74,7 +77,7 @@ impl SubstrateMessagesTarget { } } -impl Clone for SubstrateMessagesTarget { +impl Clone for SubstrateMessagesTarget { fn clone(&self) -> Self { Self { client: self.client.clone(), @@ -88,9 +91,10 @@ impl Clone for SubstrateMessagesTarget RelayClient for SubstrateMessagesTarget +impl RelayClient for SubstrateMessagesTarget where - C: Chain, + SC: Chain, + TC: Chain, P: SubstrateMessageLane, I: Send + Sync + Instance, { @@ -102,19 +106,22 @@ where } #[async_trait] -impl TargetClient

for SubstrateMessagesTarget +impl TargetClient

for SubstrateMessagesTarget where - C: Chain, - C::Header: DeserializeOwned, - C::Index: DeserializeOwned, - ::Number: BlockNumberBase, + SC: Chain, + SC::Balance: TryFrom + Bounded, + TC: Chain, + TC::Hash: Copy, + TC::BlockNumber: Copy, + TC::Header: DeserializeOwned, + TC::Index: DeserializeOwned, + ::Number: BlockNumberBase, P: SubstrateMessageLane< - TargetChain = C, - MessagesReceivingProof = SubstrateMessagesReceivingProof, - TargetHeaderNumber = ::Number, - TargetHeaderHash = ::Hash, + MessagesProof = SubstrateMessagesProof, + MessagesReceivingProof = SubstrateMessagesReceivingProof, + SourceChain = SC, + TargetChain = TC, >, - P::SourceChain: Chain, P::SourceHeaderNumber: Decode, P::SourceHeaderHash: Decode, I: Send + Sync + Instance, @@ -229,10 +236,93 @@ where async fn estimate_delivery_transaction_in_source_tokens( &self, - _nonces: RangeInclusive, - _total_dispatch_weight: Weight, - _total_size: u32, + nonces: RangeInclusive, + total_dispatch_weight: Weight, + total_size: u32, ) -> P::SourceChainBalance { - num_traits::Zero::zero() // TODO: https://github.com/paritytech/parity-bridges-common/issues/997 + // TODO: use actual rate (https://github.com/paritytech/parity-bridges-common/issues/997) + convert_target_tokens_to_source_tokens::( + FixedU128::one(), + self.client + .estimate_extrinsic_fee(self.lane.make_messages_delivery_transaction( + Zero::zero(), + HeaderId(Default::default(), Default::default()), + nonces.clone(), + prepare_dummy_messages_proof::(nonces, total_dispatch_weight, total_size), + )) + .await + .unwrap_or_else(|_| TC::Balance::max_value()), + ) + } +} + +/// Prepare 'dummy' messages proof that will compose the delivery transaction. +/// +/// We don't care about proof actually being the valid proof, because its validity doesn't +/// affect the call weight - we only care about its size. +fn prepare_dummy_messages_proof( + nonces: RangeInclusive, + total_dispatch_weight: Weight, + total_size: u32, +) -> SubstrateMessagesProof { + ( + total_dispatch_weight, + FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![vec![0; SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize]], + lane: Default::default(), + nonces_start: *nonces.start(), + nonces_end: *nonces.end(), + }, + ) +} + +/// Given delivery transaction fee in target chain tokens and conversion rate to the source +/// chain tokens, compute transaction cost in source chain tokens. +fn convert_target_tokens_to_source_tokens( + target_to_source_conversion_rate: FixedU128, + target_transaction_fee: TC::Balance, +) -> SC::Balance +where + SC::Balance: TryFrom, +{ + SC::Balance::try_from(target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee)) + .unwrap_or_else(|_| SC::Balance::max_value()) +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; + + #[test] + fn prepare_dummy_messages_proof_works() { + const DISPATCH_WEIGHT: Weight = 1_000_000; + const SIZE: u32 = 1_000; + let dummy_proof = prepare_dummy_messages_proof::(1..=10, DISPATCH_WEIGHT, SIZE); + assert_eq!(dummy_proof.0, DISPATCH_WEIGHT); + assert!( + dummy_proof.1.encode().len() as u32 > SIZE, + "Expected proof size at least {}. Got: {}", + SIZE, + dummy_proof.1.encode().len(), + ); + } + + #[test] + fn convert_target_tokens_to_source_tokens_works() { + assert_eq!( + convert_target_tokens_to_source_tokens::((150, 100).into(), 1_000), + 1_500 + ); + assert_eq!( + convert_target_tokens_to_source_tokens::((50, 100).into(), 1_000), + 500 + ); + assert_eq!( + convert_target_tokens_to_source_tokens::((100, 100).into(), 1_000), + 1_000 + ); } } diff --git a/bridges/relays/client-kusama/src/lib.rs b/bridges/relays/client-kusama/src/lib.rs index f2fba32dc1..01869fb7b1 100644 --- a/bridges/relays/client-kusama/src/lib.rs +++ b/bridges/relays/client-kusama/src/lib.rs @@ -36,6 +36,8 @@ impl ChainBase for Kusama { impl Chain for Kusama { const NAME: &'static str = "Kusama"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const STORAGE_PROOF_OVERHEAD: u32 = bp_kusama::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_kusama::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = bp_kusama::AccountId; type Index = bp_kusama::Nonce; diff --git a/bridges/relays/client-millau/Cargo.toml b/bridges/relays/client-millau/Cargo.toml index e16f06f852..5f40e1e557 100644 --- a/bridges/relays/client-millau/Cargo.toml +++ b/bridges/relays/client-millau/Cargo.toml @@ -13,6 +13,7 @@ relay-utils = { path = "../utils" } # Supported Chains +bp-millau = { path = "../../primitives/chain-millau" } millau-runtime = { path = "../../bin/millau/runtime" } # Substrate Dependencies diff --git a/bridges/relays/client-millau/src/lib.rs b/bridges/relays/client-millau/src/lib.rs index 8597d9e592..ac4368392c 100644 --- a/bridges/relays/client-millau/src/lib.rs +++ b/bridges/relays/client-millau/src/lib.rs @@ -39,6 +39,8 @@ impl ChainBase for Millau { impl Chain for Millau { const NAME: &'static str = "Millau"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + const STORAGE_PROOF_OVERHEAD: u32 = bp_millau::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = millau_runtime::AccountId; type Index = millau_runtime::Index; diff --git a/bridges/relays/client-polkadot/src/lib.rs b/bridges/relays/client-polkadot/src/lib.rs index e502463187..04ddce29d0 100644 --- a/bridges/relays/client-polkadot/src/lib.rs +++ b/bridges/relays/client-polkadot/src/lib.rs @@ -36,6 +36,8 @@ impl ChainBase for Polkadot { impl Chain for Polkadot { const NAME: &'static str = "Polkadot"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const STORAGE_PROOF_OVERHEAD: u32 = bp_polkadot::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_polkadot::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = bp_polkadot::AccountId; type Index = bp_polkadot::Nonce; diff --git a/bridges/relays/client-rialto/Cargo.toml b/bridges/relays/client-rialto/Cargo.toml index 88e8e12add..a1c30da389 100644 --- a/bridges/relays/client-rialto/Cargo.toml +++ b/bridges/relays/client-rialto/Cargo.toml @@ -13,6 +13,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies +bp-rialto = { path = "../../primitives/chain-rialto" } rialto-runtime = { path = "../../bin/rialto/runtime" } # Substrate Dependencies diff --git a/bridges/relays/client-rialto/src/lib.rs b/bridges/relays/client-rialto/src/lib.rs index 4a0023a87c..646d762923 100644 --- a/bridges/relays/client-rialto/src/lib.rs +++ b/bridges/relays/client-rialto/src/lib.rs @@ -39,6 +39,8 @@ impl ChainBase for Rialto { impl Chain for Rialto { const NAME: &'static str = "Rialto"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = rialto_runtime::AccountId; type Index = rialto_runtime::Index; diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs index 5a7d8999f7..4d4812e459 100644 --- a/bridges/relays/client-rococo/src/lib.rs +++ b/bridges/relays/client-rococo/src/lib.rs @@ -44,6 +44,8 @@ impl ChainBase for Rococo { impl Chain for Rococo { const NAME: &'static str = "Rococo"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const STORAGE_PROOF_OVERHEAD: u32 = bp_rococo::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = bp_rococo::AccountId; type Index = bp_rococo::Index; diff --git a/bridges/relays/client-substrate/Cargo.toml b/bridges/relays/client-substrate/Cargo.toml index f5c2e26560..4d7b6a0472 100644 --- a/bridges/relays/client-substrate/Cargo.toml +++ b/bridges/relays/client-substrate/Cargo.toml @@ -29,9 +29,12 @@ relay-utils = { path = "../utils" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 4cc8a0394d..63432c9701 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -17,14 +17,15 @@ use bp_runtime::Chain as ChainBase; use frame_support::Parameter; use jsonrpsee_ws_client::{DeserializeOwned, Serialize}; -use num_traits::{CheckedSub, SaturatingAdd, Zero}; +use num_traits::{Bounded, CheckedSub, SaturatingAdd, Zero}; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{ generic::SignedBlock, traits::{ - AtLeast32Bit, Block as BlockT, Dispatchable, MaybeDisplay, MaybeSerialize, MaybeSerializeDeserialize, Member, + AtLeast32Bit, AtLeast32BitUnsigned, Block as BlockT, Dispatchable, MaybeDisplay, MaybeSerialize, + MaybeSerializeDeserialize, Member, }, - EncodedJustification, + EncodedJustification, FixedPointOperand, }; use std::{fmt::Debug, time::Duration}; @@ -37,6 +38,10 @@ pub trait Chain: ChainBase + Clone { /// How often blocks are produced on that chain. It's suggested to set this value /// to match the block time of the chain. const AVERAGE_BLOCK_INTERVAL: Duration; + /// Maximal expected storage proof overhead (in bytes). + const STORAGE_PROOF_OVERHEAD: u32; + /// Maximal size (in bytes) of SCALE-encoded account id on this chain. + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32; /// The user account identifier type for the runtime. type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + Default; @@ -58,7 +63,20 @@ pub trait Chain: ChainBase + Clone { /// /// The chain may suport multiple tokens, but this particular type is for token that is used /// to pay for transaction dispatch, to reward different relayers (headers, messages), etc. - type Balance: Parameter + Member + DeserializeOwned + Clone + Copy + CheckedSub + PartialOrd + SaturatingAdd + Zero; + type Balance: AtLeast32BitUnsigned + + FixedPointOperand + + Parameter + + Parameter + + Member + + DeserializeOwned + + Clone + + Copy + + Bounded + + CheckedSub + + PartialOrd + + SaturatingAdd + + Zero + + std::convert::TryFrom; } /// Substrate-based chain with `frame_system::Config::AccountData` set to diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index f0b7158ecb..87917c022c 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -25,12 +25,14 @@ use codec::Decode; use frame_system::AccountInfo; use jsonrpsee_ws_client::{traits::SubscriptionClient, v2::params::JsonRpcParams, DeserializeOwned}; use jsonrpsee_ws_client::{Subscription, WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}; -use num_traits::Zero; +use num_traits::{Bounded, Zero}; use pallet_balances::AccountData; +use pallet_transaction_payment::InclusionFee; use relay_utils::relay_loop::RECONNECT_DELAY; use sp_core::{storage::StorageKey, Bytes}; use sp_trie::StorageProof; use sp_version::RuntimeVersion; +use std::convert::TryFrom; const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; @@ -258,6 +260,26 @@ impl Client { Ok(tx_hash) } + /// Estimate fee that will be spent on given extrinsic. + pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result { + let fee_details = Substrate::::payment_query_fee_details(&*self.client, transaction, None).await?; + let inclusion_fee = fee_details + .inclusion_fee + .map(|inclusion_fee| { + InclusionFee { + base_fee: C::Balance::try_from(inclusion_fee.base_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + adjusted_weight_fee: C::Balance::try_from(inclusion_fee.adjusted_weight_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + } + .inclusion_fee() + }) + .unwrap_or_else(Zero::zero); + Ok(inclusion_fee) + } + /// Get the GRANDPA authority set at given block. pub async fn grandpa_authorities_set(&self, block: C::Hash) -> Result { let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); diff --git a/bridges/relays/client-substrate/src/guard.rs b/bridges/relays/client-substrate/src/guard.rs index c6e191ce07..e7159bf271 100644 --- a/bridges/relays/client-substrate/src/guard.rs +++ b/bridges/relays/client-substrate/src/guard.rs @@ -185,6 +185,8 @@ mod tests { impl Chain for TestChain { const NAME: &'static str = "Test"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(1); + const STORAGE_PROOF_OVERHEAD: u32 = 0; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 0; type AccountId = u32; type Index = u32; diff --git a/bridges/relays/client-substrate/src/rpc.rs b/bridges/relays/client-substrate/src/rpc.rs index 06df1f705d..ddc8c92f77 100644 --- a/bridges/relays/client-substrate/src/rpc.rs +++ b/bridges/relays/client-substrate/src/rpc.rs @@ -18,11 +18,13 @@ use crate::chain::Chain; +use pallet_transaction_payment_rpc_runtime_api::FeeDetails; use sc_rpc_api::{state::ReadProof, system::Health}; use sp_core::{ storage::{StorageData, StorageKey}, Bytes, }; +use sp_rpc::number::NumberOrHex; use sp_version::RuntimeVersion; jsonrpsee_proc_macros::rpc_client_api! { @@ -49,5 +51,7 @@ jsonrpsee_proc_macros::rpc_client_api! { fn state_prove_storage(keys: Vec, hash: Option) -> ReadProof; #[rpc(method = "state_getRuntimeVersion", positional_params)] fn state_runtime_version() -> RuntimeVersion; + #[rpc(method = "payment_queryFeeDetails", positional_params)] + fn payment_query_fee_details(extrinsic: Bytes, at_block: Option) -> FeeDetails; } } diff --git a/bridges/relays/client-westend/src/lib.rs b/bridges/relays/client-westend/src/lib.rs index 6768b81f10..b2347da502 100644 --- a/bridges/relays/client-westend/src/lib.rs +++ b/bridges/relays/client-westend/src/lib.rs @@ -42,6 +42,8 @@ impl ChainBase for Westend { impl Chain for Westend { const NAME: &'static str = "Westend"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_westend::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = bp_westend::AccountId; type Index = bp_westend::Nonce; diff --git a/bridges/relays/client-wococo/src/lib.rs b/bridges/relays/client-wococo/src/lib.rs index 8ceba7c7c4..3ef55223b5 100644 --- a/bridges/relays/client-wococo/src/lib.rs +++ b/bridges/relays/client-wococo/src/lib.rs @@ -44,6 +44,8 @@ impl ChainBase for Wococo { impl Chain for Wococo { const NAME: &'static str = "Wococo"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const STORAGE_PROOF_OVERHEAD: u32 = bp_wococo::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type AccountId = bp_wococo::AccountId; type Index = bp_wococo::Index;