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
-2
View File
@@ -30,7 +30,6 @@ bp-rialto = { path = "../../primitives/chain-rialto" }
bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" }
bp-rococo = { path = "../../primitives/chain-rococo" }
bp-runtime = { path = "../../primitives/runtime" }
bp-statemine = { path = "../../primitives/chain-statemine" }
bp-westend = { path = "../../primitives/chain-westend" }
bp-wococo = { path = "../../primitives/chain-wococo" }
bridge-runtime-common = { path = "../../bin/runtime-common" }
@@ -45,7 +44,6 @@ relay-rialto-parachain-client = { path = "../client-rialto-parachain" }
relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" }
relay-bridge-hub-wococo-client = { path = "../client-bridge-hub-wococo" }
relay-rococo-client = { path = "../client-rococo" }
relay-statemine-client = { path = "../client-statemine" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
relay-westend-client = { path = "../client-westend" }
@@ -18,7 +18,6 @@
use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge};
use bp_messages::Weight;
use messages_relay::relay_strategy::MixStrategy;
use relay_bridge_hub_rococo_client::BridgeHubRococo;
use relay_bridge_hub_wococo_client::BridgeHubWococo;
use substrate_relay_helper::messages_lane::SubstrateMessageLane;
@@ -55,15 +54,6 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil
pub struct BridgeHubRococoMessagesToBridgeHubWococoMessageLane;
impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLane {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = BridgeHubRococo;
type TargetChain = BridgeHubWococo;
@@ -71,8 +61,4 @@ impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLan
BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder;
type ReceiveMessagesDeliveryProofCallBuilder =
BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder;
type TargetToSourceChainConversionRateUpdateBuilder = ();
type RelayStrategy = MixStrategy;
}
@@ -18,7 +18,6 @@
use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge};
use bp_messages::Weight;
use messages_relay::relay_strategy::MixStrategy;
use relay_bridge_hub_rococo_client::BridgeHubRococo;
use relay_bridge_hub_wococo_client::BridgeHubWococo;
use substrate_relay_helper::messages_lane::SubstrateMessageLane;
@@ -55,15 +54,6 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil
pub struct BridgeHubWococoMessagesToBridgeHubRococoMessageLane;
impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLane {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = BridgeHubWococo;
type TargetChain = BridgeHubRococo;
@@ -71,8 +61,4 @@ impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLan
BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder;
type ReceiveMessagesDeliveryProofCallBuilder =
BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder;
type TargetToSourceChainConversionRateUpdateBuilder = ();
type RelayStrategy = MixStrategy;
}
@@ -16,16 +16,10 @@
//! Millau chain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_rialto_parachain::RIALTO_PARACHAIN_ID;
use bp_runtime::EncodedOrDecodedCall;
use relay_millau_client::Millau;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -56,37 +50,6 @@ impl CliEncodeMessage for Millau {
})
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::MILLAU_TO_RIALTO_INDEX => millau_runtime::RuntimeCall::BridgeRialtoMessages(
millau_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
bridge::MILLAU_TO_RIALTO_PARACHAIN_INDEX =>
millau_runtime::RuntimeCall::BridgeRialtoParachainMessages(
millau_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for Millau {
@@ -16,7 +16,6 @@
//! Millau-to-Rialto messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
use substrate_relay_helper::messages_lane::{
@@ -27,25 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Millau -> Rialto messages bridge.
#[derive(Clone, Debug)]
pub struct MillauMessagesToRialto;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Millau,
MillauMessagesToRialtoUpdateConversionRateCallBuilder,
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate
);
impl SubstrateMessageLane for MillauMessagesToRialto {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = Millau;
type TargetChain = Rialto;
@@ -59,9 +41,4 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
MillauMessagesToRialtoUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -16,7 +16,6 @@
//! Millau-to-RialtoParachain messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_parachain_client::RialtoParachain;
use substrate_relay_helper::messages_lane::{
@@ -27,30 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Millau -> RialtoParachain messages bridge.
#[derive(Clone, Debug)]
pub struct MillauMessagesToRialtoParachain;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Millau,
MillauMessagesToRialtoParachainUpdateConversionRateCallBuilder,
millau_runtime::Runtime,
millau_runtime::WithRialtoParachainMessagesInstance,
millau_runtime::rialto_parachain_messages::MillauToRialtoParachainMessagesParameter::RialtoParachainToMillauConversionRate
);
impl SubstrateMessageLane for MillauMessagesToRialtoParachain {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_FEE_MULTIPLIER_PARAMETER_NAME);
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_FEE_MULTIPLIER_PARAMETER_NAME);
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_millau::TRANSACTION_PAYMENT_PALLET_NAME);
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_rialto_parachain::TRANSACTION_PAYMENT_PALLET_NAME);
type SourceChain = Millau;
type TargetChain = RialtoParachain;
@@ -64,9 +41,4 @@ impl SubstrateMessageLane for MillauMessagesToRialtoParachain {
millau_runtime::Runtime,
millau_runtime::WithRialtoParachainMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
MillauMessagesToRialtoParachainUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -37,7 +37,6 @@ mod millau;
mod rialto;
mod rialto_parachain;
mod rococo;
mod statemine;
mod westend;
mod wococo;
@@ -16,15 +16,9 @@
//! Rialto chain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use relay_rialto_client::Rialto;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -48,28 +42,6 @@ impl CliEncodeMessage for Rialto {
})
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::RIALTO_TO_MILLAU_INDEX => rialto_runtime::RuntimeCall::BridgeMillauMessages(
rialto_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for Rialto {
@@ -16,7 +16,6 @@
//! Rialto-to-Millau messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
use substrate_relay_helper::messages_lane::{
@@ -27,25 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Rialto -> Millau messages bridge.
#[derive(Clone, Debug)]
pub struct RialtoMessagesToMillau;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Rialto,
RialtoMessagesToMillauUpdateConversionRateCallBuilder,
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate
);
impl SubstrateMessageLane for RialtoMessagesToMillau {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = Rialto;
type TargetChain = Millau;
@@ -59,9 +41,4 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
RialtoMessagesToMillauUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -16,15 +16,9 @@
//! Rialto parachain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use relay_rialto_parachain_client::RialtoParachain;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -50,29 +44,6 @@ impl CliEncodeMessage for RialtoParachain {
)
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX =>
rialto_parachain_runtime::RuntimeCall::BridgeMillauMessages(
rialto_parachain_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for RialtoParachain {
@@ -16,7 +16,6 @@
//! RialtoParachain-to-Millau messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_parachain_client::RialtoParachain;
use substrate_relay_helper::messages_lane::{
@@ -27,30 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of RialtoParachain -> Millau messages bridge.
#[derive(Clone, Debug)]
pub struct RialtoParachainMessagesToMillau;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
RialtoParachain,
RialtoParachainMessagesToMillauUpdateConversionRateCallBuilder,
rialto_parachain_runtime::Runtime,
rialto_parachain_runtime::WithMillauMessagesInstance,
rialto_parachain_runtime::millau_messages::RialtoParachainToMillauMessagesParameter::MillauToRialtoParachainConversionRate
);
impl SubstrateMessageLane for RialtoParachainMessagesToMillau {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_FEE_MULTIPLIER_PARAMETER_NAME);
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_FEE_MULTIPLIER_PARAMETER_NAME);
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_rialto_parachain::TRANSACTION_PAYMENT_PALLET_NAME);
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_millau::TRANSACTION_PAYMENT_PALLET_NAME);
type SourceChain = RialtoParachain;
type TargetChain = Millau;
@@ -64,9 +41,4 @@ impl SubstrateMessageLane for RialtoParachainMessagesToMillau {
rialto_parachain_runtime::Runtime,
rialto_parachain_runtime::WithMillauMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
RialtoParachainMessagesToMillauUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -1,34 +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/>.
//! Statemine chain specification for CLI.
use crate::cli::CliChain;
use relay_statemine_client::Statemine;
use sp_version::RuntimeVersion;
impl CliChain for Statemine {
const RUNTIME_VERSION: Option<RuntimeVersion> = Some(bp_statemine::VERSION);
type KeyPair = sp_core::sr25519::Pair;
fn ss58_format() -> u16 {
sp_core::crypto::Ss58AddressFormat::from(
sp_core::crypto::Ss58AddressFormatRegistry::KusamaAccount,
)
.into()
}
}
@@ -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 }
@@ -60,7 +60,6 @@ impl Chain for BridgeHubRococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_bridge_hub_rococo::SignedBlock;
type Call = runtime::Call;
@@ -126,8 +125,6 @@ impl ChainWithMessages for BridgeHubRococo {
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_bridge_hub_rococo::FROM_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_bridge_hub_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -60,7 +60,6 @@ impl Chain for BridgeHubWococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_bridge_hub_wococo::SignedBlock;
type Call = runtime::Call;
@@ -126,8 +125,6 @@ impl ChainWithMessages for BridgeHubWococo {
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_bridge_hub_wococo::FROM_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_bridge_hub_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
-1
View File
@@ -54,7 +54,6 @@ impl Chain for Kusama {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_kusama::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_kusama::SignedBlock;
type Call = ();
-3
View File
@@ -65,8 +65,6 @@ impl ChainWithMessages for Millau {
bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_millau::FROM_MILLAU_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -82,7 +80,6 @@ impl Chain for Millau {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_millau::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = millau_runtime::SignedBlock;
type Call = millau_runtime::RuntimeCall;
@@ -54,7 +54,6 @@ impl Chain for Polkadot {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_polkadot::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_polkadot::SignedBlock;
type Call = ();
@@ -62,7 +62,6 @@ impl Chain for RialtoParachain {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = rialto_parachain_runtime::SignedBlock;
type Call = rialto_parachain_runtime::RuntimeCall;
@@ -86,8 +85,6 @@ impl ChainWithMessages for RialtoParachain {
bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_rialto_parachain::FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -157,7 +154,7 @@ impl ChainWithTransactions for RialtoParachain {
}
fn parse_transaction(_tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self>> {
unimplemented!("TODO")
None
}
}
-3
View File
@@ -62,7 +62,6 @@ impl Chain for Rialto {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = rialto_runtime::SignedBlock;
type Call = rialto_runtime::RuntimeCall;
@@ -85,8 +84,6 @@ impl ChainWithMessages for Rialto {
bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_rialto::FROM_RIALTO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
-1
View File
@@ -57,7 +57,6 @@ impl Chain for Rococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_rococo::SignedBlock;
type Call = ();
@@ -1,24 +0,0 @@
[package]
name = "relay-statemine-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.1.5" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
scale-info = { version = "2.1.1", features = ["derive"] }
# Bridge dependencies
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
bp-statemine = { path = "../../primitives/chain-statemine" }
# Substrate Dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
-112
View File
@@ -1,112 +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/>.
//! Types used to connect to the Statemine chain.
use codec::Encode;
use frame_support::weights::Weight;
use relay_substrate_client::{
Chain, ChainBase, ChainWithTransactions, Error as SubstrateError, SignParam,
UnsignedTransaction,
};
use sp_core::Pair;
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration;
pub mod runtime;
/// Statemine chain definition
#[derive(Debug, Clone, Copy)]
pub struct Statemine;
impl ChainBase for Statemine {
type BlockNumber = bp_statemine::BlockNumber;
type Hash = bp_statemine::Hash;
type Hasher = bp_statemine::Hasher;
type Header = bp_statemine::Header;
type AccountId = bp_statemine::AccountId;
type Balance = bp_statemine::Balance;
type Index = bp_statemine::Nonce;
type Signature = bp_statemine::Signature;
fn max_extrinsic_size() -> u32 {
bp_statemine::Statemine::max_extrinsic_size()
}
fn max_extrinsic_weight() -> Weight {
bp_statemine::Statemine::max_extrinsic_weight()
}
}
impl Chain for Statemine {
const NAME: &'static str = "Statemine";
const TOKEN_ID: Option<&'static str> = Some("kusama");
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "<unused>";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_statemine::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_statemine::SignedBlock;
type Call = runtime::Call;
}
impl ChainWithTransactions for Statemine {
type AccountKeyPair = sp_core::sr25519::Pair;
type SignedTransaction = runtime::UncheckedExtrinsic;
fn sign_transaction(
param: SignParam<Self>,
unsigned: UnsignedTransaction<Self>,
) -> Result<Self::SignedTransaction, SubstrateError> {
let raw_payload = SignedPayload::new(
unsigned.call.clone(),
bp_statemine::SignedExtensions::new(
param.spec_version,
param.transaction_version,
unsigned.era,
param.genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload));
let signer: sp_runtime::MultiSigner = param.signer.public().into();
let (call, extra, _) = raw_payload.deconstruct();
Ok(runtime::UncheckedExtrinsic::new_signed(
call,
signer.into_account().into(),
signature.into(),
extra,
))
}
fn is_signed(tx: &Self::SignedTransaction) -> bool {
tx.signature.is_some()
}
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_statemine::Address::Id(signer.public().into()))
.unwrap_or(false)
}
fn parse_transaction(_tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self>> {
unimplemented!("not used on Statemine")
}
}
@@ -1,68 +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/>.
//! Types that are specific to the Statemine runtime.
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime::FixedU128;
/// Unchecked Statemine extrinsic.
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
/// Statemine Runtime `Call` enum.
///
/// The enum represents a subset of possible `Call`s we can send to Statemine chain.
/// Ideally this code would be auto-generated from metadata, because we want to
/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s.
///
/// All entries here (like pretty much in the entire file) must be kept in sync with Statemine
/// `construct_runtime`, so that we maintain SCALE-compatibility.
///
/// See: [link](https://github.com/paritytech/cumulus/blob/master/parachains/runtimes/assets/statemine/src/lib.rs)
#[allow(clippy::large_enum_variant)]
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum Call {
/// With-Statemint bridge pallet.
// TODO (https://github.com/paritytech/parity-bridges-common/issues/1626):
// must be updated when we'll make appropriate changes in the Statemine runtime
#[codec(index = 42)]
WithStatemintBridgePallet(WithStatemintBridgePalletCall),
}
/// Calls of the with-Statemint bridge pallet.
// TODO (https://github.com/paritytech/parity-bridges-common/issues/1626):
// must be updated when we'll make appropriate changes in the Statemine runtime
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
#[allow(non_camel_case_types)]
pub enum WithStatemintBridgePalletCall {
#[codec(index = 42)]
update_dot_to_ksm_conversion_rate(FixedU128),
}
impl sp_runtime::traits::Dispatchable for Call {
type RuntimeOrigin = ();
type Config = ();
type Info = ();
type PostInfo = ();
fn dispatch(
self,
_origin: Self::RuntimeOrigin,
) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
unimplemented!("The Call is not expected to be dispatched.")
}
}
+1 -7
View File
@@ -19,7 +19,7 @@ use bp_runtime::{
Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEra, TransactionEraOf,
};
use codec::{Codec, Encode};
use frame_support::weights::{Weight, WeightToFee};
use frame_support::weights::WeightToFee;
use jsonrpsee::core::{DeserializeOwned, Serialize};
use num_traits::Zero;
use sc_transaction_pool_api::TransactionStatus;
@@ -52,8 +52,6 @@ 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;
/// Block type.
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
@@ -106,10 +104,6 @@ pub trait ChainWithMessages: Chain {
/// The method is provided by the runtime that is bridged with this `ChainWithMessages`.
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str;
/// Additional weight of the dispatch fee payment if dispatch is paid at the target chain
/// and this `ChainWithMessages` is the target chain.
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight;
/// Maximal number of unrewarded relayers in a single confirmation transaction at this
/// `ChainWithMessages`.
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
@@ -17,7 +17,5 @@
//! Contains several Substrate-specific metrics that may be exposed by relay.
pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric};
pub use storage_proof_overhead::StorageProofOverheadMetric;
mod float_storage_value;
mod storage_proof_overhead;
@@ -1,101 +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::{chain::Chain, client::Client, error::Error};
use async_trait::async_trait;
use relay_utils::metrics::{
metric_name, register, Gauge, Metric, PrometheusError, Registry, StandaloneMetric, U64,
};
use sp_core::storage::StorageKey;
use sp_runtime::traits::Header as HeaderT;
use sp_storage::well_known_keys::CODE;
use std::time::Duration;
/// Storage proof overhead update interval (in blocks).
const UPDATE_INTERVAL_IN_BLOCKS: u32 = 100;
/// Metric that represents extra size of storage proof as unsigned integer gauge.
///
/// There's one thing to keep in mind when using this metric: the overhead may be slightly
/// different for other values, but this metric gives a good estimation.
#[derive(Debug)]
pub struct StorageProofOverheadMetric<C: Chain> {
client: Client<C>,
metric: Gauge<U64>,
}
impl<C: Chain> Clone for StorageProofOverheadMetric<C> {
fn clone(&self) -> Self {
StorageProofOverheadMetric { client: self.client.clone(), metric: self.metric.clone() }
}
}
impl<C: Chain> StorageProofOverheadMetric<C> {
/// Create new metric instance with given name and help.
pub fn new(client: Client<C>, name: String, help: String) -> Result<Self, PrometheusError> {
Ok(StorageProofOverheadMetric {
client,
metric: Gauge::new(metric_name(None, &name), help)?,
})
}
/// Returns approximate storage proof size overhead.
async fn compute_storage_proof_overhead(&self) -> Result<usize, Error> {
let best_header_hash = self.client.best_finalized_header_hash().await?;
let best_header = self.client.header_by_hash(best_header_hash).await?;
let storage_proof = self
.client
.prove_storage(vec![StorageKey(CODE.to_vec())], best_header_hash)
.await?;
let storage_proof_size: usize = storage_proof.iter_nodes().map(|n| n.len()).sum();
let storage_value_reader = bp_runtime::StorageProofChecker::<C::Hasher>::new(
*best_header.state_root(),
storage_proof,
)
.map_err(Error::StorageProofError)?;
let maybe_encoded_storage_value =
storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?;
let encoded_storage_value_size =
maybe_encoded_storage_value.ok_or(Error::MissingMandatoryStorageValue)?.len();
Ok(storage_proof_size - encoded_storage_value_size)
}
}
impl<C: Chain> Metric for StorageProofOverheadMetric<C> {
fn register(&self, registry: &Registry) -> Result<(), PrometheusError> {
register(self.metric.clone(), registry).map(drop)
}
}
#[async_trait]
impl<C: Chain> StandaloneMetric for StorageProofOverheadMetric<C> {
fn update_interval(&self) -> Duration {
C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS
}
async fn update(&self) {
relay_utils::metrics::set_gauge_value(
&self.metric,
self.compute_storage_proof_overhead()
.await
.map(|overhead| Some(overhead as u64)),
);
}
}
@@ -54,7 +54,6 @@ impl Chain for TestChain {
const TOKEN_ID: Option<&'static str> = None;
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0);
const STORAGE_PROOF_OVERHEAD: u32 = 0;
type SignedBlock = sp_runtime::generic::SignedBlock<
sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>,
-2
View File
@@ -57,7 +57,6 @@ impl Chain for Westend {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_westend::SignedBlock;
type Call = ();
@@ -114,7 +113,6 @@ impl Chain for Westmint {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_westend::BEST_FINALIZED_WESTMINT_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_westend::SignedBlock;
type Call = ();
-1
View File
@@ -57,7 +57,6 @@ impl Chain for Wococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_wococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_wococo::SignedBlock;
type Call = ();
@@ -1,439 +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/>.
//! Tools for updating conversion rate that is stored in the runtime storage.
use crate::{messages_lane::SubstrateMessageLane, TransactionParams};
use relay_substrate_client::{
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, ChainWithTransactions,
Client, SignParam, TransactionEra, UnsignedTransaction,
};
use relay_utils::metrics::F64SharedRef;
use sp_core::Pair;
use std::time::{Duration, Instant};
/// Duration between updater iterations.
const SLEEP_DURATION: Duration = Duration::from_secs(60);
/// Duration which will almost never expire. Since changing conversion rate may require manual
/// intervention (e.g. if call is made through `multisig` pallet), we don't want relayer to
/// resubmit transaction often.
const ALMOST_NEVER_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 30);
/// Update-conversion-rate transaction status.
#[derive(Debug, Clone, Copy, PartialEq)]
enum TransactionStatus {
/// We have not submitted any transaction recently.
Idle,
/// We have recently submitted transaction that should update conversion rate.
Submitted(Instant, f64),
}
/// Different ways of building 'update conversion rate' calls.
pub trait UpdateConversionRateCallBuilder<C: Chain> {
/// Given conversion rate, build call that updates conversion rate in given chain runtime
/// storage.
fn build_update_conversion_rate_call(conversion_rate: f64) -> anyhow::Result<CallOf<C>>;
}
impl<C: Chain> UpdateConversionRateCallBuilder<C> for () {
fn build_update_conversion_rate_call(_conversion_rate: f64) -> anyhow::Result<CallOf<C>> {
Err(anyhow::format_err!("Conversion rate update is not supported at {}", C::NAME))
}
}
/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when
/// you have a direct access to the source chain runtime.
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_direct_update_conversion_rate_call_builder {
(
$source_chain:ident,
$mocked_builder:ident,
$runtime:ty,
$instance:ty,
$parameter:path
) => {
pub struct $mocked_builder;
impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain>
for $mocked_builder
{
fn build_update_conversion_rate_call(
conversion_rate: f64,
) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> {
Ok(pallet_bridge_messages::Call::update_pallet_parameter::<$runtime, $instance> {
parameter: $parameter(sp_runtime::FixedU128::from_float(conversion_rate)),
}.into())
}
}
};
}
/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when
/// you only have an access to the mocked version of source chain runtime. In this case you
/// should provide "name" of the call variant for the bridge messages calls, the "name" of
/// the variant for the `update_pallet_parameter` call within that first option and the name
/// of the conversion rate parameter itself.
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_mocked_update_conversion_rate_call_builder {
(
$source_chain:ident,
$mocked_builder:ident,
$bridge_messages:path,
$update_pallet_parameter:path,
$parameter:path
) => {
pub struct $mocked_builder;
impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain>
for $mocked_builder
{
fn build_update_conversion_rate_call(
conversion_rate: f64,
) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> {
Ok($bridge_messages($update_pallet_parameter($parameter(
sp_runtime::FixedU128::from_float(conversion_rate),
))))
}
}
};
}
/// Run infinite conversion rate updater loop.
///
/// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens *
/// Rate`.
pub fn run_conversion_rate_update_loop<Lane>(
client: Client<Lane::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
left_to_right_stored_conversion_rate: F64SharedRef,
left_to_base_conversion_rate: F64SharedRef,
right_to_base_conversion_rate: F64SharedRef,
max_difference_ratio: f64,
) where
Lane: SubstrateMessageLane,
Lane::SourceChain: ChainWithTransactions,
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> as Pair>::Public>,
{
let stall_timeout = transaction_stall_timeout(
transaction_params.mortality,
Lane::SourceChain::AVERAGE_BLOCK_INTERVAL,
ALMOST_NEVER_DURATION,
);
log::info!(
target: "bridge",
"Starting {} -> {} conversion rate (on {}) update loop. Stall timeout: {}s",
Lane::TargetChain::NAME,
Lane::SourceChain::NAME,
Lane::SourceChain::NAME,
stall_timeout.as_secs(),
);
async_std::task::spawn(async move {
let mut transaction_status = TransactionStatus::Idle;
loop {
async_std::task::sleep(SLEEP_DURATION).await;
let maybe_new_conversion_rate = maybe_select_new_conversion_rate(
stall_timeout,
&mut transaction_status,
&left_to_right_stored_conversion_rate,
&left_to_base_conversion_rate,
&right_to_base_conversion_rate,
max_difference_ratio,
)
.await;
if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate {
log::info!(
target: "bridge",
"Going to update {} -> {} (on {}) conversion rate to {}.",
Lane::TargetChain::NAME,
Lane::SourceChain::NAME,
Lane::SourceChain::NAME,
new_conversion_rate,
);
let result = update_target_to_source_conversion_rate::<Lane>(
client.clone(),
transaction_params.clone(),
new_conversion_rate,
)
.await;
match result {
Ok(()) => {
transaction_status =
TransactionStatus::Submitted(Instant::now(), prev_conversion_rate);
},
Err(error) => {
log::error!(
target: "bridge",
"Failed to submit conversion rate update transaction: {:?}",
error,
);
},
}
}
}
});
}
/// Select new conversion rate to submit to the node.
async fn maybe_select_new_conversion_rate(
stall_timeout: Duration,
transaction_status: &mut TransactionStatus,
left_to_right_stored_conversion_rate: &F64SharedRef,
left_to_base_conversion_rate: &F64SharedRef,
right_to_base_conversion_rate: &F64SharedRef,
max_difference_ratio: f64,
) -> Option<(f64, f64)> {
let left_to_right_stored_conversion_rate =
(*left_to_right_stored_conversion_rate.read().await)?;
match *transaction_status {
TransactionStatus::Idle => (),
TransactionStatus::Submitted(submitted_at, _)
if Instant::now() - submitted_at > stall_timeout =>
{
log::error!(
target: "bridge",
"Conversion rate update transaction has been lost and loop stalled. Restarting",
);
// we assume that our transaction has been lost
*transaction_status = TransactionStatus::Idle;
},
TransactionStatus::Submitted(_, previous_left_to_right_stored_conversion_rate) => {
// we can't compare float values from different sources directly, so we only care
// whether the stored rate has been changed or not. If it has been changed, then we
// assume that our proposal has been accepted.
//
// float comparison is ok here, because we compare same-origin (stored in runtime
// storage) values and if they are different, it means that the value has actually been
// updated
#[allow(clippy::float_cmp)]
if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate
{
// the rate has not been changed => we won't submit any transactions until it is
// accepted, or the rate is changed by someone else
return None
}
*transaction_status = TransactionStatus::Idle;
},
}
let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?;
let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?;
let actual_left_to_right_conversion_rate =
left_to_base_conversion_rate / right_to_base_conversion_rate;
let rate_difference =
(actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
let rate_difference_ratio = rate_difference / left_to_right_stored_conversion_rate;
if rate_difference_ratio < max_difference_ratio {
return None
}
Some((left_to_right_stored_conversion_rate, actual_left_to_right_conversion_rate))
}
/// Update Target -> Source tokens conversion rate, stored in the Source runtime storage.
pub async fn update_target_to_source_conversion_rate<Lane>(
client: Client<Lane::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
updated_rate: f64,
) -> anyhow::Result<()>
where
Lane: SubstrateMessageLane,
Lane::SourceChain: ChainWithTransactions,
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> as Pair>::Public>,
{
let genesis_hash = *client.genesis_hash();
let signer_id = transaction_params.signer.public().into();
let (spec_version, transaction_version) = client.simple_runtime_version().await?;
let call =
Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call(
updated_rate,
)?;
client
.submit_signed_extrinsic(
signer_id,
SignParam::<Lane::SourceChain> {
spec_version,
transaction_version,
genesis_hash,
signer: transaction_params.signer,
},
move |best_block_id, transaction_nonce| {
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
.era(TransactionEra::new(best_block_id, transaction_params.mortality)))
},
)
.await
.map(drop)
.map_err(|err| anyhow::format_err!("{:?}", err))
}
#[cfg(test)]
mod tests {
use super::*;
use async_std::sync::{Arc, RwLock};
const TEST_STALL_TIMEOUT: Duration = Duration::from_secs(60);
fn test_maybe_select_new_conversion_rate(
mut transaction_status: TransactionStatus,
stored_conversion_rate: Option<f64>,
left_to_base_conversion_rate: Option<f64>,
right_to_base_conversion_rate: Option<f64>,
max_difference_ratio: f64,
) -> (Option<(f64, f64)>, TransactionStatus) {
let stored_conversion_rate = Arc::new(RwLock::new(stored_conversion_rate));
let left_to_base_conversion_rate = Arc::new(RwLock::new(left_to_base_conversion_rate));
let right_to_base_conversion_rate = Arc::new(RwLock::new(right_to_base_conversion_rate));
let result = async_std::task::block_on(maybe_select_new_conversion_rate(
TEST_STALL_TIMEOUT,
&mut transaction_status,
&stored_conversion_rate,
&left_to_base_conversion_rate,
&right_to_base_conversion_rate,
max_difference_ratio,
));
(result, transaction_status)
}
#[test]
fn rate_is_not_updated_when_transaction_is_submitted() {
let status = TransactionStatus::Submitted(Instant::now(), 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(None, status),
);
}
#[test]
fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Submitted(Instant::now(), 1.0),
Some(10.0),
Some(1.0),
Some(1.0),
100.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_left_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
None,
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_right_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
Some(1.0),
None,
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_stored_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
None,
Some(1.0),
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_difference_is_below_threshold() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(1.0),
Some(1.0),
Some(1.01),
0.02
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_submitted_when_difference_is_above_threshold() {
let left_to_right_stored_conversion_rate = 1.0;
let left_to_base_conversion_rate = 18f64;
let right_to_base_conversion_rate = 180f64;
assert!(left_to_base_conversion_rate < right_to_base_conversion_rate);
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(left_to_right_stored_conversion_rate),
Some(left_to_base_conversion_rate),
Some(right_to_base_conversion_rate),
0.02
),
(
Some((
left_to_right_stored_conversion_rate,
left_to_base_conversion_rate / right_to_base_conversion_rate,
)),
TransactionStatus::Idle
),
);
}
#[test]
fn transaction_expires() {
let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT / 2, 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(None, status),
);
let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT * 2, 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(Some((10.0, 1.0)), TransactionStatus::Idle),
);
}
}
@@ -1,106 +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/>.
//! Substrate relay helpers
use relay_utils::metrics::{FloatJsonValueMetric, PrometheusError, StandaloneMetric};
/// Creates standalone token price metric.
pub fn token_price_metric(token_id: &str) -> Result<FloatJsonValueMetric, PrometheusError> {
FloatJsonValueMetric::new(
format!("https://api.coingecko.com/api/v3/simple/price?ids={token_id}&vs_currencies=btc"),
format!("$.{token_id}.btc"),
format!("{}_to_base_conversion_rate", token_id.replace('-', "_")),
format!("Rate used to convert from {} to some BASE tokens", token_id.to_uppercase()),
)
}
/// Compute conversion rate between two tokens immediately, without spawning any metrics.
///
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
pub async fn tokens_conversion_rate_from_metrics(
from_token_id: &str,
to_token_id: &str,
) -> anyhow::Result<f64> {
let from_token_metric = token_price_metric(from_token_id)?;
from_token_metric.update().await;
let to_token_metric = token_price_metric(to_token_id)?;
to_token_metric.update().await;
let from_token_value = *from_token_metric.shared_value_ref().read().await;
let to_token_value = *to_token_metric.shared_value_ref().read().await;
// `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional
// checks required here
match (from_token_value, to_token_value) {
(Some(from_token_value), Some(to_token_value)) =>
Ok(tokens_conversion_rate(from_token_value, to_token_value)),
_ => Err(anyhow::format_err!(
"Failed to compute conversion rate from {} to {}",
from_token_id,
to_token_id,
)),
}
}
/// Compute conversion rate between two tokens, given token prices.
///
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
///
/// Both prices are assumed to be normal and non-negative.
pub fn tokens_conversion_rate(from_token_value: f64, to_token_value: f64) -> f64 {
from_token_value / to_token_value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rialto_to_millau_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(rialto_price, millau_price);
let rialto_amount = 100.0;
let millau_amount = rialto_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} RLT * {} = {} MLU",
rialto_amount,
conversion_rate,
millau_amount,
);
}
#[test]
fn millau_to_rialto_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(millau_price, rialto_price);
let millau_amount = 100.0;
let rialto_amount = millau_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} MLU * {} = {} RLT",
millau_amount,
conversion_rate,
rialto_amount,
);
}
}
@@ -18,10 +18,8 @@
#![warn(missing_docs)]
pub mod conversion_rate_update;
pub mod error;
pub mod finality;
pub mod helpers;
pub mod messages_lane;
pub mod messages_metrics;
pub mod messages_source;
@@ -17,8 +17,6 @@
//! Tools for supporting message lanes between two Substrate-based chains.
use crate::{
conversion_rate_update::UpdateConversionRateCallBuilder,
messages_metrics::StandaloneMessagesMetrics,
messages_source::{SubstrateMessagesProof, SubstrateMessagesSource},
messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget},
on_demand::OnDemandRelay,
@@ -33,49 +31,21 @@ use bridge_runtime_common::messages::{
};
use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use messages_relay::{message_lane::MessageLane, relay_strategy::RelayStrategy};
use messages_relay::message_lane::MessageLane;
use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig};
use relay_substrate_client::{
transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain,
ChainWithMessages, ChainWithTransactions, Client, HashOf,
};
use relay_utils::{metrics::MetricsParams, STALL_TIMEOUT};
use relay_utils::{
metrics::{GlobalMetrics, MetricsParams, StandaloneMetric},
STALL_TIMEOUT,
};
use sp_core::Pair;
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
/// Substrate -> Substrate messages synchronization pipeline.
pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
/// Name of the source -> target tokens conversion rate parameter.
///
/// The parameter is stored at the target chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
/// Name of the target -> source tokens conversion rate parameter.
///
/// The parameter is stored at the source chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
/// Name of the source chain fee multiplier parameter.
///
/// The parameter is stored at the target chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
/// Name of the target chain fee multiplier parameter.
///
/// The parameter is stored at the source chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
/// Name of the transaction payment pallet, deployed at the source chain.
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
/// Name of the transaction payment pallet, deployed at the target chain.
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
/// Messages of this chain are relayed to the `TargetChain`.
type SourceChain: ChainWithMessages + ChainWithTransactions;
/// Messages from the `SourceChain` are dispatched on this chain.
@@ -85,16 +55,6 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder<Self>;
/// How receive messages delivery proof call is built?
type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder<Self>;
/// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder.
///
/// If not applicable to this bridge, you may use `()` here.
type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder<
Self::SourceChain,
>;
/// Message relay strategy.
type RelayStrategy: RelayStrategy;
}
/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`.
@@ -138,10 +98,6 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane> {
pub lane_id: LaneId,
/// Metrics parameters.
pub metrics_params: MetricsParams,
/// Pre-registered standalone metrics.
pub standalone_metrics: Option<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>>,
/// Relay strategy.
pub relay_strategy: P::RelayStrategy,
}
/// Run Substrate-to-Substrate messages sync loop.
@@ -170,13 +126,6 @@ where
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
(max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2);
let standalone_metrics = params.standalone_metrics.map(Ok).unwrap_or_else(|| {
crate::messages_metrics::standalone_metrics::<P>(
source_client.clone(),
target_client.clone(),
)
})?;
log::info!(
target: "bridge",
"Starting {} -> {} messages relay.\n\t\
@@ -220,7 +169,6 @@ where
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
relay_strategy: params.relay_strategy,
},
},
SubstrateMessagesSource::<P>::new(
@@ -236,10 +184,12 @@ where
params.lane_id,
relayer_id_at_source,
params.target_transaction_params,
standalone_metrics.clone(),
params.source_to_target_headers_relay,
),
standalone_metrics.register_and_spawn(params.metrics_params)?,
{
GlobalMetrics::new()?.register_and_spawn(&params.metrics_params.registry)?;
params.metrics_params
},
futures::future::pending(),
)
.await
@@ -271,7 +221,6 @@ where
R: BridgeMessagesConfig<I, InboundRelayer = AccountIdOf<P::SourceChain>>,
I: 'static,
R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
R::InboundMessageFee,
MessagesProof = FromBridgedChainMessagesProof<HashOf<P::SourceChain>>,
>,
CallOf<P::TargetChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
@@ -16,260 +16,20 @@
//! Tools for supporting message lanes between two Substrate-based chains.
use crate::{helpers::tokens_conversion_rate, messages_lane::SubstrateMessageLane, TaggedAccount};
use crate::TaggedAccount;
use codec::Decode;
use frame_system::AccountInfo;
use pallet_balances::AccountData;
use relay_substrate_client::{
metrics::{
FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric, StorageProofOverheadMetric,
},
metrics::{FloatStorageValue, FloatStorageValueMetric},
AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf,
};
use relay_utils::metrics::{
FloatJsonValueMetric, GlobalMetrics, MetricsParams, PrometheusError, StandaloneMetric,
};
use relay_utils::metrics::{MetricsParams, StandaloneMetric};
use sp_core::storage::StorageData;
use sp_runtime::{FixedPointNumber, FixedU128};
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
/// Name of the `NextFeeMultiplier` storage value within the transaction payment pallet.
const NEXT_FEE_MULTIPLIER_VALUE_NAME: &str = "NextFeeMultiplier";
/// Shared references to the standalone metrics of the message lane relay loop.
#[derive(Debug, Clone)]
pub struct StandaloneMessagesMetrics<SC: Chain, TC: Chain> {
/// Global metrics.
pub global: GlobalMetrics,
/// Storage chain proof overhead metric.
pub source_storage_proof_overhead: StorageProofOverheadMetric<SC>,
/// Target chain proof overhead metric.
pub target_storage_proof_overhead: StorageProofOverheadMetric<TC>,
/// Source tokens to base conversion rate metric.
pub source_to_base_conversion_rate: Option<FloatJsonValueMetric>,
/// Target tokens to base conversion rate metric.
pub target_to_base_conversion_rate: Option<FloatJsonValueMetric>,
/// Source tokens to target tokens conversion rate metric. This rate is stored by the target
/// chain.
pub source_to_target_conversion_rate: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Target tokens to source tokens conversion rate metric. This rate is stored by the source
/// chain.
pub target_to_source_conversion_rate: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
/// Actual source chain fee multiplier.
pub source_fee_multiplier: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
/// Source chain fee multiplier, stored at the target chain.
pub source_fee_multiplier_at_target: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Actual target chain fee multiplier.
pub target_fee_multiplier: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Target chain fee multiplier, stored at the target chain.
pub target_fee_multiplier_at_source: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
}
impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> {
/// Swap source and target sides.
pub fn reverse(self) -> StandaloneMessagesMetrics<TC, SC> {
StandaloneMessagesMetrics {
global: self.global,
source_storage_proof_overhead: self.target_storage_proof_overhead,
target_storage_proof_overhead: self.source_storage_proof_overhead,
source_to_base_conversion_rate: self.target_to_base_conversion_rate,
target_to_base_conversion_rate: self.source_to_base_conversion_rate,
source_to_target_conversion_rate: self.target_to_source_conversion_rate,
target_to_source_conversion_rate: self.source_to_target_conversion_rate,
source_fee_multiplier: self.target_fee_multiplier,
source_fee_multiplier_at_target: self.target_fee_multiplier_at_source,
target_fee_multiplier: self.source_fee_multiplier,
target_fee_multiplier_at_source: self.source_fee_multiplier_at_target,
}
}
/// Register all metrics in the registry.
pub fn register_and_spawn(
self,
metrics: MetricsParams,
) -> Result<MetricsParams, PrometheusError> {
self.global.register_and_spawn(&metrics.registry)?;
self.source_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
self.target_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
if let Some(m) = self.source_to_base_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_to_base_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_to_source_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.source_fee_multiplier {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.source_fee_multiplier_at_target {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_fee_multiplier {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_fee_multiplier_at_source {
m.register_and_spawn(&metrics.registry)?;
}
Ok(metrics)
}
/// Return conversion rate from target to source tokens.
pub async fn target_to_source_conversion_rate(&self) -> Option<f64> {
let from_token_value =
(*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
let to_token_value =
(*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
Some(tokens_conversion_rate(from_token_value, to_token_value))
}
}
/// Create symmetric standalone metrics for the message lane relay loop.
///
/// All metrics returned by this function are exposed by loops that are serving given lane (`P`)
/// and by loops that are serving reverse lane (`P` with swapped `TargetChain` and `SourceChain`).
/// We assume that either conversion rate parameters have values in the storage, or they are
/// initialized with 1:1.
pub fn standalone_metrics<P: SubstrateMessageLane>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
) -> anyhow::Result<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>> {
Ok(StandaloneMessagesMetrics {
global: GlobalMetrics::new()?,
source_storage_proof_overhead: StorageProofOverheadMetric::new(
source_client.clone(),
format!("{}_storage_proof_overhead", P::SourceChain::NAME.to_lowercase()),
format!("{} storage proof overhead", P::SourceChain::NAME),
)?,
target_storage_proof_overhead: StorageProofOverheadMetric::new(
target_client.clone(),
format!("{}_storage_proof_overhead", P::TargetChain::NAME.to_lowercase()),
format!("{} storage proof overhead", P::TargetChain::NAME),
)?,
source_to_base_conversion_rate: P::SourceChain::TOKEN_ID
.map(|source_chain_token_id| {
crate::helpers::token_price_metric(source_chain_token_id).map(Some)
})
.unwrap_or(Ok(None))?,
target_to_base_conversion_rate: P::TargetChain::TOKEN_ID
.map(|target_chain_token_id| {
crate::helpers::token_price_metric(target_chain_token_id).map(Some)
})
.unwrap_or(Ok(None))?,
source_to_target_conversion_rate: P::SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client.clone(),
key,
format!(
"{}_{}_to_{}_conversion_rate",
P::TargetChain::NAME,
P::SourceChain::NAME,
P::TargetChain::NAME
),
format!(
"{} to {} tokens conversion rate (used by {})",
P::SourceChain::NAME,
P::TargetChain::NAME,
P::TargetChain::NAME
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_to_source_conversion_rate: P::TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client.clone(),
key,
format!(
"{}_{}_to_{}_conversion_rate",
P::SourceChain::NAME,
P::TargetChain::NAME,
P::SourceChain::NAME
),
format!(
"{} to {} tokens conversion rate (used by {})",
P::TargetChain::NAME,
P::SourceChain::NAME,
P::SourceChain::NAME
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
source_fee_multiplier: P::AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
.map(|key| {
log::trace!(target: "bridge", "{}_fee_multiplier", P::SourceChain::NAME);
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client.clone(),
key,
format!("{}_fee_multiplier", P::SourceChain::NAME,),
format!("{} fee multiplier", P::SourceChain::NAME,),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
source_fee_multiplier_at_target: P::SOURCE_FEE_MULTIPLIER_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client.clone(),
key,
format!("{}_{}_fee_multiplier", P::TargetChain::NAME, P::SourceChain::NAME,),
format!(
"{} fee multiplier stored at {}",
P::SourceChain::NAME,
P::TargetChain::NAME,
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_fee_multiplier: P::AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
.map(|key| {
log::trace!(target: "bridge", "{}_fee_multiplier", P::TargetChain::NAME);
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client,
key,
format!("{}_fee_multiplier", P::TargetChain::NAME,),
format!("{} fee multiplier", P::TargetChain::NAME,),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_fee_multiplier_at_source: P::TARGET_FEE_MULTIPLIER_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client,
key,
format!("{}_{}_fee_multiplier", P::SourceChain::NAME, P::TargetChain::NAME,),
format!(
"{} fee multiplier stored at {}",
P::TargetChain::NAME,
P::SourceChain::NAME,
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
})
}
/// Add relay accounts balance metrics.
pub async fn add_relay_balances_metrics<C: ChainWithBalances>(
client: Client<C>,
@@ -359,9 +119,6 @@ fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 {
#[cfg(test)]
mod tests {
use super::*;
use frame_support::storage::generator::StorageValue;
use sp_core::storage::StorageKey;
#[test]
fn token_decimals_used_properly() {
let plancks = 425_000_000_000;
@@ -369,12 +126,4 @@ mod tests {
let dots = convert_to_token_balance(plancks, token_decimals);
assert_eq!(dots, FixedU128::saturating_from_rational(425, 10));
}
#[test]
fn next_fee_multiplier_storage_key_is_correct() {
assert_eq!(
bp_runtime::storage_value_key("TransactionPayment", NEXT_FEE_MULTIPLIER_VALUE_NAME),
StorageKey(pallet_transaction_payment::NextFeeMultiplier::<rialto_runtime::Runtime>::storage_value_final_key().to_vec()),
);
}
}
@@ -31,13 +31,11 @@ use async_std::sync::Arc;
use async_trait::async_trait;
use bp_messages::{
storage_keys::{operating_mode_key, outbound_lane_data_key},
InboundMessageDetails, LaneId, MessageData, MessageNonce, MessagePayload,
MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState,
};
use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, HeaderIdProvider};
use bridge_runtime_common::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
InboundMessageDetails, LaneId, MessageNonce, MessagePayload, MessagesOperatingMode,
OutboundLaneData, OutboundMessageDetails,
};
use bp_runtime::{BasicOperatingMode, HeaderIdProvider};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use messages_relay::{
@@ -47,11 +45,11 @@ use messages_relay::{
SourceClientState,
},
};
use num_traits::{Bounded, Zero};
use num_traits::Zero;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
TransactionEra, TransactionTracker, UnsignedTransaction,
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
TransactionTracker, UnsignedTransaction,
};
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::{Bytes, Pair};
@@ -62,7 +60,7 @@ use std::ops::RangeInclusive;
/// required to submit to the target node: cumulative dispatch weight of bundled messages and
/// the proof itself.
pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
type MessagesToRefine<'a, Balance> = Vec<(MessagePayload, &'a mut OutboundMessageDetails<Balance>)>;
type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>;
/// Substrate client as Substrate messages source.
pub struct SubstrateMessagesSource<P: SubstrateMessageLane> {
@@ -207,9 +205,7 @@ where
// prepare arguments of the inbound message details call (if we need it)
let mut msgs_to_refine = vec![];
for out_msg_details in out_msgs_details.iter_mut() {
if out_msg_details.dispatch_fee_payment != DispatchFeePayment::AtTargetChain {
continue
}
// in our current strategy all messages are supposed to be paid at the target chain
// for pay-at-target messages we may want to ask target chain for
// refined dispatch weight
@@ -218,7 +214,7 @@ where
&self.lane_id,
out_msg_details.nonce,
);
let msg_data: MessageData<BalanceOf<P::SourceChain>> =
let msg_payload: MessagePayload =
self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| {
SubstrateError::Custom(format!(
"Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}",
@@ -230,7 +226,7 @@ where
))
})?;
msgs_to_refine.push((msg_data.payload, out_msg_details));
msgs_to_refine.push((msg_payload, out_msg_details));
}
for mut msgs_to_refine_batch in
@@ -277,8 +273,7 @@ where
MessageDetails {
dispatch_weight: out_msg_details.dispatch_weight,
size: out_msg_details.size as _,
reward: out_msg_details.delivery_and_dispatch_fee,
dispatch_fee_payment: out_msg_details.dispatch_fee_payment,
reward: Zero::zero(),
},
);
}
@@ -370,39 +365,6 @@ where
target_to_source_headers_relay.require_more_headers(id.0).await;
}
}
async fn estimate_confirmation_transaction(
&self,
) -> <MessageLaneAdapter<P> as MessageLane>::SourceChainBalance {
let runtime_version = match self.source_client.runtime_version().await {
Ok(v) => v,
Err(_) => return BalanceOf::<P::SourceChain>::max_value(),
};
async {
let dummy_tx = P::SourceChain::sign_transaction(
SignParam::<P::SourceChain> {
spec_version: runtime_version.spec_version,
transaction_version: runtime_version.transaction_version,
genesis_hash: *self.source_client.genesis_hash(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_proof_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
prepare_dummy_messages_delivery_proof::<P::SourceChain, P::TargetChain>(),
false,
)?,
)?
.encode();
self.source_client
.estimate_extrinsic_fee(Bytes(dummy_tx))
.await
.map(|fee| fee.inclusion_fee())
}
.await
.unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value())
}
}
/// Ensure that the messages pallet at source chain is active.
@@ -441,30 +403,6 @@ fn make_messages_delivery_proof_transaction<P: SubstrateMessageLane>(
.era(TransactionEra::new(source_best_block_id, source_transaction_params.mortality)))
}
/// 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>(
) -> SubstrateMessagesDeliveryProof<TC> {
let single_message_confirmation_size =
bp_messages::InboundLaneData::<()>::encoded_size_hint_u32(1, 1);
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,
last_delivered_nonce: 1,
},
FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: Default::default(),
storage_proof: vec![vec![0; proof_size as usize]],
lane: Default::default(),
},
)
}
/// Read best blocks from given client.
///
/// This function assumes that the chain that is followed by the `self_client` has
@@ -552,7 +490,7 @@ where
}
fn validate_out_msgs_details<C: Chain>(
out_msgs_details: &[OutboundMessageDetails<C::Balance>],
out_msgs_details: &[OutboundMessageDetails],
nonces: RangeInclusive<MessageNonce>,
) -> Result<(), SubstrateError> {
let make_missing_nonce_error = |expected_nonce| {
@@ -601,8 +539,8 @@ fn validate_out_msgs_details<C: Chain>(
fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
lane_id: LaneId,
msgs_to_refine: MessagesToRefine<Source::Balance>,
) -> Result<Vec<MessagesToRefine<Source::Balance>>, SubstrateError> {
msgs_to_refine: MessagesToRefine,
) -> Result<Vec<MessagesToRefine>, SubstrateError> {
let max_batch_size = Target::max_extrinsic_size() as usize;
let mut batches = vec![];
@@ -635,23 +573,20 @@ fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
#[cfg(test)]
mod tests {
use super::*;
use bp_runtime::{messages::DispatchFeePayment, Chain as ChainBase};
use codec::MaxEncodedLen;
use bp_runtime::Chain as ChainBase;
use relay_rialto_client::Rialto;
use relay_rococo_client::Rococo;
use relay_wococo_client::Wococo;
fn message_details_from_rpc(
nonces: RangeInclusive<MessageNonce>,
) -> Vec<OutboundMessageDetails<bp_wococo::Balance>> {
) -> Vec<OutboundMessageDetails> {
nonces
.into_iter()
.map(|nonce| bp_messages::OutboundMessageDetails {
nonce,
dispatch_weight: Weight::zero(),
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
})
.collect()
}
@@ -704,31 +639,16 @@ mod tests {
));
}
#[test]
fn prepare_dummy_messages_delivery_proof_works() {
let expected_minimal_size =
bp_wococo::AccountId::max_encoded_len() as u32 + Rococo::STORAGE_PROOF_OVERHEAD;
let dummy_proof = prepare_dummy_messages_delivery_proof::<Wococo, Rococo>();
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(),
);
}
fn check_split_msgs_to_refine(
payload_sizes: Vec<usize>,
expected_batches: Result<Vec<usize>, ()>,
) {
let mut out_msgs_details = vec![];
for (idx, _) in payload_sizes.iter().enumerate() {
out_msgs_details.push(OutboundMessageDetails::<BalanceOf<Rialto>> {
out_msgs_details.push(OutboundMessageDetails {
nonce: idx as MessageNonce,
dispatch_weight: Weight::zero(),
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}
@@ -20,7 +20,6 @@
use crate::{
messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane},
messages_metrics::StandaloneMessagesMetrics,
messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof},
on_demand::OnDemandRelay,
TransactionParams,
@@ -32,24 +31,18 @@ use bp_messages::{
storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId,
MessageNonce, UnrewardedRelayersState,
};
use bridge_runtime_common::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
};
use codec::Encode;
use frame_support::weights::{Weight, WeightToFee};
use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof;
use messages_relay::{
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState},
};
use num_traits::{Bounded, Zero};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
TransactionEra, TransactionTracker, UnsignedTransaction, WeightToFeeOf,
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
TransactionTracker, UnsignedTransaction,
};
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::{Bytes, Pair};
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use relay_utils::relay_loop::Client as RelayClient;
use sp_core::Pair;
use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive};
/// Message receiving proof returned by the target Substrate node.
@@ -63,7 +56,6 @@ pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> {
lane_id: LaneId,
relayer_id_at_source: AccountIdOf<P::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
source_to_target_headers_relay: Option<Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>>,
}
@@ -75,7 +67,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
lane_id: LaneId,
relayer_id_at_source: AccountIdOf<P::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
source_to_target_headers_relay: Option<
Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>,
>,
@@ -86,7 +77,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
lane_id,
relayer_id_at_source,
transaction_params,
metric_values,
source_to_target_headers_relay,
}
}
@@ -121,7 +111,6 @@ impl<P: SubstrateMessageLane> Clone for SubstrateMessagesTarget<P> {
lane_id: self.lane_id,
relayer_id_at_source: self.relayer_id_at_source.clone(),
transaction_params: self.transaction_params.clone(),
metric_values: self.metric_values.clone(),
source_to_target_headers_relay: self.source_to_target_headers_relay.clone(),
}
}
@@ -283,142 +272,6 @@ where
source_to_target_headers_relay.require_more_headers(id.0).await;
}
}
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<<MessageLaneAdapter<P> as MessageLane>::SourceChainBalance, SubstrateError> {
let conversion_rate =
self.metric_values.target_to_source_conversion_rate().await.ok_or_else(|| {
SubstrateError::Custom(format!(
"Failed to compute conversion rate from {} to {}",
P::TargetChain::NAME,
P::SourceChain::NAME,
))
})?;
let (spec_version, transaction_version) =
self.target_client.simple_runtime_version().await?;
// Prepare 'dummy' delivery transaction - we only care about its length and dispatch weight.
let delivery_tx = P::TargetChain::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: Default::default(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
self.relayer_id_at_source.clone(),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
total_dispatch_weight,
total_size,
),
false,
)?,
)?
.encode();
let delivery_tx_fee = self.target_client.estimate_extrinsic_fee(Bytes(delivery_tx)).await?;
let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee();
// The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch
// fee payment (Currency::transfer in regular deployment). But if message dispatch has
// already been paid at the Source chain, the delivery transaction will refund relayer with
// this additional cost. But `estimate_extrinsic_fee` obviously just returns pre-dispatch
// cost of the transaction. So if transaction delivers prepaid message, then it may happen
// that pre-dispatch cost is larger than reward and `Rational` relayer will refuse to
// deliver this message.
//
// The most obvious solution would be to deduct total weight of dispatch fee payments from
// the `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if
// `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is
// strictly positive, so we can't use this option.
//
// Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target
// chain. This requires more knowledge of the Target chain, but seems there's no better way
// to solve this now.
let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 {
const WEIGHT_DIFFERENCE: Weight = Weight::from_ref_time(100);
let (spec_version, transaction_version) =
self.target_client.simple_runtime_version().await?;
let larger_dispatch_weight = total_dispatch_weight.saturating_add(WEIGHT_DIFFERENCE);
let dummy_tx = P::TargetChain::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: Default::default(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
self.relayer_id_at_source.clone(),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
larger_dispatch_weight,
total_size,
),
false,
)?,
)?
.encode();
let larger_delivery_tx_fee =
self.target_client.estimate_extrinsic_fee(Bytes(dummy_tx)).await?;
compute_prepaid_messages_refund::<P::TargetChain>(
total_prepaid_nonces,
compute_fee_multiplier::<P::TargetChain>(
delivery_tx_fee.adjusted_weight_fee,
total_dispatch_weight,
larger_delivery_tx_fee.adjusted_weight_fee,
larger_dispatch_weight,
),
)
} else {
Zero::zero()
};
let delivery_fee_in_source_tokens =
convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>(
FixedU128::from_float(conversion_rate),
inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens),
);
log::trace!(
target: "bridge",
"Estimated {} -> {} messages delivery transaction.\n\t\
Total nonces: {:?}\n\t\
Prepaid messages: {}\n\t\
Total messages size: {}\n\t\
Total messages dispatch weight: {}\n\t\
Inclusion fee (in {1} tokens): {:?}\n\t\
Expected refund (in {1} tokens): {:?}\n\t\
{1} -> {0} conversion rate: {:?}\n\t\
Expected delivery tx fee (in {0} tokens): {:?}",
P::SourceChain::NAME,
P::TargetChain::NAME,
nonces,
total_prepaid_nonces,
total_size,
total_dispatch_weight,
inclusion_fee_in_target_tokens,
expected_refund_in_target_tokens,
conversion_rate,
delivery_fee_in_source_tokens,
);
Ok(delivery_fee_in_source_tokens)
}
}
/// Make messages delivery transaction from given proof.
@@ -443,153 +296,3 @@ fn make_messages_delivery_transaction<P: SubstrateMessageLane>(
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
.era(TransactionEra::new(target_best_block_id, target_transaction_params.mortality)))
}
/// 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())
}
/// Compute fee multiplier that is used by the chain, given a couple of fees for transactions
/// that are only differ in dispatch weights.
///
/// This function assumes that standard transaction payment pallet is used by the chain.
/// The only fee component that depends on dispatch weight is the `adjusted_weight_fee`.
///
/// **WARNING**: this functions will only be accurate if weight-to-fee conversion function
/// is linear. For non-linear polynomials the error will grow with `weight_difference` growth.
/// So better to use smaller differences.
fn compute_fee_multiplier<C: ChainWithMessages>(
smaller_adjusted_weight_fee: BalanceOf<C>,
smaller_tx_weight: Weight,
larger_adjusted_weight_fee: BalanceOf<C>,
larger_tx_weight: Weight,
) -> FixedU128 {
let adjusted_weight_fee_difference =
larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee);
let smaller_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&smaller_tx_weight);
let larger_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&larger_tx_weight);
FixedU128::saturating_from_rational(
adjusted_weight_fee_difference,
larger_tx_unadjusted_weight_fee.saturating_sub(smaller_tx_unadjusted_weight_fee),
)
}
/// Compute fee that will be refunded to the relayer because dispatch of `total_prepaid_nonces`
/// messages has been paid at the source chain.
fn compute_prepaid_messages_refund<C: ChainWithMessages>(
total_prepaid_nonces: MessageNonce,
fee_multiplier: FixedU128,
) -> BalanceOf<C> {
fee_multiplier.saturating_mul_int(WeightToFeeOf::<C>::weight_to_fee(
&C::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN.saturating_mul(total_prepaid_nonces),
))
}
#[cfg(test)]
mod tests {
use super::*;
use relay_rialto_client::Rialto;
use relay_rococo_client::Rococo;
use relay_wococo_client::Wococo;
#[test]
fn prepare_dummy_messages_proof_works() {
const DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1_000_000);
const SIZE: u32 = 1_000;
let dummy_proof = prepare_dummy_messages_proof::<Rococo>(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::<Rococo, Wococo>((150, 100).into(), 1_000),
1_500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((50, 100).into(), 1_000),
500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((100, 100).into(), 1_000),
1_000
);
}
#[test]
fn compute_fee_multiplier_returns_sane_results() {
let multiplier: FixedU128 =
bp_rialto::WeightToFee::weight_to_fee(&Weight::from_ref_time(1)).into();
let smaller_weight = 1_000_000;
let smaller_adjusted_weight_fee = multiplier.saturating_mul_int(
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(smaller_weight)),
);
let larger_weight = smaller_weight + 200_000;
let larger_adjusted_weight_fee = multiplier.saturating_mul_int(
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(larger_weight)),
);
assert_eq!(
compute_fee_multiplier::<Rialto>(
smaller_adjusted_weight_fee,
Weight::from_ref_time(smaller_weight),
larger_adjusted_weight_fee,
Weight::from_ref_time(larger_weight),
),
multiplier,
);
}
#[test]
fn compute_prepaid_messages_refund_returns_sane_results() {
assert!(
compute_prepaid_messages_refund::<Rialto>(
10,
FixedU128::saturating_from_rational(110, 100),
) > Rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN
.saturating_mul(10u64)
.ref_time() as _
);
}
}
+1 -1
View File
@@ -29,9 +29,9 @@ mod metrics;
pub mod message_lane;
pub mod message_lane_loop;
pub mod relay_strategy;
mod message_race_delivery;
mod message_race_limits;
mod message_race_loop;
mod message_race_receiving;
mod message_race_strategy;
@@ -30,7 +30,6 @@ use async_trait::async_trait;
use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
use bp_runtime::messages::DispatchFeePayment;
use relay_utils::{
interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient,
retry_backoff, FailedClient, TransactionTracker,
@@ -41,12 +40,11 @@ use crate::{
message_race_delivery::run as run_message_delivery_race,
message_race_receiving::run as run_message_receiving_race,
metrics::MessageLaneLoopMetrics,
relay_strategy::RelayStrategy,
};
/// Message lane loop configuration params.
#[derive(Debug, Clone)]
pub struct Params<Strategy: RelayStrategy> {
pub struct Params {
/// Id of lane this loop is servicing.
pub lane: LaneId,
/// Interval at which we ask target node about its updates.
@@ -56,22 +54,12 @@ pub struct Params<Strategy: RelayStrategy> {
/// Delay between moments when connection error happens and our reconnect attempt.
pub reconnect_delay: Duration,
/// Message delivery race parameters.
pub delivery_params: MessageDeliveryParams<Strategy>,
}
/// Relayer operating mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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,
pub delivery_params: MessageDeliveryParams,
}
/// Message delivery race parameters.
#[derive(Debug, Clone)]
pub struct MessageDeliveryParams<Strategy: RelayStrategy> {
pub struct MessageDeliveryParams {
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number
/// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until
/// reward payment will be proved (by including outbound lane state to the message delivery
@@ -87,8 +75,6 @@ pub struct MessageDeliveryParams<Strategy: RelayStrategy> {
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// Relay strategy
pub relay_strategy: Strategy,
}
/// Message details.
@@ -100,8 +86,6 @@ pub struct MessageDetails<SourceChainBalance> {
pub size: u32,
/// The relayer reward paid in the source chain tokens.
pub reward: SourceChainBalance,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
}
/// Messages details map.
@@ -173,9 +157,6 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
/// We need given finalized target header on source to continue synchronization.
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P>);
/// Estimate cost of single message confirmation transaction in source chain tokens.
async fn estimate_confirmation_transaction(&self) -> P::SourceChainBalance;
}
/// Target client trait.
@@ -221,18 +202,6 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
/// We need given finalized source header on target to continue synchronization.
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>);
/// Estimate cost of messages delivery transaction in source chain tokens.
///
/// Please keep in mind that the returned cost must be converted to the source chain
/// tokens, even though the transaction fee will be paid in the target chain tokens.
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<P::SourceChainBalance, Self::Error>;
}
/// State of the client.
@@ -272,8 +241,8 @@ pub fn metrics_prefix<P: MessageLane>(lane: &LaneId) -> String {
}
/// Run message lane service loop.
pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
params: Params<Strategy>,
pub async fn run<P: MessageLane>(
params: Params,
source_client: impl SourceClient<P>,
target_client: impl TargetClient<P>,
metrics_params: MetricsParams,
@@ -283,11 +252,7 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
relay_utils::relay_loop(source_client, target_client)
.reconnect_delay(params.reconnect_delay)
.with_metrics(metrics_params)
.loop_metric(MessageLaneLoopMetrics::new(
Some(&metrics_prefix::<P>(&params.lane)),
P::SOURCE_NAME,
P::TARGET_NAME,
)?)?
.loop_metric(MessageLaneLoopMetrics::new(Some(&metrics_prefix::<P>(&params.lane)))?)?
.expose()
.await?
.run(metrics_prefix::<P>(&params.lane), move |source_client, target_client, metrics| {
@@ -304,13 +269,8 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
/// Run one-way message delivery loop until connection with target or source node is lost, or exit
/// signal is received.
async fn run_until_connection_lost<
P: MessageLane,
Strategy: RelayStrategy,
SC: SourceClient<P>,
TC: TargetClient<P>,
>(
params: Params<Strategy>,
async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
params: Params,
source_client: SC,
target_client: TC,
metrics_msg: Option<MessageLaneLoopMetrics>,
@@ -477,17 +437,12 @@ pub(crate) mod tests {
use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus};
use crate::relay_strategy::AltruisticStrategy;
use super::*;
pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId {
HeaderId(number, number)
}
pub const CONFIRMATION_TRANSACTION_COST: TestSourceChainBalance = 1;
pub const BASE_MESSAGE_DELIVERY_TRANSACTION_COST: TestSourceChainBalance = 1;
pub type TestSourceChainBalance = u64;
pub type TestSourceHeaderId = HeaderId<TestSourceHeaderNumber, TestSourceHeaderHash>;
pub type TestTargetHeaderId = HeaderId<TestTargetHeaderNumber, TestTargetHeaderHash>;
@@ -681,7 +636,6 @@ pub(crate) mod tests {
dispatch_weight: Weight::from_ref_time(1),
size: 1,
reward: 1,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
)
})
@@ -737,10 +691,6 @@ pub(crate) mod tests {
(self.tick)(&mut data);
(self.post_tick)(&mut data);
}
async fn estimate_confirmation_transaction(&self) -> TestSourceChainBalance {
CONFIRMATION_TRANSACTION_COST
}
}
#[derive(Clone)]
@@ -871,20 +821,6 @@ pub(crate) mod tests {
(self.tick)(&mut data);
(self.post_tick)(&mut data);
}
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
_total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<TestSourceChainBalance, TestError> {
Ok((Weight::from_ref_time(BASE_MESSAGE_DELIVERY_TRANSACTION_COST) *
(nonces.end() - nonces.start() + 1) +
total_dispatch_weight +
Weight::from_ref_time(total_size as u64))
.ref_time())
}
}
fn run_loop_test(
@@ -920,7 +856,6 @@ pub(crate) mod tests {
max_messages_in_single_batch: 4,
max_messages_weight_in_single_batch: Weight::from_ref_time(4),
max_messages_size_in_single_batch: 4,
relay_strategy: AltruisticStrategy,
},
},
source_client,
@@ -28,23 +28,23 @@ use crate::{
SourceClient as MessageLaneSourceClient, SourceClientState,
TargetClient as MessageLaneTargetClient, TargetClientState,
},
message_race_limits::{MessageRaceLimits, RelayMessagesBatchReference},
message_race_loop::{
MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces,
TargetClient, TargetClientNonces,
},
message_race_strategy::BasicStrategy,
metrics::MessageLaneLoopMetrics,
relay_strategy::{EnforcementStrategy, RelayMessagesBatchReference, RelayStrategy},
};
/// Run message delivery race.
pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
pub async fn run<P: MessageLane>(
source_client: impl MessageLaneSourceClient<P>,
source_state_updates: impl FusedStream<Item = SourceClientState<P>>,
target_client: impl MessageLaneTargetClient<P>,
target_state_updates: impl FusedStream<Item = TargetClientState<P>>,
metrics_msg: Option<MessageLaneLoopMetrics>,
params: MessageDeliveryParams<Strategy>,
params: MessageDeliveryParams,
) -> Result<(), FailedClient> {
crate::message_race_loop::run(
MessageDeliveryRaceSource {
@@ -59,7 +59,7 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
_phantom: Default::default(),
},
target_state_updates,
MessageDeliveryStrategy::<P, Strategy, _, _> {
MessageDeliveryStrategy::<P, _, _> {
lane_source_client: source_client,
lane_target_client: target_client,
max_unrewarded_relayer_entries_at_target: params
@@ -68,7 +68,6 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
max_messages_in_single_batch: params.max_messages_in_single_batch,
max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch,
max_messages_size_in_single_batch: params.max_messages_size_in_single_batch,
relay_strategy: params.relay_strategy,
latest_confirmed_nonces_at_source: VecDeque::new(),
target_nonces: None,
strategy: BasicStrategy::new(),
@@ -231,7 +230,7 @@ struct DeliveryRaceTargetNoncesData {
}
/// Messages delivery strategy.
struct MessageDeliveryStrategy<P: MessageLane, Strategy: RelayStrategy, SC, TC> {
struct MessageDeliveryStrategy<P: MessageLane, SC, TC> {
/// The client that is connected to the message lane source node.
lane_source_client: SC,
/// The client that is connected to the message lane target node.
@@ -246,8 +245,6 @@ struct MessageDeliveryStrategy<P: MessageLane, Strategy: RelayStrategy, SC, TC>
max_messages_weight_in_single_batch: Weight,
/// Maximal messages size in the single delivery transaction.
max_messages_size_in_single_batch: u32,
/// Relayer operating mode.
relay_strategy: Strategy,
/// Latest confirmed nonces at the source client + the header id where we have first met this
/// nonce.
latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf<P>, MessageNonce)>,
@@ -268,9 +265,7 @@ type MessageDeliveryStrategyBase<P> = BasicStrategy<
<P as MessageLane>::MessagesProof,
>;
impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> std::fmt::Debug
for MessageDeliveryStrategy<P, Strategy, SC, TC>
{
impl<P: MessageLane, SC, TC> std::fmt::Debug for MessageDeliveryStrategy<P, SC, TC> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("MessageDeliveryStrategy")
.field(
@@ -288,7 +283,7 @@ impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> std::fmt::Debug
}
}
impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> MessageDeliveryStrategy<P, Strategy, SC, TC> {
impl<P: MessageLane, SC, TC> MessageDeliveryStrategy<P, SC, TC> {
/// Returns total weight of all undelivered messages.
fn total_queued_dispatch_weight(&self) -> Weight {
self.strategy
@@ -300,9 +295,8 @@ impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> MessageDeliveryStrategy<P,
}
#[async_trait]
impl<P, Strategy: RelayStrategy, SC, TC>
RaceStrategy<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>
for MessageDeliveryStrategy<P, Strategy, SC, TC>
impl<P, SC, TC> RaceStrategy<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>
for MessageDeliveryStrategy<P, SC, TC>
where
P: MessageLane,
SC: MessageLaneSourceClient<P>,
@@ -524,8 +518,7 @@ where
metrics: self.metrics_msg.clone(),
};
let mut strategy = EnforcementStrategy::new(self.relay_strategy.clone());
let range_end = strategy.decide(reference).await?;
let range_end = MessageRaceLimits::decide(reference).await?;
let range_begin = source_queue[0].1.begin();
let selected_nonces = range_begin..=range_end;
@@ -562,38 +555,27 @@ impl<SourceChainBalance: std::fmt::Debug> NoncesRange for MessageDetailsMap<Sour
#[cfg(test)]
mod tests {
use bp_runtime::messages::DispatchFeePayment;
use crate::{
message_lane_loop::{
tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
BASE_MESSAGE_DELIVERY_TRANSACTION_COST, CONFIRMATION_TRANSACTION_COST,
},
MessageDetails, RelayerMode,
use crate::message_lane_loop::{
tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
},
relay_strategy::MixStrategy,
MessageDetails,
};
use super::*;
const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1);
const DEFAULT_SIZE: u32 = 1;
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST +
BASE_MESSAGE_DELIVERY_TRANSACTION_COST +
DEFAULT_DISPATCH_WEIGHT.ref_time() +
(DEFAULT_SIZE as TestSourceChainBalance);
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>;
type TestStrategy =
MessageDeliveryStrategy<TestMessageLane, MixStrategy, TestSourceClient, TestTargetClient>;
MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
fn source_nonces(
new_nonces: RangeInclusive<MessageNonce>,
confirmed_nonce: MessageNonce,
reward: TestSourceChainBalance,
dispatch_fee_payment: DispatchFeePayment,
) -> SourceClientNonces<MessageDetailsMap<TestSourceChainBalance>> {
SourceClientNonces {
new_nonces: new_nonces
@@ -605,7 +587,6 @@ mod tests {
dispatch_weight: DEFAULT_DISPATCH_WEIGHT,
size: DEFAULT_SIZE,
reward,
dispatch_fee_payment,
},
)
})
@@ -648,13 +629,11 @@ mod tests {
},
}),
strategy: BasicStrategy::new(),
relay_strategy: MixStrategy::new(RelayerMode::Altruistic),
};
race_strategy.strategy.source_nonces_updated(
header_id(1),
source_nonces(20..=23, 19, DEFAULT_REWARD, DispatchFeePayment::AtSourceChain),
);
race_strategy
.strategy
.source_nonces_updated(header_id(1), source_nonces(20..=23, 19, 0));
let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () };
race_strategy
@@ -687,7 +666,6 @@ mod tests {
dispatch_weight: Weight::from_ref_time(idx),
size: idx as _,
reward: idx as _,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
)
})
@@ -978,83 +956,6 @@ mod tests {
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2)));
}
#[async_std::test]
async fn rational_relayer_is_delivering_messages_if_cost_is_equal_to_reward() {
let (state, mut strategy) = prepare_strategy();
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// => strategy shall select all 20..=23
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
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,
19,
DEFAULT_REWARD - BASE_MESSAGE_DELIVERY_TRANSACTION_COST,
DispatchFeePayment::AtSourceChain,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// - 24..=25 with reward less than cost
// => strategy shall only select 20..=23
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
async fn rational_relayer_is_delivering_unpaid_messages() {
async fn test_with_dispatch_fee_payment(
dispatch_fee_payment: DispatchFeePayment,
) -> Option<(RangeInclusive<MessageNonce>, MessageProofParameters)> {
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(
24..=24,
19,
DEFAULT_REWARD - DEFAULT_DISPATCH_WEIGHT.ref_time(),
dispatch_fee_payment,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100;
strategy.max_messages_in_single_batch = 100;
strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(100);
strategy.max_messages_size_in_single_batch = 100;
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// - 24..=24 with reward less than cost, but we're deducting `DEFAULT_DISPATCH_WEIGHT`
// from the cost, so it should be fine;
// => when MSG#24 fee is paid at the target chain, strategy shall select all 20..=24
// => when MSG#25 fee is paid at the source chain, strategy shall only select 20..=23
strategy.select_nonces_to_deliver(state).await
}
assert_eq!(
test_with_dispatch_fee_payment(DispatchFeePayment::AtTargetChain).await,
Some(((20..=24), proof_parameters(false, 5)))
);
assert_eq!(
test_with_dispatch_fee_payment(DispatchFeePayment::AtSourceChain).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() {
// Real scenario that has happened on test deployments:
@@ -1066,7 +967,7 @@ mod tests {
// This was happening because selector (`select_nonces_for_delivery_transaction`) has been
// called for every `source_queue` entry separately without preserving any context.
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD, DispatchFeePayment::AtSourceChain);
let nonces = source_nonces(24..=25, 19, 0);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100;
@@ -17,43 +17,88 @@
//! enforcement strategy
use num_traits::Zero;
use std::ops::Range;
use bp_messages::{MessageNonce, Weight};
use bp_runtime::messages::DispatchFeePayment;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
MessageDetails, SourceClient as MessageLaneSourceClient,
MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
message_race_loop::NoncesRange,
relay_strategy::{RelayMessagesBatchReference, RelayReference, RelayStrategy},
message_race_strategy::SourceRangesQueue,
metrics::MessageLaneLoopMetrics,
};
/// Do hard check and run soft check strategy
/// Reference data for participating in relay
pub struct RelayReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Messages size summary
pub selected_size: u32,
/// Hard check begin nonce
pub hard_selected_begin_nonce: MessageNonce,
/// Index by all ready nonces
pub index: usize,
/// Current nonce
pub nonce: MessageNonce,
/// Current nonce details
pub details: MessageDetails<P::SourceChainBalance>,
}
/// Relay reference data
pub struct RelayMessagesBatchReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// Maximal number of relayed messages in single delivery transaction.
pub max_messages_in_this_batch: MessageNonce,
/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Source queue.
pub nonces_queue: SourceRangesQueue<
P::SourceHeaderHash,
P::SourceHeaderNumber,
MessageDetailsMap<P::SourceChainBalance>,
>,
/// Source queue range
pub nonces_queue_range: Range<usize>,
}
/// Limits of the message race transactions.
#[derive(Clone)]
pub struct EnforcementStrategy<Strategy: RelayStrategy> {
strategy: Strategy,
}
pub struct MessageRaceLimits;
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
pub fn new(strategy: Strategy) -> Self {
Self { strategy }
}
}
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
impl MessageRaceLimits {
pub async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: RelayMessagesBatchReference<P, SourceClient, TargetClient>,
) -> Option<MessageNonce> {
let mut hard_selected_count = 0;
let mut soft_selected_count = 0;
let mut selected_weight = Weight::zero();
let mut selected_count: MessageNonce = 0;
@@ -67,17 +112,9 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
lane_target_client: reference.lane_target_client.clone(),
metrics: reference.metrics.clone(),
selected_reward: P::SourceChainBalance::zero(),
selected_cost: P::SourceChainBalance::zero(),
selected_size: 0,
total_reward: P::SourceChainBalance::zero(),
total_confirmations_cost: P::SourceChainBalance::zero(),
total_cost: P::SourceChainBalance::zero(),
hard_selected_begin_nonce,
selected_prepaid_nonces: 0,
selected_unpaid_weight: Weight::zero(),
index: 0,
nonce: 0,
@@ -85,7 +122,6 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
dispatch_weight: Weight::zero(),
size: 0,
reward: P::SourceChainBalance::zero(),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
};
@@ -148,74 +184,14 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
}
relay_reference.selected_size = new_selected_size;
// If dispatch fee has been paid at the source chain, it means that it is **relayer**
// who's paying for dispatch at the target chain AND reward must cover this dispatch
// fee.
//
// If dispatch fee is paid at the target chain, it means that it'll be withdrawn from
// the dispatch origin account AND reward is not covering this fee.
//
// So in the latter case we're not adding the dispatch weight to the delivery
// transaction weight.
let mut new_selected_prepaid_nonces = relay_reference.selected_prepaid_nonces;
let new_selected_unpaid_weight = match details.dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => {
new_selected_prepaid_nonces += 1;
relay_reference.selected_unpaid_weight.saturating_add(details.dispatch_weight)
},
DispatchFeePayment::AtTargetChain => relay_reference.selected_unpaid_weight,
};
relay_reference.selected_prepaid_nonces = new_selected_prepaid_nonces;
relay_reference.selected_unpaid_weight = new_selected_unpaid_weight;
// now the message has passed all 'strong' checks, and we CAN deliver it. But do we WANT
// to deliver it? It depends on the relayer strategy.
if self.strategy.decide(&mut relay_reference).await {
soft_selected_count = index + 1;
}
hard_selected_count = index + 1;
selected_weight = new_selected_weight;
selected_count = new_selected_count;
}
if hard_selected_count != soft_selected_count {
let hard_selected_end_nonce =
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
let soft_selected_begin_nonce = hard_selected_begin_nonce;
let soft_selected_end_nonce =
soft_selected_begin_nonce + soft_selected_count as MessageNonce - 1;
log::warn!(
target: "bridge",
"Relayer may deliver nonces [{:?}; {:?}], but because of its strategy it has selected \
nonces [{:?}; {:?}].",
hard_selected_begin_nonce,
hard_selected_end_nonce,
soft_selected_begin_nonce,
soft_selected_end_nonce,
);
hard_selected_count = soft_selected_count;
}
if hard_selected_count != 0 {
if relay_reference.selected_reward != P::SourceChainBalance::zero() &&
relay_reference.selected_cost != P::SourceChainBalance::zero()
{
log::trace!(
target: "bridge",
"Expected reward from delivering nonces [{:?}; {:?}] is: {:?} - {:?} = {:?}",
hard_selected_begin_nonce,
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1,
&relay_reference.selected_reward,
&relay_reference.selected_cost,
relay_reference.selected_reward - relay_reference.selected_cost,
);
}
let selected_max_nonce =
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
self.strategy.on_final_decision(&relay_reference);
Some(selected_max_nonce)
} else {
None
+2 -21
View File
@@ -24,7 +24,7 @@ use crate::{
use bp_messages::MessageNonce;
use finality_relay::SyncLoopMetrics;
use relay_utils::metrics::{
metric_name, register, Counter, GaugeVec, Metric, Opts, PrometheusError, Registry, U64,
metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64,
};
/// Message lane relay metrics.
@@ -39,17 +39,11 @@ pub struct MessageLaneLoopMetrics {
/// Lane state nonces: "source_latest_generated", "source_latest_confirmed",
/// "target_latest_received", "target_latest_confirmed".
lane_state_nonces: GaugeVec<U64>,
/// Count of unprofitable message delivery transactions that we have submitted so far.
unprofitable_delivery_transactions: Counter<U64>,
}
impl MessageLaneLoopMetrics {
/// Create and register messages loop metrics.
pub fn new(
prefix: Option<&str>,
source_name: &str,
target_name: &str,
) -> Result<Self, PrometheusError> {
pub fn new(prefix: Option<&str>) -> Result<Self, PrometheusError> {
Ok(MessageLaneLoopMetrics {
source_to_target_finality_metrics: SyncLoopMetrics::new(
prefix,
@@ -65,13 +59,6 @@ impl MessageLaneLoopMetrics {
Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"),
&["type"],
)?,
unprofitable_delivery_transactions: Counter::new(
metric_name(prefix, "unprofitable_delivery_transactions"),
format!(
"Count of unprofitable message delivery transactions from {} to {}",
source_name, target_name
),
)?,
})
}
@@ -140,11 +127,6 @@ impl MessageLaneLoopMetrics {
.with_label_values(&["target_latest_confirmed"])
.set(target_latest_confirmed_nonce);
}
/// Note unprofitable delivery transaction.
pub fn note_unprofitable_delivery_transactions(&self) {
self.unprofitable_delivery_transactions.inc()
}
}
impl Metric for MessageLaneLoopMetrics {
@@ -152,7 +134,6 @@ impl Metric for MessageLaneLoopMetrics {
self.source_to_target_finality_metrics.register(registry)?;
self.target_to_source_finality_metrics.register(registry)?;
register(self.lane_state_nonces.clone(), registry)?;
register(self.unprofitable_delivery_transactions.clone(), registry)?;
Ok(())
}
}
@@ -1,81 +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/>.
//! Altruistic relay strategy
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
},
relay_strategy::{RelayReference, RelayStrategy},
};
/// The relayer doesn't care about rewards.
#[derive(Clone)]
pub struct AltruisticStrategy;
#[async_trait]
impl RelayStrategy for AltruisticStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
// We don't care about costs and rewards, but we want to report unprofitable transactions.
if let Err(e) = reference.update_cost_and_reward().await {
log::debug!(
target: "bridge",
"Failed to update transaction cost and reward: {:?}. \
The `unprofitable_delivery_transactions` metric will be inaccurate",
e,
);
}
true
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
) {
if let Some(ref metrics) = reference.metrics {
if !reference.is_profitable() {
log::debug!(
target: "bridge",
"The relayer has submitted unprofitable {} -> {} message delivery transaction \
with {} messages: total cost = {:?}, total reward = {:?}",
P::SOURCE_NAME,
P::TARGET_NAME,
reference.index + 1,
reference.total_cost,
reference.total_reward,
);
metrics.note_unprofitable_delivery_transactions();
}
}
}
}
@@ -1,72 +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/>.
//! Adapter for using `enum RelayerMode` in a context which requires `RelayStrategy`.
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
RelayerMode, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
relay_strategy::{AltruisticStrategy, RationalStrategy, RelayReference, RelayStrategy},
};
/// `RelayerMode` adapter.
#[derive(Clone)]
pub struct MixStrategy {
relayer_mode: RelayerMode,
}
impl MixStrategy {
/// Create mix strategy instance
pub fn new(relayer_mode: RelayerMode) -> Self {
Self { relayer_mode }
}
}
#[async_trait]
impl RelayStrategy for MixStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
match self.relayer_mode {
RelayerMode::Altruistic => AltruisticStrategy.decide(reference).await,
RelayerMode::Rational => RationalStrategy.decide(reference).await,
}
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
) {
match self.relayer_mode {
RelayerMode::Altruistic => AltruisticStrategy.on_final_decision(reference),
RelayerMode::Rational => RationalStrategy.on_final_decision(reference),
}
}
}
@@ -1,224 +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/>.
//! Relayer strategy
use async_trait::async_trait;
use bp_messages::{MessageNonce, Weight};
use sp_arithmetic::traits::Saturating;
use std::ops::Range;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
message_race_strategy::SourceRangesQueue,
metrics::MessageLaneLoopMetrics,
};
pub(crate) use self::enforcement_strategy::*;
pub use self::{altruistic_strategy::*, mix_strategy::*, rational_strategy::*};
mod altruistic_strategy;
mod enforcement_strategy;
mod mix_strategy;
mod rational_strategy;
/// Relayer strategy trait
#[async_trait]
pub trait RelayStrategy: 'static + Clone + Send + Sync {
/// The relayer decide how to process nonce by reference.
/// From given set of source nonces, that are ready to be delivered, select nonces
/// to fit into single delivery transaction.
///
/// The function returns last nonce that must be delivered to the target chain.
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool;
/// Notification that the following maximal nonce has been selected for the delivery.
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
);
}
/// Total cost of mesage delivery and confirmation.
struct MessagesDeliveryCost<SourceChainBalance> {
/// Cost of message delivery transaction.
pub delivery_transaction_cost: SourceChainBalance,
/// Cost of confirmation delivery transaction.
pub confirmation_transaction_cost: SourceChainBalance,
}
/// Reference data for participating in relay
pub struct RelayReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Current block reward summary
pub selected_reward: P::SourceChainBalance,
/// Current block cost summary
pub selected_cost: P::SourceChainBalance,
/// Messages size summary
pub selected_size: u32,
/// Current block reward summary
pub total_reward: P::SourceChainBalance,
/// All confirmations cost
pub total_confirmations_cost: P::SourceChainBalance,
/// Current block cost summary
pub total_cost: P::SourceChainBalance,
/// Hard check begin nonce
pub hard_selected_begin_nonce: MessageNonce,
/// Count prepaid nonces
pub selected_prepaid_nonces: MessageNonce,
/// Unpaid nonces weight summary
pub selected_unpaid_weight: Weight,
/// Index by all ready nonces
pub index: usize,
/// Current nonce
pub nonce: MessageNonce,
/// Current nonce details
pub details: MessageDetails<P::SourceChainBalance>,
}
impl<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> RelayReference<P, SourceClient, TargetClient>
{
/// Returns whether the current `RelayReference` is profitable.
pub fn is_profitable(&self) -> bool {
self.total_reward >= self.total_cost
}
async fn estimate_messages_delivery_cost(
&self,
) -> Result<MessagesDeliveryCost<P::SourceChainBalance>, TargetClient::Error> {
// technically, multiple confirmations will be delivered in a single transaction,
// meaning less loses for relayer. But here we don't know the final relayer yet, so
// we're adding a separate transaction for every message. Normally, this cost is covered
// by the message sender. Probably reconsider this?
let confirmation_transaction_cost =
self.lane_source_client.estimate_confirmation_transaction().await;
let delivery_transaction_cost = self
.lane_target_client
.estimate_delivery_transaction_in_source_tokens(
self.hard_selected_begin_nonce..=
(self.hard_selected_begin_nonce + self.index as MessageNonce),
self.selected_prepaid_nonces,
self.selected_unpaid_weight,
self.selected_size,
)
.await?;
Ok(MessagesDeliveryCost { confirmation_transaction_cost, delivery_transaction_cost })
}
async fn update_cost_and_reward(&mut self) -> Result<(), TargetClient::Error> {
let prev_is_profitable = self.is_profitable();
let prev_total_cost = self.total_cost;
let prev_total_reward = self.total_reward;
let MessagesDeliveryCost { confirmation_transaction_cost, delivery_transaction_cost } =
self.estimate_messages_delivery_cost().await?;
self.total_confirmations_cost =
self.total_confirmations_cost.saturating_add(confirmation_transaction_cost);
self.total_reward = self.total_reward.saturating_add(self.details.reward);
self.total_cost = self.total_confirmations_cost.saturating_add(delivery_transaction_cost);
if prev_is_profitable && !self.is_profitable() {
// if it is the first message that makes reward less than cost, let's log it
log::debug!(
target: "bridge",
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it larger than \
total reward {:?}->{:?}",
self.nonce,
self.details.reward,
prev_total_cost,
self.total_cost,
prev_total_reward,
self.total_reward,
);
} else if !prev_is_profitable && self.is_profitable() {
// if this message makes batch profitable again, let's log it
log::debug!(
target: "bridge",
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it less than or \
equal to the total reward {:?}->{:?} (again)",
self.nonce,
self.details.reward,
prev_total_cost,
self.total_cost,
prev_total_reward,
self.total_reward,
);
}
Ok(())
}
}
/// Relay reference data
pub struct RelayMessagesBatchReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// Maximal number of relayed messages in single delivery transaction.
pub max_messages_in_this_batch: MessageNonce,
/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Source queue.
pub nonces_queue: SourceRangesQueue<
P::SourceHeaderHash,
P::SourceHeaderNumber,
MessageDetailsMap<P::SourceChainBalance>,
>,
/// Source queue range
pub nonces_queue_range: Range<usize>,
}
@@ -1,75 +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/>.
//! Rational relay strategy
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
},
relay_strategy::{RelayReference, RelayStrategy},
};
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
#[derive(Clone)]
pub struct RationalStrategy;
#[async_trait]
impl RelayStrategy for RationalStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
if let Err(e) = reference.update_cost_and_reward().await {
log::debug!(
target: "bridge",
"Failed to update transaction cost and reward: {:?}. No nonces selected for delivery",
e,
);
return false
}
// Rational relayer never wants to lose his funds.
if reference.is_profitable() {
reference.selected_reward = reference.total_reward;
reference.selected_cost = reference.total_cost;
return true
}
false
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
_reference: &RelayReference<P, SourceClient, TargetClient>,
) {
// rational relayer would never submit unprofitable transactions, so we don't need to do
// anything here
}
}