Remove message fee + message send calls (#1642)

* remove message fee

* it is compiling!

* fixes + fmt

* more cleanup

* more cleanup

* restore MessageDeliveryAndDispatchPayment since we'll need relayer rewards

* started rational relayer removal

* more removal

* removed estimate fee subcommand

* remove DispatchFeePayment

* more removals

* removed conversion rates && some metrics

* - unneeded associated type

* - OutboundMessageFee

* fix benchmarks compilation

* fmt

* test + fix benchmarks

* fix send message

* clippy
This commit is contained in:
Svyatoslav Nikolsky
2022-11-18 12:24:45 +03:00
committed by Bastian Köcher
parent 1217b2cf80
commit 8c845602cf
92 changed files with 589 additions and 5796 deletions
@@ -15,7 +15,6 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::CliChain;
use messages_relay::relay_strategy::MixStrategy;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use parachains_relay::ParachainsPipeline;
use relay_substrate_client::{AccountKeyPairOf, Chain, ChainWithTransactions, RelayChain};
@@ -46,7 +45,7 @@ impl FullBridge {
Self::MillauToRialtoParachain => MILLAU_TO_RIALTO_PARACHAIN_INDEX,
Self::RialtoParachainToMillau => RIALTO_PARACHAIN_TO_MILLAU_INDEX,
Self::BridgeHubRococoToBridgeHubWococo | Self::BridgeHubWococoToBridgeHubRococo =>
unimplemented!("TODO: (bridge_instance_index) do we need it or refactor or remove?"),
unimplemented!("Relay doesn't support send-message subcommand on bridge hubs"),
}
}
}
@@ -102,9 +101,5 @@ pub trait MessagesCliBridge: CliBridgeBase {
/// defined bridge.
const ESTIMATE_MESSAGE_FEE_METHOD: &'static str;
/// The Source -> Destination messages synchronization pipeline.
type MessagesLane: SubstrateMessageLane<
SourceChain = Self::Source,
TargetChain = Self::Target,
RelayStrategy = MixStrategy,
>;
type MessagesLane: SubstrateMessageLane<SourceChain = Self::Source, TargetChain = Self::Target>;
}
@@ -15,8 +15,8 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::{ExplicitOrMaximal, HexBytes};
use bp_messages::LaneId;
use bp_runtime::EncodedOrDecodedCall;
use codec::Encode;
use relay_substrate_client::Chain;
use structopt::StructOpt;
@@ -47,14 +47,6 @@ pub trait CliEncodeMessage: Chain {
message: xcm::VersionedXcm<()>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
/// Encode a send message call of the bridge-messages pallet.
fn encode_send_message_call(
lane: LaneId,
message: RawMessage,
fee: Self::Balance,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
}
/// Encode message payload passed through CLI flags.
@@ -63,15 +55,36 @@ pub(crate) fn encode_message<Source: Chain, Target: Chain>(
) -> anyhow::Result<RawMessage> {
Ok(match message {
Message::Raw { ref data } => data.0.clone(),
Message::Sized { ref size } => match *size {
ExplicitOrMaximal::Explicit(size) => vec![42; size as usize],
ExplicitOrMaximal::Maximal => {
let maximal_size = compute_maximal_message_size(
Message::Sized { ref size } => {
let expected_xcm_size = match *size {
ExplicitOrMaximal::Explicit(size) => size,
ExplicitOrMaximal::Maximal => compute_maximal_message_size(
Source::max_extrinsic_size(),
Target::max_extrinsic_size(),
),
};
// there's no way to craft XCM of the given size - we'll be using `ExpectPallet`
// instruction, which has byte vector inside
let mut current_vec_size = expected_xcm_size;
let xcm = loop {
let xcm = xcm::VersionedXcm::<()>::V3(
vec![xcm::v3::Instruction::ExpectPallet {
index: 0,
name: vec![42; current_vec_size as usize],
module_name: vec![],
crate_major: 0,
min_crate_minor: 0,
}]
.into(),
);
vec![42; maximal_size as usize]
},
if xcm.encode().len() <= expected_xcm_size as usize {
break xcm
}
current_vec_size -= 1;
};
xcm.encode()
},
})
}
@@ -95,3 +108,38 @@ pub(crate) fn compute_maximal_message_size(
maximal_message_size
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::send_message::decode_xcm;
use bp_runtime::Chain;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
#[test]
fn encode_explicit_size_message_works() {
let msg = encode_message::<Rialto, Millau>(&Message::Sized {
size: ExplicitOrMaximal::Explicit(100),
})
.unwrap();
assert_eq!(msg.len(), 100);
// check that it decodes to valid xcm
let _ = decode_xcm(msg).unwrap();
}
#[test]
fn encode_maximal_size_message_works() {
let maximal_size = compute_maximal_message_size(
Rialto::max_extrinsic_size(),
Millau::max_extrinsic_size(),
);
let msg =
encode_message::<Rialto, Millau>(&Message::Sized { size: ExplicitOrMaximal::Maximal })
.unwrap();
assert_eq!(msg.len(), maximal_size as usize);
// check that it decodes to valid xcm
let _ = decode_xcm(msg).unwrap();
}
}
@@ -1,292 +0,0 @@
// Copyright 2019-2021 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 <http://www.gnu.org/licenses/>.
use crate::{
chains::{
millau_headers_to_rialto::MillauToRialtoCliBridge,
millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge,
rialto_headers_to_millau::RialtoToMillauCliBridge,
rialto_parachains_to_millau::RialtoParachainToMillauCliBridge,
},
cli::{
bridge::{FullBridge, MessagesCliBridge},
chain_schema::*,
relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
Balance, HexBytes, HexLaneId,
},
};
use async_trait::async_trait;
use bp_runtime::BalanceOf;
use codec::{Decode, Encode};
use relay_substrate_client::{Chain, ChainBase};
use sp_runtime::FixedU128;
use std::fmt::Display;
use structopt::StructOpt;
use strum::VariantNames;
use substrate_relay_helper::helpers::tokens_conversion_rate_from_metrics;
/// Estimate Delivery & Dispatch Fee command.
#[derive(StructOpt, Debug, PartialEq)]
pub struct EstimateFee {
/// A bridge instance to encode call for.
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
source: SourceConnectionParams,
/// Hex-encoded id of lane that will be delivering the message.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// A way to override conversion rate between bridge tokens.
///
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
/// your message won't be relayed.
#[structopt(long)]
conversion_rate_override: Option<ConversionRateOverride>,
/// Payload to send over the bridge.
#[structopt(flatten)]
payload: crate::cli::encode_message::Message,
}
/// A way to override conversion rate between bridge tokens.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ConversionRateOverride {
/// The actual conversion rate is computed in the same way how rate metric works.
Metric,
/// The actual conversion rate is specified explicitly.
Explicit(f64),
}
impl std::str::FromStr for ConversionRateOverride {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "metric" {
return Ok(ConversionRateOverride::Metric)
}
f64::from_str(s)
.map(ConversionRateOverride::Explicit)
.map_err(|e| format!("Failed to parse '{e:?}'. Expected 'metric' or explicit value"))
}
}
#[async_trait]
trait FeeEstimator: MessagesCliBridge
where
<Self::Source as ChainBase>::Balance: Display + Into<u128>,
{
async fn estimate_fee(data: EstimateFee) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let lane = data.lane.into();
let payload =
crate::cli::encode_message::encode_message::<Self::Source, Self::Target>(&data.payload)
.map_err(|e| anyhow::format_err!("{:?}", e))?;
let fee = estimate_message_delivery_and_dispatch_fee::<Self::Source, Self::Target, _>(
&source_client,
data.conversion_rate_override,
Self::ESTIMATE_MESSAGE_FEE_METHOD,
lane,
&payload,
)
.await?;
log::info!(target: "bridge", "Fee: {:?}", Balance(fee.into()));
println!("{fee}");
Ok(())
}
}
impl FeeEstimator for MillauToRialtoCliBridge {}
impl FeeEstimator for RialtoToMillauCliBridge {}
impl FeeEstimator for MillauToRialtoParachainCliBridge {}
impl FeeEstimator for RialtoParachainToMillauCliBridge {}
impl EstimateFee {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
match self.bridge {
FullBridge::MillauToRialto => MillauToRialtoCliBridge::estimate_fee(self),
FullBridge::RialtoToMillau => RialtoToMillauCliBridge::estimate_fee(self),
FullBridge::MillauToRialtoParachain =>
MillauToRialtoParachainCliBridge::estimate_fee(self),
FullBridge::RialtoParachainToMillau =>
RialtoParachainToMillauCliBridge::estimate_fee(self),
FullBridge::BridgeHubRococoToBridgeHubWococo |
FullBridge::BridgeHubWococoToBridgeHubRococo =>
unimplemented!("TODO: (EstimateFee) do we need it or refactor or remove?"),
}
.await
}
}
/// The caller may provide target to source tokens conversion rate override to use in fee
/// computation.
pub(crate) async fn estimate_message_delivery_and_dispatch_fee<
Source: Chain,
Target: Chain,
P: Clone + Encode,
>(
client: &relay_substrate_client::Client<Source>,
conversion_rate_override: Option<ConversionRateOverride>,
estimate_fee_method: &str,
lane: bp_messages::LaneId,
payload: &P,
) -> anyhow::Result<BalanceOf<Source>> {
// actual conversion rate CAN be lesser than the rate stored in the runtime. So we may try to
// pay lesser fee for the message delivery. But in this case, message may be rejected by the
// lane. So we MUST use the larger of two fees - one computed with stored fee and the one
// computed with actual fee.
let conversion_rate_override =
match (conversion_rate_override, Source::TOKEN_ID, Target::TOKEN_ID) {
(Some(ConversionRateOverride::Explicit(v)), _, _) => {
let conversion_rate_override = FixedU128::from_float(v);
log::info!(
target: "bridge",
"{} -> {} conversion rate override: {:?} (explicit)",
Target::NAME,
Source::NAME,
conversion_rate_override.to_float(),
);
Some(conversion_rate_override)
},
(
Some(ConversionRateOverride::Metric),
Some(source_token_id),
Some(target_token_id),
) => {
let conversion_rate_override =
tokens_conversion_rate_from_metrics(target_token_id, source_token_id).await?;
// So we have current actual conversion rate and rate that is stored in the runtime.
// And we may simply choose the maximal of these. But what if right now there's
// rate update transaction on the way, that is updating rate to 10 seconds old
// actual rate, which is bigger than the current rate? Then our message will be
// rejected.
//
// So let's increase the actual rate by the same value that the conversion rate
// updater is using.
let increased_conversion_rate_override = FixedU128::from_float(
conversion_rate_override * (1.0 + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO),
);
log::info!(
target: "bridge",
"{} -> {} conversion rate override: {} (value from metric - {})",
Target::NAME,
Source::NAME,
increased_conversion_rate_override.to_float(),
conversion_rate_override,
);
Some(increased_conversion_rate_override)
},
_ => None,
};
let without_override = do_estimate_message_delivery_and_dispatch_fee(
client,
estimate_fee_method,
lane,
payload,
None,
)
.await?;
let with_override = do_estimate_message_delivery_and_dispatch_fee(
client,
estimate_fee_method,
lane,
payload,
conversion_rate_override,
)
.await?;
let maximal_fee = std::cmp::max(without_override, with_override);
log::info!(
target: "bridge",
"Estimated message fee: {:?} = max of {:?} (without rate override) and {:?} (with override to {:?})",
maximal_fee,
without_override,
with_override,
conversion_rate_override,
);
Ok(maximal_fee)
}
/// Estimate message delivery and dispatch fee with given conversion rate override.
async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode>(
client: &relay_substrate_client::Client<Source>,
estimate_fee_method: &str,
lane: bp_messages::LaneId,
payload: &P,
conversion_rate_override: Option<FixedU128>,
) -> anyhow::Result<BalanceOf<Source>> {
let encoded_response = client
.state_call(
estimate_fee_method.into(),
(lane, payload, conversion_rate_override).encode().into(),
None,
)
.await?;
let decoded_response: Option<BalanceOf<Source>> = Decode::decode(&mut &encoded_response.0[..])
.map_err(relay_substrate_client::Error::ResponseParseFailed)?;
let fee = decoded_response.ok_or_else(|| {
anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec()))
})?;
Ok(fee)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_parse_cli_options() {
// when
let res = EstimateFee::from_iter(vec![
"estimate_fee",
"rialto-to-millau",
"--source-port",
"1234",
"--conversion-rate-override",
"42.5",
"raw",
"1234",
]);
// then
assert_eq!(
res,
EstimateFee {
bridge: FullBridge::RialtoToMillau,
lane: HexLaneId([0, 0, 0, 0]),
conversion_rate_override: Some(ConversionRateOverride::Explicit(42.5)),
source: SourceConnectionParams {
source_host: "127.0.0.1".into(),
source_port: 1234,
source_secure: false,
source_runtime_version: SourceRuntimeVersionParams {
source_version_mode: RuntimeVersionType::Bundle,
source_spec_version: None,
source_transaction_version: None,
}
},
payload: crate::cli::encode_message::Message::Raw {
data: HexBytes(vec![0x12, 0x34])
}
}
);
}
}
@@ -26,7 +26,6 @@ use bp_messages::LaneId;
pub(crate) mod bridge;
pub(crate) mod encode_message;
pub(crate) mod estimate_fee;
pub(crate) mod send_message;
mod chain_schema;
@@ -74,8 +73,6 @@ pub enum Command {
/// The message is being sent to the source chain, delivered to the target chain and dispatched
/// there.
SendMessage(send_message::SendMessage),
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
EstimateFee(estimate_fee::EstimateFee),
/// Resubmit transactions with increased tip if they are stalled.
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
/// Register parachain.
@@ -111,7 +108,6 @@ impl Command {
Self::RelayHeadersAndMessages(arg) => arg.run().await?,
Self::InitBridge(arg) => arg.run().await?,
Self::SendMessage(arg) => arg.run().await?,
Self::EstimateFee(arg) => arg.run().await?,
Self::ResubmitTransactions(arg) => arg.run().await?,
Self::RegisterParachain(arg) => arg.run().await?,
Self::RelayParachains(arg) => arg.run().await?,
@@ -31,7 +31,6 @@ mod relay_to_parachain;
use async_trait::async_trait;
use std::{marker::PhantomData, sync::Arc};
use structopt::StructOpt;
use strum::VariantNames;
use futures::{FutureExt, TryFutureExt};
use relay_to_parachain::*;
@@ -50,40 +49,27 @@ use crate::{
RelayToRelayHeadersCliBridge,
},
chain_schema::*,
relay_messages::RelayerMode,
CliChain, HexLaneId, PrometheusParams,
},
declare_chain_cli_schema,
};
use bp_messages::LaneId;
use bp_runtime::{BalanceOf, BlockNumberOf};
use messages_relay::relay_strategy::MixStrategy;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithTransactions, Client,
};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use substrate_relay_helper::{
messages_lane::MessagesRelayParams, messages_metrics::StandaloneMessagesMetrics,
on_demand::OnDemandRelay, TaggedAccount, TransactionParams,
messages_lane::MessagesRelayParams, on_demand::OnDemandRelay, TaggedAccount, TransactionParams,
};
/// Maximal allowed conversion rate error ratio (abs(real - stored) / stored) that we allow.
///
/// If it is zero, then transaction will be submitted every time we see difference between
/// stored and real conversion rates. If it is large enough (e.g. > than 10 percents, which is 0.1),
/// then rational relayers may stop relaying messages because they were submitted using
/// lesser conversion rate.
pub(crate) const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05;
/// Parameters that have the same names across all bridges.
#[derive(Debug, PartialEq, StructOpt)]
pub struct HeadersAndMessagesSharedParams {
/// Hex-encoded lane identifiers that should be served by the complex relay.
#[structopt(long, default_value = "00000000")]
pub lane: Vec<HexLaneId>,
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
pub relayer_mode: RelayerMode,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
@@ -101,8 +87,6 @@ pub struct Full2WayBridgeCommonParams<
pub right: BridgeEndCommonParams<Right>,
pub metrics_params: MetricsParams,
pub left_to_right_metrics: StandaloneMessagesMetrics<Left, Right>,
pub right_to_left_metrics: StandaloneMessagesMetrics<Right, Left>,
}
impl<Left: ChainWithTransactions + CliChain, Right: ChainWithTransactions + CliChain>
@@ -116,19 +100,8 @@ impl<Left: ChainWithTransactions + CliChain, Right: ChainWithTransactions + CliC
// Create metrics registry.
let metrics_params = shared.prometheus_params.clone().into();
let metrics_params = relay_utils::relay_metrics(metrics_params).into_params();
let left_to_right_metrics = substrate_relay_helper::messages_metrics::standalone_metrics::<
L2R::MessagesLane,
>(left.client.clone(), right.client.clone())?;
let right_to_left_metrics = left_to_right_metrics.clone().reverse();
Ok(Self {
shared,
left,
right,
metrics_params,
left_to_right_metrics,
right_to_left_metrics,
})
Ok(Self { shared, left, right, metrics_params })
}
}
@@ -146,11 +119,9 @@ struct FullBridge<
Target: ChainWithTransactions + CliChain,
Bridge: MessagesCliBridge<Source = Source, Target = Target>,
> {
shared: &'a HeadersAndMessagesSharedParams,
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
metrics: &'a StandaloneMessagesMetrics<Source, Target>,
_phantom_data: PhantomData<Bridge>,
}
@@ -166,55 +137,11 @@ where
BalanceOf<Source>: TryFrom<BalanceOf<Target>> + Into<u128>,
{
fn new(
shared: &'a HeadersAndMessagesSharedParams,
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
metrics: &'a StandaloneMessagesMetrics<Source, Target>,
) -> Self {
Self { shared, source, target, metrics_params, metrics, _phantom_data: Default::default() }
}
fn start_conversion_rate_update_loop(&mut self) -> anyhow::Result<()> {
if let Some(ref messages_pallet_owner) = self.source.messages_pallet_owner {
let format_err = || {
anyhow::format_err!(
"Cannon run conversion rate updater: {} -> {}",
Target::NAME,
Source::NAME
)
};
substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::<
Bridge::MessagesLane,
>(
self.source.client.clone(),
TransactionParams {
signer: messages_pallet_owner.clone(),
mortality: self.source.transactions_mortality,
},
self.metrics
.target_to_source_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
self.metrics
.target_to_base_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
self.metrics
.source_to_base_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
);
self.source.accounts.push(TaggedAccount::MessagesPalletOwner {
id: messages_pallet_owner.public().into(),
bridged_chain: Target::NAME.to_string(),
});
}
Ok(())
Self { source, target, metrics_params, _phantom_data: Default::default() }
}
fn messages_relay_params(
@@ -223,9 +150,6 @@ where
target_to_source_headers_relay: Arc<dyn OnDemandRelay<BlockNumberOf<Target>>>,
lane_id: LaneId,
) -> MessagesRelayParams<Bridge::MessagesLane> {
let relayer_mode = self.shared.relayer_mode.into();
let relay_strategy = MixStrategy::new(relayer_mode);
MessagesRelayParams {
source_client: self.source.client.clone(),
source_transaction_params: TransactionParams {
@@ -241,8 +165,6 @@ where
target_to_source_headers_relay: Some(target_to_source_headers_relay),
lane_id,
metrics_params: self.metrics_params.clone().disable(),
standalone_metrics: Some(self.metrics.clone()),
relay_strategy,
}
}
}
@@ -314,22 +236,18 @@ where
fn left_to_right(&mut self) -> FullBridge<Self::Left, Self::Right, Self::L2R> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::L2R>::new(
&common.shared,
&mut common.left,
&mut common.right,
&common.metrics_params,
&common.left_to_right_metrics,
)
}
fn right_to_left(&mut self) -> FullBridge<Self::Right, Self::Left, Self::R2L> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::R2L>::new(
&common.shared,
&mut common.right,
&mut common.left,
&common.metrics_params,
&common.right_to_left_metrics,
)
}
@@ -347,10 +265,6 @@ where
});
}
// start conversion rate update loops for left/right chains
self.left_to_right().start_conversion_rate_update_loop()?;
self.right_to_left().start_conversion_rate_update_loop()?;
// start on-demand header relays
let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) =
self.mut_base().start_on_demand_headers_relayers().await?;
@@ -528,7 +442,6 @@ mod tests {
HexLaneId([0x00, 0x00, 0x00, 0x00]),
HexLaneId([0x73, 0x77, 0x61, 0x70])
],
relayer_mode: RelayerMode::Rational,
only_mandatory_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
@@ -641,7 +554,6 @@ mod tests {
MillauRialtoParachainHeadersAndMessages {
shared: HeadersAndMessagesSharedParams {
lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])],
relayer_mode: RelayerMode::Rational,
only_mandatory_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
@@ -17,7 +17,7 @@
use async_trait::async_trait;
use sp_core::Pair;
use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames};
use strum::VariantNames;
use crate::chains::{
bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge,
@@ -27,32 +27,11 @@ use crate::chains::{
rialto_headers_to_millau::RialtoToMillauCliBridge,
rialto_parachains_to_millau::RialtoParachainToMillauCliBridge,
};
use messages_relay::relay_strategy::MixStrategy;
use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithTransactions};
use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams};
use crate::cli::{bridge::*, chain_schema::*, CliChain, HexLaneId, PrometheusParams};
/// Relayer operating mode.
#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab_case")]
pub enum RelayerMode {
/// The relayer doesn't care about rewards.
Altruistic,
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
Rational,
}
impl From<RelayerMode> for messages_relay::message_lane_loop::RelayerMode {
fn from(mode: RelayerMode) -> Self {
match mode {
RelayerMode::Altruistic => Self::Altruistic,
RelayerMode::Rational => Self::Rational,
}
}
}
/// Start messages relayer process.
#[derive(StructOpt)]
pub struct RelayMessages {
@@ -62,8 +41,6 @@ pub struct RelayMessages {
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
relayer_mode: RelayerMode,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
@@ -91,8 +68,6 @@ where
let target_client = data.target.into_client::<Self::Target>().await?;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
let target_transactions_mortality = data.target_sign.transactions_mortality()?;
let relayer_mode = data.relayer_mode.into();
let relay_strategy = MixStrategy::new(relayer_mode);
substrate_relay_helper::messages_lane::run::<Self::MessagesLane>(MessagesRelayParams {
source_client,
@@ -109,8 +84,6 @@ where
target_to_source_headers_relay: None,
lane_id: data.lane.into(),
metrics_params: data.prometheus_params.into(),
standalone_metrics: None,
relay_strategy,
})
.await
.map_err(|e| anyhow::format_err!("{}", e))
@@ -142,43 +115,3 @@ impl RelayMessages {
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_use_rational_relayer_mode_by_default() {
assert_eq!(
RelayMessages::from_iter(vec![
"relay-messages",
"rialto-to-millau",
"--source-port=0",
"--source-signer=//Alice",
"--target-port=0",
"--target-signer=//Alice",
"--lane=00000000",
])
.relayer_mode,
RelayerMode::Rational,
);
}
#[test]
fn should_accept_altruistic_relayer_mode() {
assert_eq!(
RelayMessages::from_iter(vec![
"relay-messages",
"rialto-to-millau",
"--source-port=0",
"--source-signer=//Alice",
"--target-port=0",
"--target-signer=//Alice",
"--lane=00000000",
"--relayer-mode=altruistic",
])
.relayer_mode,
RelayerMode::Altruistic,
);
}
}
@@ -25,8 +25,7 @@ use crate::{
bridge::{FullBridge, MessagesCliBridge},
chain_schema::*,
encode_message::{self, CliEncodeMessage, RawMessage},
estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride},
Balance, CliChain, HexLaneId,
CliChain,
},
};
use async_trait::async_trait;
@@ -35,30 +34,11 @@ use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, SignParam,
UnsignedTransaction,
};
use sp_core::{Bytes, Pair};
use sp_core::Pair;
use sp_runtime::AccountId32;
use std::fmt::{Debug, Display};
use std::fmt::Display;
use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames};
/// Relayer operating mode.
#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab_case")]
pub enum DispatchFeePayment {
/// The dispatch fee is paid at the source chain.
AtSourceChain,
/// The dispatch fee is paid at the target chain.
AtTargetChain,
}
impl From<DispatchFeePayment> for bp_runtime::messages::DispatchFeePayment {
fn from(dispatch_fee_payment: DispatchFeePayment) -> Self {
match dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => Self::AtSourceChain,
DispatchFeePayment::AtTargetChain => Self::AtTargetChain,
}
}
}
use strum::VariantNames;
/// Send bridge message.
#[derive(StructOpt)]
@@ -70,23 +50,6 @@ pub struct SendMessage {
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
/// Send message using XCM pallet instead. By default message is sent using
/// bridge messages pallet.
#[structopt(long)]
use_xcm_pallet: bool,
/// Hex-encoded lane id. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// A way to override conversion rate between bridge tokens.
///
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
/// your message won't be relayed.
#[structopt(long)]
conversion_rate_override: Option<ConversionRateOverride>,
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined
/// automatically.
#[structopt(long)]
fee: Option<Balance>,
/// Message type.
#[structopt(subcommand)]
message: crate::cli::encode_message::Message,
@@ -111,53 +74,14 @@ where
let source_client = data.source.into_client::<Self::Source>().await?;
let source_sign = data.source_sign.to_keypair::<Self::Source>()?;
let lane = data.lane.clone().into();
let conversion_rate_override = data.conversion_rate_override;
let fee = match data.fee {
Some(fee) => fee,
None => Balance(
estimate_message_delivery_and_dispatch_fee::<Self::Source, Self::Target, _>(
&source_client,
conversion_rate_override,
Self::ESTIMATE_MESSAGE_FEE_METHOD,
lane,
&payload,
)
.await?
.into(),
),
};
let payload_len = payload.encoded_size();
let send_message_call = if data.use_xcm_pallet {
Self::Source::encode_send_xcm(
decode_xcm(payload)?,
data.bridge.bridge_instance_index(),
)?
} else {
Self::Source::encode_send_message_call(
data.lane.0,
payload,
fee.cast().into(),
data.bridge.bridge_instance_index(),
)?
};
let send_message_call = Self::Source::encode_send_xcm(
decode_xcm(payload)?,
data.bridge.bridge_instance_index(),
)?;
let source_genesis_hash = *source_client.genesis_hash();
let (spec_version, transaction_version) = source_client.simple_runtime_version().await?;
let estimated_transaction_fee = source_client
.estimate_extrinsic_fee(Bytes(
Self::Source::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: source_genesis_hash,
signer: source_sign.clone(),
},
UnsignedTransaction::new(send_message_call.clone(), 0),
)?
.encode(),
))
.await?;
source_client
.submit_signed_extrinsic(
source_sign.public().into(),
@@ -171,22 +95,10 @@ where
let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce);
log::info!(
target: "bridge",
"Sending message to {}. Lane: {:?}. Size: {}. Fee: {}",
"Sending message to {}. Size: {}",
Self::Target::NAME,
lane,
payload_len,
fee,
);
log::info!(
target: "bridge",
"The source account ({:?}) balance will be reduced by (at most) {} (message fee)
+ {} (tx fee ) = {} {} tokens", AccountId32::from(source_sign.public()),
fee.0,
estimated_transaction_fee.inclusion_fee(),
fee.0.saturating_add(estimated_transaction_fee.inclusion_fee().into()),
Self::Source::NAME,
);
Ok(unsigned)
},
)
@@ -223,7 +135,7 @@ impl SendMessage {
}
/// Decode SCALE encoded raw XCM message.
fn decode_xcm(message: RawMessage) -> anyhow::Result<xcm::VersionedXcm<()>> {
pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result<xcm::VersionedXcm<()>> {
Decode::decode(&mut &message[..])
.map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e))
}
@@ -243,8 +155,6 @@ mod tests {
"1234",
"--source-signer",
"//Alice",
"--conversion-rate-override",
"0.75",
"raw",
"dead",
]);
@@ -253,10 +163,6 @@ mod tests {
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(
send_message.conversion_rate_override,
Some(ConversionRateOverride::Explicit(0.75))
);
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) }
@@ -273,8 +179,6 @@ mod tests {
"1234",
"--source-signer",
"//Alice",
"--conversion-rate-override",
"metric",
"sized",
"max",
]);
@@ -283,7 +187,6 @@ mod tests {
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(send_message.conversion_rate_override, Some(ConversionRateOverride::Metric));
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal }