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 7761834672..05ff36be1f 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 @@ -164,11 +164,13 @@ pub async fn run( Millau relayer account id: {:?}\n\t\ Max messages in single transaction: {}\n\t\ Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}", + Max messages weight in single transaction: {}\n\t\ + Relayer mode: {:?}", lane.relayer_id_at_source, max_messages_in_single_batch, max_messages_size_in_single_batch, max_messages_weight_in_single_batch, + params.relayer_mode, ); let (metrics_params, metrics_values) = add_standalone_metrics( @@ -191,7 +193,7 @@ pub async fn run( max_messages_in_single_batch, max_messages_weight_in_single_batch, max_messages_size_in_single_batch, - relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic, + relayer_mode: params.relayer_mode, }, }, MillauSourceClient::new( 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 c2764c20d8..0aec33a339 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 @@ -163,11 +163,13 @@ pub async fn run( Rialto relayer account id: {:?}\n\t\ Max messages in single transaction: {}\n\t\ Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}", + Max messages weight in single transaction: {}\n\t\ + Relayer mode: {:?}", lane.relayer_id_at_source, max_messages_in_single_batch, max_messages_size_in_single_batch, max_messages_weight_in_single_batch, + params.relayer_mode, ); let (metrics_params, metrics_values) = add_standalone_metrics( @@ -190,7 +192,7 @@ pub async fn run( max_messages_in_single_batch, max_messages_weight_in_single_batch, max_messages_size_in_single_batch, - relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic, + relayer_mode: params.relayer_mode, }, }, RialtoSourceClient::new( 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 99bcf53c8a..080fa3b049 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 @@ -178,11 +178,13 @@ pub async fn run( Rococo relayer account id: {:?}\n\t\ Max messages in single transaction: {}\n\t\ Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}", + Max messages weight in single transaction: {}\n\t\ + Relayer mode: {:?}", lane.relayer_id_at_source, max_messages_in_single_batch, max_messages_size_in_single_batch, max_messages_weight_in_single_batch, + params.relayer_mode, ); let (metrics_params, metrics_values) = add_standalone_metrics( @@ -205,7 +207,7 @@ pub async fn run( max_messages_in_single_batch, max_messages_weight_in_single_batch, max_messages_size_in_single_batch, - relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic, + relayer_mode: params.relayer_mode, }, }, RococoSourceClient::new( 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 b9fcd7ac3a..b36088388f 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 @@ -178,11 +178,13 @@ pub async fn run( Wococo relayer account id: {:?}\n\t\ Max messages in single transaction: {}\n\t\ Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}", + Max messages weight in single transaction: {}\n\t\ + Relayer mode: {:?}", lane.relayer_id_at_source, max_messages_in_single_batch, max_messages_size_in_single_batch, max_messages_weight_in_single_batch, + params.relayer_mode, ); let (metrics_params, metrics_values) = add_standalone_metrics( @@ -205,7 +207,7 @@ pub async fn run( max_messages_in_single_batch, max_messages_weight_in_single_batch, max_messages_size_in_single_batch, - relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic, + relayer_mode: params.relayer_mode, }, }, WococoSourceClient::new( diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index 8e2d2ce2a7..04ee1a6bc2 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -22,7 +22,7 @@ //! 2) add `declare_bridge_options!(...)` for the bridge; //! 3) add bridge support to the `select_bridge! { ... }` macro. -use crate::cli::{CliChain, HexLaneId, PrometheusParams}; +use crate::cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams}; use crate::declare_chain_options; use crate::messages_lane::MessagesRelayParams; use crate::on_demand_headers::OnDemandHeadersRelay; @@ -30,6 +30,7 @@ use crate::on_demand_headers::OnDemandHeadersRelay; use futures::{FutureExt, TryFutureExt}; use relay_utils::metrics::MetricsParams; use structopt::StructOpt; +use strum::VariantNames; /// Start headers+messages relayer process. #[derive(StructOpt)] @@ -44,6 +45,8 @@ pub struct HeadersAndMessagesSharedParams { /// Hex-encoded lane identifiers that should be served by the complex relay. #[structopt(long, default_value = "00000000")] lane: Vec, + #[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")] + relayer_mode: RelayerMode, #[structopt(flatten)] prometheus_params: PrometheusParams, } @@ -158,6 +161,7 @@ impl RelayHeadersAndMessages { let right_sign = params.right_sign.to_keypair::()?; let lanes = params.shared.lane; + let relayer_mode = params.shared.relayer_mode.into(); let metrics_params: MetricsParams = params.shared.prometheus_params.into(); let metrics_params = relay_utils::relay_metrics(None, metrics_params).into_params(); @@ -189,6 +193,7 @@ impl RelayHeadersAndMessages { source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()), target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()), lane_id: lane, + relayer_mode, metrics_params: metrics_params.clone().disable().metrics_prefix( messages_relay::message_lane_loop::metrics_prefix::(&lane), ), @@ -203,6 +208,7 @@ impl RelayHeadersAndMessages { source_to_target_headers_relay: Some(right_to_left_on_demand_headers.clone()), target_to_source_headers_relay: Some(left_to_right_on_demand_headers.clone()), lane_id: lane, + relayer_mode, metrics_params: metrics_params.clone().disable().metrics_prefix( messages_relay::message_lane_loop::metrics_prefix::(&lane), ), diff --git a/bridges/relays/bin-substrate/src/cli/relay_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_messages.rs index e798888f4c..afd59c7a3a 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_messages.rs @@ -22,7 +22,26 @@ use crate::cli::{ use crate::messages_lane::MessagesRelayParams; use crate::select_full_bridge; use structopt::StructOpt; -use strum::VariantNames; +use strum::{EnumString, EnumVariantNames, VariantNames}; + +/// Relayer operating mode. +#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq)] +#[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 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)] @@ -33,6 +52,8 @@ 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)] @@ -62,6 +83,7 @@ impl RelayMessages { source_to_target_headers_relay: None, target_to_source_headers_relay: None, lane_id: self.lane.into(), + relayer_mode: self.relayer_mode.into(), metrics_params: self.prometheus_params.into(), }) .await @@ -69,3 +91,43 @@ impl RelayMessages { }) } } + +#[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, + ); + } +} diff --git a/bridges/relays/bin-substrate/src/messages_lane.rs b/bridges/relays/bin-substrate/src/messages_lane.rs index b0119b12f0..1920137f14 100644 --- a/bridges/relays/bin-substrate/src/messages_lane.rs +++ b/bridges/relays/bin-substrate/src/messages_lane.rs @@ -49,6 +49,8 @@ pub struct MessagesRelayParams { pub target_to_source_headers_relay: Option>, /// Identifier of lane that needs to be served. pub lane_id: LaneId, + /// Relayer operating mode. + pub relayer_mode: messages_relay::message_lane_loop::RelayerMode, /// Metrics parameters. pub metrics_params: MetricsParams, } diff --git a/bridges/relays/bin-substrate/src/messages_target.rs b/bridges/relays/bin-substrate/src/messages_target.rs index 5abcd88b85..bbee261c6d 100644 --- a/bridges/relays/bin-substrate/src/messages_target.rs +++ b/bridges/relays/bin-substrate/src/messages_target.rs @@ -255,6 +255,13 @@ where SC::NAME, )) })?; + log::trace!( + target: "bridge", + "Using conversion rate {} when converting from {} tokens to {} tokens", + conversion_rate, + TC::NAME, + SC::NAME + ); Ok(convert_target_tokens_to_source_tokens::( FixedU128::from_float(conversion_rate), self.client diff --git a/bridges/relays/messages/src/message_lane.rs b/bridges/relays/messages/src/message_lane.rs index 8757e9322c..d03d407597 100644 --- a/bridges/relays/messages/src/message_lane.rs +++ b/bridges/relays/messages/src/message_lane.rs @@ -21,7 +21,7 @@ use num_traits::{SaturatingAdd, Zero}; use relay_utils::{BlockNumberBase, HeaderId}; -use std::fmt::Debug; +use std::{fmt::Debug, ops::Sub}; /// One-way message lane. pub trait MessageLane: 'static + Clone + Send + Sync { @@ -40,7 +40,15 @@ pub trait MessageLane: 'static + Clone + Send + Sync { /// 1) pay transaction fees; /// 2) pay message delivery and dispatch fee; /// 3) pay relayer rewards. - type SourceChainBalance: Clone + Copy + Debug + PartialOrd + SaturatingAdd + Zero + Send + Sync; + type SourceChainBalance: Clone + + Copy + + Debug + + PartialOrd + + Sub + + SaturatingAdd + + Zero + + Send + + Sync; /// Number of the source header. type SourceHeaderNumber: BlockNumberBase; /// Hash of the source header. diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index c70fc532e9..282c3bc8b6 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -65,7 +65,7 @@ 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. - NoLosses, + Rational, } /// Message delivery race parameters. diff --git a/bridges/relays/messages/src/message_race_delivery.rs b/bridges/relays/messages/src/message_race_delivery.rs index a03a671c70..260a72f3f0 100644 --- a/bridges/relays/messages/src/message_race_delivery.rs +++ b/bridges/relays/messages/src/message_race_delivery.rs @@ -540,8 +540,7 @@ where /// From given set of source nonces, that are ready to be delivered, select nonces /// to fit into single delivery transaction. /// -/// The function returns nonces that are NOT selected for current batch and will be -/// delivered later. +/// The function returns last nonce that must be delivered to the target chain. #[allow(clippy::too_many_arguments)] async fn select_nonces_for_delivery_transaction( relayer_mode: RelayerMode, @@ -564,6 +563,8 @@ async fn select_nonces_for_delivery_transaction( let mut selected_unpaid_weight: Weight = 0; let mut selected_size: u32 = 0; let mut selected_count: MessageNonce = 0; + let mut selected_reward = P::SourceChainBalance::zero(); + let mut selected_cost = P::SourceChainBalance::zero(); let mut total_reward = P::SourceChainBalance::zero(); let mut total_confirmations_cost = P::SourceChainBalance::zero(); @@ -647,7 +648,7 @@ async fn select_nonces_for_delivery_transaction( RelayerMode::Altruistic => { soft_selected_count = index + 1; } - RelayerMode::NoLosses => { + RelayerMode::Rational => { let delivery_transaction_cost = lane_target_client .estimate_delivery_transaction_in_source_tokens( 0..=(new_selected_count as MessageNonce - 1), @@ -698,9 +699,11 @@ async fn select_nonces_for_delivery_transaction( ); } - // NoLosses relayer never want to lose his funds + // Rational relayer never want to lose his funds if total_reward >= total_cost { soft_selected_count = index + 1; + selected_reward = total_reward; + selected_cost = total_cost; } } } @@ -732,6 +735,18 @@ async fn select_nonces_for_delivery_transaction( } if hard_selected_count != 0 { + if relayer_mode != RelayerMode::Altruistic { + log::trace!( + target: "bridge", + "Expected reward from delivering nonces [{:?}; {:?}] is: {:?} - {:?} = {:?}", + hard_selected_begin_nonce, + hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1, + selected_reward, + selected_cost, + selected_reward - selected_cost, + ); + } + Some(hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1) } else { None @@ -1151,9 +1166,9 @@ mod tests { } #[async_std::test] - async fn no_losses_relayer_is_delivering_messages_if_cost_is_equal_to_reward() { + async fn rational_relayer_is_delivering_messages_if_cost_is_equal_to_reward() { let (state, mut strategy) = prepare_strategy(); - strategy.relayer_mode = RelayerMode::NoLosses; + strategy.relayer_mode = RelayerMode::Rational; // so now we have: // - 20..=23 with reward = cost @@ -1165,7 +1180,7 @@ mod tests { } #[async_std::test] - async fn no_losses_relayer_is_not_delivering_messages_if_cost_is_larger_than_reward() { + async fn rational_relayer_is_not_delivering_messages_if_cost_is_larger_than_reward() { let (mut state, mut strategy) = prepare_strategy(); let nonces = source_nonces( 24..=25, @@ -1175,7 +1190,7 @@ mod tests { ); strategy.strategy.source_nonces_updated(header_id(2), nonces); state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - strategy.relayer_mode = RelayerMode::NoLosses; + strategy.relayer_mode = RelayerMode::Rational; // so now we have: // - 20..=23 with reward = cost @@ -1188,7 +1203,7 @@ mod tests { } #[async_std::test] - async fn no_losses_relayer_is_delivering_unpaid_messages() { + async fn rational_relayer_is_delivering_unpaid_messages() { async fn test_with_dispatch_fee_payment( dispatch_fee_payment: DispatchFeePayment, ) -> Option<(RangeInclusive, MessageProofParameters)> { @@ -1206,7 +1221,7 @@ mod tests { strategy.max_messages_in_single_batch = 100; strategy.max_messages_weight_in_single_batch = 100; strategy.max_messages_size_in_single_batch = 100; - strategy.relayer_mode = RelayerMode::NoLosses; + strategy.relayer_mode = RelayerMode::Rational; // so now we have: // - 20..=23 with reward = cost