estimate transaction fee (#1015)

This commit is contained in:
Svyatoslav Nikolsky
2021-06-28 12:13:53 +03:00
committed by Bastian Köcher
parent 165730a2c2
commit db0216dabb
20 changed files with 294 additions and 72 deletions
@@ -127,11 +127,11 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
/// Millau node as messages source.
type MillauSourceClient =
SubstrateMessagesSource<Millau, MillauMessagesToRialto, millau_runtime::WithRialtoMessagesInstance>;
SubstrateMessagesSource<Millau, Rialto, MillauMessagesToRialto, millau_runtime::WithRialtoMessagesInstance>;
/// Rialto node as messages target.
type RialtoTargetClient =
SubstrateMessagesTarget<Rialto, MillauMessagesToRialto, rialto_runtime::WithMillauMessagesInstance>;
SubstrateMessagesTarget<Millau, Rialto, MillauMessagesToRialto, rialto_runtime::WithMillauMessagesInstance>;
/// Run Millau-to-Rialto messages sync.
pub async fn run(
@@ -127,11 +127,11 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
/// Rialto node as messages source.
type RialtoSourceClient =
SubstrateMessagesSource<Rialto, RialtoMessagesToMillau, rialto_runtime::WithMillauMessagesInstance>;
SubstrateMessagesSource<Rialto, Millau, RialtoMessagesToMillau, rialto_runtime::WithMillauMessagesInstance>;
/// Millau node as messages target.
type MillauTargetClient =
SubstrateMessagesTarget<Millau, RialtoMessagesToMillau, millau_runtime::WithRialtoMessagesInstance>;
SubstrateMessagesTarget<Rialto, Millau, RialtoMessagesToMillau, millau_runtime::WithRialtoMessagesInstance>;
/// Run Rialto-to-Millau messages sync.
pub async fn run(
@@ -124,12 +124,20 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
}
/// Rococo node as messages source.
type RococoSourceClient =
SubstrateMessagesSource<Rococo, RococoMessagesToWococo, relay_rococo_client::runtime::WithWococoMessagesInstance>;
type RococoSourceClient = SubstrateMessagesSource<
Rococo,
Wococo,
RococoMessagesToWococo,
relay_rococo_client::runtime::WithWococoMessagesInstance,
>;
/// Wococo node as messages target.
type WococoTargetClient =
SubstrateMessagesTarget<Wococo, RococoMessagesToWococo, relay_wococo_client::runtime::WithRococoMessagesInstance>;
type WococoTargetClient = SubstrateMessagesTarget<
Rococo,
Wococo,
RococoMessagesToWococo,
relay_wococo_client::runtime::WithRococoMessagesInstance,
>;
/// Run Rococo-to-Wococo messages sync.
pub async fn run(
@@ -124,12 +124,20 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
}
/// Wococo node as messages source.
type WococoSourceClient =
SubstrateMessagesSource<Wococo, WococoMessagesToRococo, relay_wococo_client::runtime::WithRococoMessagesInstance>;
type WococoSourceClient = SubstrateMessagesSource<
Wococo,
Rococo,
WococoMessagesToRococo,
relay_wococo_client::runtime::WithRococoMessagesInstance,
>;
/// Rococo node as messages target.
type RococoTargetClient =
SubstrateMessagesTarget<Rococo, WococoMessagesToRococo, relay_rococo_client::runtime::WithWococoMessagesInstance>;
type RococoTargetClient = SubstrateMessagesTarget<
Wococo,
Rococo,
WococoMessagesToRococo,
relay_rococo_client::runtime::WithWococoMessagesInstance,
>;
/// Run Wococo-to-Rococo messages sync.
pub async fn run(
@@ -19,12 +19,15 @@
//! <BridgedName> 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<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
/// Substrate client as Substrate messages source.
pub struct SubstrateMessagesSource<C: Chain, P: SubstrateMessageLane, I> {
client: Client<C>,
pub struct SubstrateMessagesSource<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> {
client: Client<SC>,
lane: P,
lane_id: LaneId,
instance: ChainId,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<TC>>,
_phantom: PhantomData<I>,
}
impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesSource<C, P, I> {
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> SubstrateMessagesSource<SC, TC, P, I> {
/// Create new Substrate headers source.
pub fn new(
client: Client<C>,
client: Client<SC>,
lane: P,
lane_id: LaneId,
instance: ChainId,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<TC>>,
) -> Self {
SubstrateMessagesSource {
client,
@@ -74,7 +78,7 @@ impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesSource<C, P, I> {
}
}
impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesSource<C, P, I> {
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesSource<SC, TC, P, I> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
@@ -88,9 +92,10 @@ impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesSource<C,
}
#[async_trait]
impl<C, P, I> RelayClient for SubstrateMessagesSource<C, P, I>
impl<SC, TC, P, I> RelayClient for SubstrateMessagesSource<SC, TC, P, I>
where
C: Chain,
SC: Chain,
TC: Chain,
P: SubstrateMessageLane,
I: Send + Sync + Instance,
{
@@ -102,20 +107,22 @@ where
}
#[async_trait]
impl<C, P, I> SourceClient<P> for SubstrateMessagesSource<C, P, I>
impl<SC, TC, P, I> SourceClient<P> for SubstrateMessagesSource<SC, TC, P, I>
where
C: Chain,
C::Header: DeserializeOwned,
C::Index: DeserializeOwned,
C::BlockNumber: BlockNumberBase,
SC: Chain<Hash = P::SourceHeaderHash, BlockNumber = P::SourceHeaderNumber, Balance = P::SourceChainBalance>,
SC::Hash: Copy,
SC::BlockNumber: Copy,
SC::Balance: Decode + Bounded,
SC::Header: DeserializeOwned,
SC::Index: DeserializeOwned,
SC::BlockNumber: BlockNumberBase,
TC: Chain<Hash = P::TargetHeaderHash, BlockNumber = P::TargetHeaderNumber>,
P: SubstrateMessageLane<
MessagesProof = SubstrateMessagesProof<C>,
SourceChainBalance = C::Balance,
SourceHeaderNumber = <C::Header as HeaderT>::Number,
SourceHeaderHash = <C::Header as HeaderT>::Hash,
SourceChain = C,
MessagesProof = SubstrateMessagesProof<SC>,
MessagesReceivingProof = SubstrateMessagesReceivingProof<TC>,
SourceChain = SC,
TargetChain = TC,
>,
P::TargetChain: Chain<Hash = P::TargetHeaderHash, BlockNumber = P::TargetHeaderNumber>,
P::TargetHeaderNumber: Decode,
P::TargetHeaderHash: Decode,
I: Send + Sync + Instance,
@@ -180,7 +187,7 @@ where
)
.await?;
make_message_details_map::<C>(
make_message_details_map::<SC>(
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::<SC, TC>(),
))
.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<SC: Chain, TC: Chain>() -> SubstrateMessagesReceivingProof<TC> {
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<SelfChain, BridgedHeaderHash, BridgedHeaderNumber>(
self_client: &Client<SelfChain>,
best_finalized_header_id_method_name: &str,
@@ -295,7 +332,7 @@ fn make_message_details_map<C: Chain>(
) -> Result<MessageDetailsMap<C::Balance>, 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<C: Chain>(
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<C: Chain>(
#[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<MessageNonce>,
@@ -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::<Rialto, Millau>();
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(),
);
}
}
@@ -19,24 +19,27 @@
//! <BridgedName> 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<C> = (
@@ -45,23 +48,23 @@ pub type SubstrateMessagesReceivingProof<C> = (
);
/// Substrate client as Substrate messages target.
pub struct SubstrateMessagesTarget<C: Chain, P: SubstrateMessageLane, I> {
client: Client<C>,
pub struct SubstrateMessagesTarget<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> {
client: Client<TC>,
lane: P,
lane_id: LaneId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<SC>>,
_phantom: PhantomData<I>,
}
impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesTarget<C, P, I> {
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> SubstrateMessagesTarget<SC, TC, P, I> {
/// Create new Substrate headers target.
pub fn new(
client: Client<C>,
client: Client<TC>,
lane: P,
lane_id: LaneId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<SC>>,
) -> Self {
SubstrateMessagesTarget {
client,
@@ -74,7 +77,7 @@ impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesTarget<C, P, I> {
}
}
impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesTarget<C, P, I> {
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesTarget<SC, TC, P, I> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
@@ -88,9 +91,10 @@ impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesTarget<C,
}
#[async_trait]
impl<C, P, I> RelayClient for SubstrateMessagesTarget<C, P, I>
impl<SC, TC, P, I> RelayClient for SubstrateMessagesTarget<SC, TC, P, I>
where
C: Chain,
SC: Chain,
TC: Chain,
P: SubstrateMessageLane,
I: Send + Sync + Instance,
{
@@ -102,19 +106,22 @@ where
}
#[async_trait]
impl<C, P, I> TargetClient<P> for SubstrateMessagesTarget<C, P, I>
impl<SC, TC, P, I> TargetClient<P> for SubstrateMessagesTarget<SC, TC, P, I>
where
C: Chain,
C::Header: DeserializeOwned,
C::Index: DeserializeOwned,
<C::Header as HeaderT>::Number: BlockNumberBase,
SC: Chain<Hash = P::SourceHeaderHash, BlockNumber = P::SourceHeaderNumber, Balance = P::SourceChainBalance>,
SC::Balance: TryFrom<TC::Balance> + Bounded,
TC: Chain<Hash = P::TargetHeaderHash, BlockNumber = P::TargetHeaderNumber>,
TC::Hash: Copy,
TC::BlockNumber: Copy,
TC::Header: DeserializeOwned,
TC::Index: DeserializeOwned,
<TC::Header as HeaderT>::Number: BlockNumberBase,
P: SubstrateMessageLane<
TargetChain = C,
MessagesReceivingProof = SubstrateMessagesReceivingProof<C>,
TargetHeaderNumber = <C::Header as HeaderT>::Number,
TargetHeaderHash = <C::Header as HeaderT>::Hash,
MessagesProof = SubstrateMessagesProof<SC>,
MessagesReceivingProof = SubstrateMessagesReceivingProof<TC>,
SourceChain = SC,
TargetChain = TC,
>,
P::SourceChain: Chain<Hash = P::SourceHeaderHash, BlockNumber = P::SourceHeaderNumber>,
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<MessageNonce>,
_total_dispatch_weight: Weight,
_total_size: u32,
nonces: RangeInclusive<MessageNonce>,
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::<SC, TC>(
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::<SC>(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<SC: Chain>(
nonces: RangeInclusive<MessageNonce>,
total_dispatch_weight: Weight,
total_size: u32,
) -> SubstrateMessagesProof<SC> {
(
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<SC: Chain, TC: Chain>(
target_to_source_conversion_rate: FixedU128,
target_transaction_fee: TC::Balance,
) -> SC::Balance
where
SC::Balance: TryFrom<TC::Balance>,
{
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::<Rialto>(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::<Rialto, Millau>((150, 100).into(), 1_000),
1_500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rialto, Millau>((50, 100).into(), 1_000),
500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rialto, Millau>((100, 100).into(), 1_000),
1_000
);
}
}