diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs index e8f8fc1f7f03805ce24b8be1a9f19e937bd8b963..173a501032f6ec08d16d4aefd48089b262f58449 100644 --- a/bridges/bin/runtime-common/src/messages.rs +++ b/bridges/bin/runtime-common/src/messages.rs @@ -860,7 +860,7 @@ pub mod target { return Err(MessageProofError::Empty) } - // We only support single lane messages in this schema + // We only support single lane messages in this generated_schema let mut proved_messages = ProvedMessages::new(); proved_messages.insert(lane, proved_lane_messages); diff --git a/bridges/relays/bin-substrate/src/cli/bridge.rs b/bridges/relays/bin-substrate/src/cli/bridge.rs index 3ef324265c3c2af373d48ea0d07bfe8933d2d55f..e0d65e3caf9b1df4bb51d5a86e79001754bdad55 100644 --- a/bridges/relays/bin-substrate/src/cli/bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/bridge.rs @@ -15,10 +15,14 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use crate::cli::CliChain; -use relay_substrate_client::{AccountKeyPairOf, Chain, TransactionSignScheme}; +use messages_relay::relay_strategy::MixStrategy; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{AccountKeyPairOf, Chain, RelayChain, TransactionSignScheme}; use strum::{EnumString, EnumVariantNames}; use substrate_relay_helper::{ finality::SubstrateFinalitySyncPipeline, messages_lane::SubstrateMessageLane, + parachains::SubstrateParachainsPipeline, }; #[derive(Debug, PartialEq, Eq, EnumString, EnumVariantNames)] @@ -59,8 +63,9 @@ pub trait CliBridgeBase: Sized { + CliChain<KeyPair = AccountKeyPairOf<Self::Target>>; } -/// Bridge representation that can be used from the CLI for relaying headers. -pub trait HeadersCliBridge: CliBridgeBase { +/// Bridge representation that can be used from the CLI for relaying headers +/// from a relay chain to a relay chain. +pub trait RelayToRelayHeadersCliBridge: CliBridgeBase { /// Finality proofs synchronization pipeline. type Finality: SubstrateFinalitySyncPipeline< SourceChain = Self::Source, @@ -69,6 +74,29 @@ pub trait HeadersCliBridge: CliBridgeBase { >; } +/// Bridge representation that can be used from the CLI for relaying headers +/// from a parachain to a relay chain. +pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase { + // The `CliBridgeBase` type represents the parachain in this situation. + // We need to add an extra type for the relay chain. + type SourceRelay: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher> + + CliChain + + RelayChain; + /// Finality proofs synchronization pipeline (source parachain -> target). + type ParachainFinality: SubstrateParachainsPipeline< + SourceRelayChain = Self::SourceRelay, + SourceParachain = Self::Source, + TargetChain = Self::Target, + TransactionSignScheme = Self::Target, + > + ParachainsPipeline<SourceChain = Self::SourceRelay, TargetChain = Self::Target>; + /// Finality proofs synchronization pipeline (source relay chain -> target). + type RelayFinality: SubstrateFinalitySyncPipeline< + SourceChain = Self::SourceRelay, + TargetChain = Self::Target, + TransactionSignScheme = Self::Target, + >; +} + /// Bridge representation that can be used from the CLI for relaying messages. pub trait MessagesCliBridge: CliBridgeBase { /// Name of the runtime method used to estimate the message dispatch and delivery fee for the @@ -80,6 +108,7 @@ pub trait MessagesCliBridge: CliBridgeBase { TargetChain = Self::Target, SourceTransactionSignScheme = Self::Source, TargetTransactionSignScheme = Self::Target, + RelayStrategy = MixStrategy, >; } @@ -91,7 +120,7 @@ impl CliBridgeBase for MillauToRialtoCliBridge { type Target = relay_rialto_client::Rialto; } -impl HeadersCliBridge for MillauToRialtoCliBridge { +impl RelayToRelayHeadersCliBridge for MillauToRialtoCliBridge { type Finality = crate::chains::millau_headers_to_rialto::MillauFinalityToRialto; } @@ -109,7 +138,7 @@ impl CliBridgeBase for RialtoToMillauCliBridge { type Target = relay_millau_client::Millau; } -impl HeadersCliBridge for RialtoToMillauCliBridge { +impl RelayToRelayHeadersCliBridge for RialtoToMillauCliBridge { type Finality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau; } @@ -127,7 +156,7 @@ impl CliBridgeBase for WestendToMillauCliBridge { type Target = relay_millau_client::Millau; } -impl HeadersCliBridge for WestendToMillauCliBridge { +impl RelayToRelayHeadersCliBridge for WestendToMillauCliBridge { type Finality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau; } @@ -139,7 +168,7 @@ impl CliBridgeBase for MillauToRialtoParachainCliBridge { type Target = relay_rialto_parachain_client::RialtoParachain; } -impl HeadersCliBridge for MillauToRialtoParachainCliBridge { +impl RelayToRelayHeadersCliBridge for MillauToRialtoParachainCliBridge { type Finality = crate::chains::millau_headers_to_rialto_parachain::MillauFinalityToRialtoParachain; } @@ -159,6 +188,12 @@ impl CliBridgeBase for RialtoParachainToMillauCliBridge { type Target = relay_millau_client::Millau; } +impl ParachainToRelayHeadersCliBridge for RialtoParachainToMillauCliBridge { + type SourceRelay = relay_rialto_client::Rialto; + type ParachainFinality = crate::chains::rialto_parachains_to_millau::RialtoParachainsToMillau; + type RelayFinality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau; +} + impl MessagesCliBridge for RialtoParachainToMillauCliBridge { const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD; @@ -169,6 +204,12 @@ impl MessagesCliBridge for RialtoParachainToMillauCliBridge { //// `WestendParachain` to `Millau` bridge definition. pub struct WestmintToMillauCliBridge {} +impl ParachainToRelayHeadersCliBridge for WestmintToMillauCliBridge { + type SourceRelay = relay_westend_client::Westend; + type ParachainFinality = crate::chains::westend_parachains_to_millau::WestendParachainsToMillau; + type RelayFinality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau; +} + impl CliBridgeBase for WestmintToMillauCliBridge { type Source = relay_westend_client::Westmint; type Target = relay_millau_client::Millau; diff --git a/bridges/relays/bin-substrate/src/cli/chain_schema.rs b/bridges/relays/bin-substrate/src/cli/chain_schema.rs new file mode 100644 index 0000000000000000000000000000000000000000..f6edd26db7a672ceb0cc0ecb0388d1f62d081ae2 --- /dev/null +++ b/bridges/relays/bin-substrate/src/cli/chain_schema.rs @@ -0,0 +1,409 @@ +// Copyright 2019-2022 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 sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames}; + +use crate::cli::CliChain; +pub use relay_substrate_client::ChainRuntimeVersion; +use substrate_relay_helper::TransactionParams; + +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_runtime_version_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + paste::item! { + #[doc = $chain " runtime version params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] + pub struct [<$chain RuntimeVersionParams>] { + #[doc = "The type of runtime version for chain " $chain] + #[structopt(long, default_value = "Bundle")] + pub [<$chain_prefix _version_mode>]: RuntimeVersionType, + #[doc = "The custom sepc_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _spec_version>]: Option<u32>, + #[doc = "The custom transaction_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _transaction_version>]: Option<u32>, + } + + impl [<$chain RuntimeVersionParams>] { + /// Converts self into `ChainRuntimeVersion`. + pub fn into_runtime_version( + self, + bundle_runtime_version: Option<sp_version::RuntimeVersion>, + ) -> anyhow::Result<ChainRuntimeVersion> { + Ok(match self.[<$chain_prefix _version_mode>] { + RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, + RuntimeVersionType::Custom => { + let except_spec_version = self.[<$chain_prefix _spec_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; + let except_transaction_version = self.[<$chain_prefix _transaction_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; + ChainRuntimeVersion::Custom( + except_spec_version, + except_transaction_version + ) + }, + RuntimeVersionType::Bundle => match bundle_runtime_version { + Some(runtime_version) => ChainRuntimeVersion::Custom( + runtime_version.spec_version, + runtime_version.transaction_version + ), + None => ChainRuntimeVersion::Auto + }, + }) + } + } + } + }; +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_connection_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + paste::item! { + #[doc = $chain " connection params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain ConnectionParams>] { + #[doc = "Connect to " $chain " node at given host."] + #[structopt(long, default_value = "127.0.0.1")] + pub [<$chain_prefix _host>]: String, + #[doc = "Connect to " $chain " node websocket server at given port."] + #[structopt(long, default_value = "9944")] + pub [<$chain_prefix _port>]: u16, + #[doc = "Use secure websocket connection."] + #[structopt(long)] + pub [<$chain_prefix _secure>]: bool, + #[doc = "Custom runtime version"] + #[structopt(flatten)] + pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>], + } + + impl [<$chain ConnectionParams>] { + /// Convert connection params into Substrate client. + #[allow(dead_code)] + pub async fn into_client<Chain: CliChain>( + self, + ) -> anyhow::Result<relay_substrate_client::Client<Chain>> { + let chain_runtime_version = self + .[<$chain_prefix _runtime_version>] + .into_runtime_version(Some(Chain::RUNTIME_VERSION))?; + Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { + host: self.[<$chain_prefix _host>], + port: self.[<$chain_prefix _port>], + secure: self.[<$chain_prefix _secure>], + chain_runtime_version, + }) + .await + ) + } + } + } + }; +} + +/// Helper trait to override transaction parameters differently. +pub trait TransactionParamsProvider { + /// Returns `true` if transaction parameters are defined by this provider. + fn is_defined(&self) -> bool; + /// Returns transaction parameters. + fn transaction_params<Chain: CliChain>( + &self, + ) -> anyhow::Result<TransactionParams<Chain::KeyPair>>; + + /// Returns transaction parameters, defined by `self` provider or, if they're not defined, + /// defined by `other` provider. + fn transaction_params_or<Chain: CliChain, T: TransactionParamsProvider>( + &self, + other: &T, + ) -> anyhow::Result<TransactionParams<Chain::KeyPair>> { + if self.is_defined() { + self.transaction_params::<Chain>() + } else { + other.transaction_params::<Chain>() + } + } +} + +/// Create chain-specific set of signing parameters. +#[macro_export] +macro_rules! declare_chain_signing_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + paste::item! { + #[doc = $chain " signing params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain SigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer>]: Option<String>, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer_password>]: Option<String>, + + #[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."] + #[structopt(long)] + pub [<$chain_prefix _signer_file>]: Option<std::path::PathBuf>, + #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] + #[structopt(long)] + pub [<$chain_prefix _signer_password_file>]: Option<std::path::PathBuf>, + + #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] + #[structopt(long)] + pub [<$chain_prefix _transactions_mortality>]: Option<u32>, + } + + impl [<$chain SigningParams>] { + /// Return transactions mortality. + #[allow(dead_code)] + pub fn transactions_mortality(&self) -> anyhow::Result<Option<u32>> { + self.[<$chain_prefix _transactions_mortality>] + .map(|transactions_mortality| { + if !(4..=65536).contains(&transactions_mortality) + || !transactions_mortality.is_power_of_two() + { + Err(anyhow::format_err!( + "Transactions mortality {} is not a power of two in a [4; 65536] range", + transactions_mortality, + )) + } else { + Ok(transactions_mortality) + } + }) + .transpose() + } + + /// Parse signing params into chain-specific KeyPair. + #[allow(dead_code)] + pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Chain::KeyPair> { + let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { + (Some(suri), _) => suri.to_owned(), + (None, Some(suri_file)) => std::fs::read_to_string(suri_file) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI from file {:?}: {}", + suri_file, + err, + ))?, + (None, None) => return Err(anyhow::format_err!( + "One of options must be specified: '{}' or '{}'", + stringify!([<$chain_prefix _signer>]), + stringify!([<$chain_prefix _signer_file>]), + )), + }; + + let suri_password = match ( + self.[<$chain_prefix _signer_password>].as_ref(), + self.[<$chain_prefix _signer_password_file>].as_ref(), + ) { + (Some(suri_password), _) => Some(suri_password.to_owned()), + (None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file) + .map(Some) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI password from file {:?}: {}", + suri_password_file, + err, + ))?, + _ => None, + }; + + use sp_core::crypto::Pair; + + Chain::KeyPair::from_string( + &suri, + suri_password.as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)) + } + } + + #[allow(dead_code)] + impl TransactionParamsProvider for [<$chain SigningParams>] { + fn is_defined(&self) -> bool { + self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some() + } + + fn transaction_params<Chain: CliChain>(&self) -> anyhow::Result<TransactionParams<Chain::KeyPair>> { + Ok(TransactionParams { + mortality: self.transactions_mortality()?, + signer: self.to_keypair::<Chain>()?, + }) + } + } + } + }; +} + +/// Create chain-specific set of messages pallet owner signing parameters. +#[macro_export] +macro_rules! declare_chain_messages_pallet_owner_signing_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + paste::item! { + #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] + #[derive(StructOpt, Debug, PartialEq, Eq)] + pub struct [<$chain MessagesPalletOwnerSigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner>]: Option<String>, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner_password>]: Option<String>, + } + + #[allow(dead_code)] + impl [<$chain MessagesPalletOwnerSigningParams>] { + /// Parse signing params into chain-specific KeyPair. + pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Option<Chain::KeyPair>> { + let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] { + Some(ref messages_pallet_owner) => messages_pallet_owner, + None => return Ok(None), + }; + Chain::KeyPair::from_string( + [<$chain_prefix _messages_pallet_owner>], + self.[<$chain_prefix _messages_pallet_owner_password>].as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some) + } + } + } + }; +} + +/// Create chain-specific set of configuration objects: connection parameters, +/// signing parameters and bridge initialization parameters. +#[macro_export] +macro_rules! declare_chain_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + $crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_messages_pallet_owner_signing_params_cli_schema!( + $chain, + $chain_prefix + ); + }; +} + +declare_chain_cli_schema!(Source, source); +declare_chain_cli_schema!(Target, target); +declare_chain_cli_schema!(Relaychain, relaychain); +declare_chain_cli_schema!(Parachain, parachain); + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::Pair; + + #[test] + fn reads_suri_from_file() { + const ALICE: &str = "//Alice"; + const BOB: &str = "//Bob"; + const ALICE_PASSWORD: &str = "alice_password"; + const BOB_PASSWORD: &str = "bob_password"; + + let alice: sp_core::sr25519::Pair = Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap(); + let bob: sp_core::sr25519::Pair = Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap(); + let bob_with_alice_password = + sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap(); + + let temp_dir = tempfile::tempdir().unwrap(); + let mut suri_file_path = temp_dir.path().to_path_buf(); + let mut password_file_path = temp_dir.path().to_path_buf(); + suri_file_path.push("suri"); + password_file_path.push("password"); + std::fs::write(&suri_file_path, BOB.as_bytes()).unwrap(); + std::fs::write(&password_file_path, BOB_PASSWORD.as_bytes()).unwrap(); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: None, + target_signer_password_file: None, + + target_transactions_mortality: None, + } + .to_keypair::<relay_rialto_client::Rialto>() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: None, + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::<relay_rialto_client::Rialto>() + .map(|p| p.public()) + .map_err(drop), + Ok(bob.public()), + ); + + // when password are is overriden by cli option + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::<relay_rialto_client::Rialto>() + .map(|p| p.public()) + .map_err(drop), + Ok(bob_with_alice_password.public()), + ); + + // when both seed and password are overriden by cli options + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path), + target_signer_password_file: Some(password_file_path), + + target_transactions_mortality: None, + } + .to_keypair::<relay_rialto_client::Rialto>() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + } +} diff --git a/bridges/relays/bin-substrate/src/cli/estimate_fee.rs b/bridges/relays/bin-substrate/src/cli/estimate_fee.rs index 30f20b681d4a22430bbdda5b4e0cb15ce21ea26a..0f4fd765f4074a7ed24d4c83b24af292a7118aec 100644 --- a/bridges/relays/bin-substrate/src/cli/estimate_fee.rs +++ b/bridges/relays/bin-substrate/src/cli/estimate_fee.rs @@ -16,8 +16,9 @@ use crate::cli::{ bridge::{FullBridge, MessagesCliBridge, *}, + chain_schema::*, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - Balance, HexBytes, HexLaneId, SourceConnectionParams, + Balance, HexBytes, HexLaneId, }; use async_trait::async_trait; use bp_runtime::BalanceOf; @@ -80,7 +81,7 @@ where <Self::Source as ChainBase>::Balance: Display + Into<u128>, { async fn estimate_fee(data: EstimateFee) -> anyhow::Result<()> { - let source_client = data.source.to_client::<Self::Source>().await?; + 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) @@ -239,7 +240,6 @@ async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode> #[cfg(test)] mod tests { use super::*; - use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams}; #[test] fn should_parse_cli_options() { diff --git a/bridges/relays/bin-substrate/src/cli/init_bridge.rs b/bridges/relays/bin-substrate/src/cli/init_bridge.rs index b3110e69ff3495a2c8f0fdfe40a88587220af373..98f30b9f7bdfc57e781506dcc8714ed10c14a55e 100644 --- a/bridges/relays/bin-substrate/src/cli/init_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/init_bridge.rs @@ -21,7 +21,7 @@ use crate::cli::{ CliBridgeBase, MillauToRialtoCliBridge, MillauToRialtoParachainCliBridge, RialtoToMillauCliBridge, WestendToMillauCliBridge, }, - SourceConnectionParams, TargetConnectionParams, TargetSigningParams, + chain_schema::*, }; use bp_runtime::Chain as ChainBase; use codec::Encode; @@ -71,8 +71,8 @@ where /// Initialize the bridge. async fn init_bridge(data: InitBridge) -> anyhow::Result<()> { - let source_client = data.source.to_client::<Self::Source>().await?; - let target_client = data.target.to_client::<Self::Target>().await?; + let source_client = data.source.into_client::<Self::Source>().await?; + let target_client = data.target.into_client::<Self::Target>().await?; let target_sign = data.target_sign.to_keypair::<Self::Target>()?; let (spec_version, transaction_version) = target_client.simple_runtime_version().await?; diff --git a/bridges/relays/bin-substrate/src/cli/mod.rs b/bridges/relays/bin-substrate/src/cli/mod.rs index 6f394092e14ad6afcbf51c346805398f1c765c43..e35548c347c4247762aacfe9369af49dec875684 100644 --- a/bridges/relays/bin-substrate/src/cli/mod.rs +++ b/bridges/relays/bin-substrate/src/cli/mod.rs @@ -19,10 +19,8 @@ use std::convert::TryInto; use codec::{Decode, Encode}; -use relay_substrate_client::ChainRuntimeVersion; use structopt::{clap::arg_enum, StructOpt}; use strum::{EnumString, EnumVariantNames}; -use substrate_relay_helper::TransactionParams; use bp_messages::LaneId; @@ -31,6 +29,7 @@ pub(crate) mod encode_message; pub(crate) mod estimate_fee; pub(crate) mod send_message; +mod chain_schema; mod init_bridge; mod register_parachain; mod relay_headers; @@ -296,283 +295,9 @@ pub enum RuntimeVersionType { Bundle, } -/// Helper trait to override transaction parameters differently. -pub trait TransactionParamsProvider { - /// Returns `true` if transaction parameters are defined by this provider. - fn is_defined(&self) -> bool; - /// Returns transaction parameters. - fn transaction_params<Chain: CliChain>( - &self, - ) -> anyhow::Result<TransactionParams<Chain::KeyPair>>; - - /// Returns transaction parameters, defined by `self` provider or, if they're not defined, - /// defined by `other` provider. - fn transaction_params_or<Chain: CliChain, T: TransactionParamsProvider>( - &self, - other: &T, - ) -> anyhow::Result<TransactionParams<Chain::KeyPair>> { - if self.is_defined() { - self.transaction_params::<Chain>() - } else { - other.transaction_params::<Chain>() - } - } -} - -/// Create chain-specific set of configuration objects: connection parameters, -/// signing parameters and bridge initialization parameters. -#[macro_export] -macro_rules! declare_chain_options { - ($chain:ident, $chain_prefix:ident) => { - paste::item! { - #[doc = $chain " connection params."] - #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] - pub struct [<$chain ConnectionParams>] { - #[doc = "Connect to " $chain " node at given host."] - #[structopt(long, default_value = "127.0.0.1")] - pub [<$chain_prefix _host>]: String, - #[doc = "Connect to " $chain " node websocket server at given port."] - #[structopt(long, default_value = "9944")] - pub [<$chain_prefix _port>]: u16, - #[doc = "Use secure websocket connection."] - #[structopt(long)] - pub [<$chain_prefix _secure>]: bool, - #[doc = "Custom runtime version"] - #[structopt(flatten)] - pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>], - } - - #[doc = $chain " runtime version params."] - #[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] - pub struct [<$chain RuntimeVersionParams>] { - #[doc = "The type of runtime version for chain " $chain] - #[structopt(long, default_value = "Bundle")] - pub [<$chain_prefix _version_mode>]: RuntimeVersionType, - #[doc = "The custom sepc_version for chain " $chain] - #[structopt(long)] - pub [<$chain_prefix _spec_version>]: Option<u32>, - #[doc = "The custom transaction_version for chain " $chain] - #[structopt(long)] - pub [<$chain_prefix _transaction_version>]: Option<u32>, - } - - #[doc = $chain " signing params."] - #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] - pub struct [<$chain SigningParams>] { - #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _signer>]: Option<String>, - #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _signer_password>]: Option<String>, - - #[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."] - #[structopt(long)] - pub [<$chain_prefix _signer_file>]: Option<std::path::PathBuf>, - #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] - #[structopt(long)] - pub [<$chain_prefix _signer_password_file>]: Option<std::path::PathBuf>, - - #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] - #[structopt(long)] - pub [<$chain_prefix _transactions_mortality>]: Option<u32>, - } - - #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] - #[derive(StructOpt, Debug, PartialEq, Eq)] - pub struct [<$chain MessagesPalletOwnerSigningParams>] { - #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _messages_pallet_owner>]: Option<String>, - #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _messages_pallet_owner_password>]: Option<String>, - } - - impl [<$chain SigningParams>] { - /// Return transactions mortality. - #[allow(dead_code)] - pub fn transactions_mortality(&self) -> anyhow::Result<Option<u32>> { - self.[<$chain_prefix _transactions_mortality>] - .map(|transactions_mortality| { - if !(4..=65536).contains(&transactions_mortality) - || !transactions_mortality.is_power_of_two() - { - Err(anyhow::format_err!( - "Transactions mortality {} is not a power of two in a [4; 65536] range", - transactions_mortality, - )) - } else { - Ok(transactions_mortality) - } - }) - .transpose() - } - - /// Parse signing params into chain-specific KeyPair. - #[allow(dead_code)] - pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Chain::KeyPair> { - let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { - (Some(suri), _) => suri.to_owned(), - (None, Some(suri_file)) => std::fs::read_to_string(suri_file) - .map_err(|err| anyhow::format_err!( - "Failed to read SURI from file {:?}: {}", - suri_file, - err, - ))?, - (None, None) => return Err(anyhow::format_err!( - "One of options must be specified: '{}' or '{}'", - stringify!([<$chain_prefix _signer>]), - stringify!([<$chain_prefix _signer_file>]), - )), - }; - - let suri_password = match ( - self.[<$chain_prefix _signer_password>].as_ref(), - self.[<$chain_prefix _signer_password_file>].as_ref(), - ) { - (Some(suri_password), _) => Some(suri_password.to_owned()), - (None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file) - .map(Some) - .map_err(|err| anyhow::format_err!( - "Failed to read SURI password from file {:?}: {}", - suri_password_file, - err, - ))?, - _ => None, - }; - - use sp_core::crypto::Pair; - - Chain::KeyPair::from_string( - &suri, - suri_password.as_deref() - ).map_err(|e| anyhow::format_err!("{:?}", e)) - } - } - - #[allow(dead_code)] - impl TransactionParamsProvider for [<$chain SigningParams>] { - fn is_defined(&self) -> bool { - self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some() - } - - fn transaction_params<Chain: CliChain>(&self) -> anyhow::Result<TransactionParams<Chain::KeyPair>> { - Ok(TransactionParams { - mortality: self.transactions_mortality()?, - signer: self.to_keypair::<Chain>()?, - }) - } - } - - #[allow(dead_code)] - impl [<$chain MessagesPalletOwnerSigningParams>] { - /// Parse signing params into chain-specific KeyPair. - pub fn to_keypair<Chain: CliChain>(&self) -> anyhow::Result<Option<Chain::KeyPair>> { - use sp_core::crypto::Pair; - - let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] { - Some(ref messages_pallet_owner) => messages_pallet_owner, - None => return Ok(None), - }; - Chain::KeyPair::from_string( - [<$chain_prefix _messages_pallet_owner>], - self.[<$chain_prefix _messages_pallet_owner_password>].as_deref() - ).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some) - } - } - - impl [<$chain ConnectionParams>] { - /// Returns `true` if version guard can be started. - /// - /// There's no reason to run version guard when version mode is set to `Auto`. It can - /// lead to relay shutdown when chain is upgraded, even though we have explicitly - /// said that we don't want to shutdown. - #[allow(dead_code)] - pub fn can_start_version_guard(&self) -> bool { - self.[<$chain_prefix _runtime_version>].[<$chain_prefix _version_mode>] != RuntimeVersionType::Auto - } - - /// Convert connection params into Substrate client. - pub async fn to_client<Chain: CliChain>( - &self, - ) -> anyhow::Result<relay_substrate_client::Client<Chain>> { - let chain_runtime_version = self - .[<$chain_prefix _runtime_version>] - .into_runtime_version(Some(Chain::RUNTIME_VERSION))?; - Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { - host: self.[<$chain_prefix _host>].clone(), - port: self.[<$chain_prefix _port>], - secure: self.[<$chain_prefix _secure>], - chain_runtime_version, - }) - .await - ) - } - - /// Return selected `chain_spec` version. - /// - /// This function only connects to the node if version mode is set to `Auto`. - #[allow(dead_code)] - pub async fn selected_chain_spec_version<Chain: CliChain>( - &self, - ) -> anyhow::Result<u32> { - let chain_runtime_version = self - .[<$chain_prefix _runtime_version>] - .into_runtime_version(Some(Chain::RUNTIME_VERSION))?; - Ok(match chain_runtime_version { - ChainRuntimeVersion::Auto => self - .to_client::<Chain>() - .await? - .simple_runtime_version() - .await? - .0, - ChainRuntimeVersion::Custom(spec_version, _) => spec_version, - }) - } - } - - impl [<$chain RuntimeVersionParams>] { - /// Converts self into `ChainRuntimeVersion`. - pub fn into_runtime_version( - self, - bundle_runtime_version: Option<sp_version::RuntimeVersion>, - ) -> anyhow::Result<ChainRuntimeVersion> { - Ok(match self.[<$chain_prefix _version_mode>] { - RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, - RuntimeVersionType::Custom => { - let except_spec_version = self.[<$chain_prefix _spec_version>] - .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; - let except_transaction_version = self.[<$chain_prefix _transaction_version>] - .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; - ChainRuntimeVersion::Custom( - except_spec_version, - except_transaction_version - ) - }, - RuntimeVersionType::Bundle => match bundle_runtime_version { - Some(runtime_version) => ChainRuntimeVersion::Custom( - runtime_version.spec_version, - runtime_version.transaction_version - ), - None => ChainRuntimeVersion::Auto - }, - }) - } - } - } - }; -} - -declare_chain_options!(Source, source); -declare_chain_options!(Target, target); -declare_chain_options!(Relaychain, relaychain); -declare_chain_options!(Parachain, parachain); - #[cfg(test)] mod tests { use super::*; - use sp_core::Pair; #[test] fn hex_bytes_display_matches_from_str_for_clap() { @@ -586,93 +311,4 @@ mod tests { // then assert_eq!(hex.0, hex2.0); } - - #[test] - fn reads_suri_from_file() { - const ALICE: &str = "//Alice"; - const BOB: &str = "//Bob"; - const ALICE_PASSWORD: &str = "alice_password"; - const BOB_PASSWORD: &str = "bob_password"; - - let alice = sp_core::sr25519::Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap(); - let bob = sp_core::sr25519::Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap(); - let bob_with_alice_password = - sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap(); - - let temp_dir = tempfile::tempdir().unwrap(); - let mut suri_file_path = temp_dir.path().to_path_buf(); - let mut password_file_path = temp_dir.path().to_path_buf(); - suri_file_path.push("suri"); - password_file_path.push("password"); - std::fs::write(&suri_file_path, BOB.as_bytes()).unwrap(); - std::fs::write(&password_file_path, BOB_PASSWORD.as_bytes()).unwrap(); - - // when both seed and password are read from file - assert_eq!( - TargetSigningParams { - target_signer: Some(ALICE.into()), - target_signer_password: Some(ALICE_PASSWORD.into()), - - target_signer_file: None, - target_signer_password_file: None, - - target_transactions_mortality: None, - } - .to_keypair::<relay_rialto_client::Rialto>() - .map(|p| p.public()) - .map_err(drop), - Ok(alice.public()), - ); - - // when both seed and password are read from file - assert_eq!( - TargetSigningParams { - target_signer: None, - target_signer_password: None, - - target_signer_file: Some(suri_file_path.clone()), - target_signer_password_file: Some(password_file_path.clone()), - - target_transactions_mortality: None, - } - .to_keypair::<relay_rialto_client::Rialto>() - .map(|p| p.public()) - .map_err(drop), - Ok(bob.public()), - ); - - // when password are is overriden by cli option - assert_eq!( - TargetSigningParams { - target_signer: None, - target_signer_password: Some(ALICE_PASSWORD.into()), - - target_signer_file: Some(suri_file_path.clone()), - target_signer_password_file: Some(password_file_path.clone()), - - target_transactions_mortality: None, - } - .to_keypair::<relay_rialto_client::Rialto>() - .map(|p| p.public()) - .map_err(drop), - Ok(bob_with_alice_password.public()), - ); - - // when both seed and password are overriden by cli options - assert_eq!( - TargetSigningParams { - target_signer: Some(ALICE.into()), - target_signer_password: Some(ALICE_PASSWORD.into()), - - target_signer_file: Some(suri_file_path), - target_signer_password_file: Some(password_file_path), - - target_transactions_mortality: None, - } - .to_keypair::<relay_rialto_client::Rialto>() - .map(|p| p.public()) - .map_err(drop), - Ok(alice.public()), - ); - } } diff --git a/bridges/relays/bin-substrate/src/cli/register_parachain.rs b/bridges/relays/bin-substrate/src/cli/register_parachain.rs index d1647812053786234e71f268162f3d5ea932d67f..9573cde30319f5135bfbefd8969374b2c588917c 100644 --- a/bridges/relays/bin-substrate/src/cli/register_parachain.rs +++ b/bridges/relays/bin-substrate/src/cli/register_parachain.rs @@ -14,9 +14,7 @@ // 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::cli::{ - Balance, ParachainConnectionParams, RelaychainConnectionParams, RelaychainSigningParams, -}; +use crate::cli::{chain_schema::*, Balance}; use codec::Encode; use frame_support::Twox64Concat; @@ -94,9 +92,9 @@ impl RegisterParachain { /// Run the command. pub async fn run(self) -> anyhow::Result<()> { select_bridge!(self.parachain, { - let relay_client = self.relay_connection.to_client::<Relaychain>().await?; + let relay_client = self.relay_connection.into_client::<Relaychain>().await?; let relay_sign = self.relay_sign.to_keypair::<Relaychain>()?; - let para_client = self.para_connection.to_client::<Parachain>().await?; + let para_client = self.para_connection.into_client::<Parachain>().await?; // hopefully we're the only actor that is registering parachain right now // => read next parachain id @@ -343,9 +341,6 @@ async fn wait_para_state<Relaychain: Chain>( #[cfg(test)] mod tests { use super::*; - use crate::cli::{ - ParachainRuntimeVersionParams, RelaychainRuntimeVersionParams, RuntimeVersionType, - }; #[test] fn register_rialto_parachain() { diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/bridges/relays/bin-substrate/src/cli/relay_headers.rs index f8d7eeb541830e97bc579d16d58d6df462472541..59325e452e370dbc29b6dbaf8edc650bbfb08da3 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -15,7 +15,7 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use async_trait::async_trait; -use relay_substrate_client::{AccountKeyPairOf, ChainBase}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf}; use sp_core::Pair; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; @@ -23,10 +23,7 @@ use strum::{EnumString, EnumVariantNames, VariantNames}; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline; -use crate::cli::{ - bridge::*, PrometheusParams, SourceConnectionParams, TargetConnectionParams, - TargetSigningParams, -}; +use crate::cli::{bridge::*, chain_schema::*, PrometheusParams}; /// Start headers relayer process. #[derive(StructOpt)] @@ -59,14 +56,14 @@ pub enum RelayHeadersBridge { } #[async_trait] -trait HeadersRelayer: HeadersCliBridge +trait HeadersRelayer: RelayToRelayHeadersCliBridge where - <Self::Target as ChainBase>::AccountId: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>, + AccountIdOf<Self::Target>: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>, { /// Relay headers. async fn relay_headers(data: RelayHeaders) -> anyhow::Result<()> { - let source_client = data.source.to_client::<Self::Source>().await?; - let target_client = data.target.to_client::<Self::Target>().await?; + let source_client = data.source.into_client::<Self::Source>().await?; + let target_client = data.target.into_client::<Self::Target>().await?; let target_transactions_mortality = data.target_sign.target_transactions_mortality; let target_sign = data.target_sign.to_keypair::<Self::Target>()?; @@ -80,7 +77,7 @@ where Self::Finality::start_relay_guards( &target_client, &target_transactions_params, - data.target.can_start_version_guard(), + target_client.can_start_version_guard(), ) .await?; diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs deleted file mode 100644 index 4d6456cc6198e944e5d993502ffe9888d7625a20..0000000000000000000000000000000000000000 --- a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ /dev/null @@ -1,910 +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/>. - -//! Complex headers+messages relays support. -//! -//! To add new complex relay between `ChainA` and `ChainB`, you must: -//! -//! 1) ensure that there's a `declare_chain_options!(...)` for both chains; -//! 2) add `declare_bridge_options!(...)` for the bridge; -//! 3) add bridge support to the `select_bridge! { ... }` macro. - -use futures::{FutureExt, TryFutureExt}; -use structopt::StructOpt; -use strum::VariantNames; - -use async_std::sync::Arc; -use bp_polkadot_core::parachains::ParaHash; -use messages_relay::relay_strategy::MixStrategy; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, ChainRuntimeVersion, Client, - TransactionSignScheme, -}; -use relay_utils::metrics::MetricsParams; -use sp_core::Pair; -use substrate_relay_helper::{ - finality::SubstrateFinalitySyncPipeline, - messages_lane::MessagesRelayParams, - on_demand::{ - headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, - }, - parachains::SubstrateParachainsPipeline, - TaggedAccount, TransactionParams, -}; - -use crate::{ - cli::{ - relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams, RuntimeVersionType, - TransactionParamsProvider, - }, - declare_chain_options, -}; - -/// 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; - -/// Start headers+messages relayer process. -#[derive(Debug, PartialEq, StructOpt)] -pub enum RelayHeadersAndMessages { - MillauRialto(MillauRialtoHeadersAndMessages), - MillauRialtoParachain(MillauRialtoParachainHeadersAndMessages), -} - -/// 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")] - lane: Vec<HexLaneId>, - #[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")] - relayer_mode: RelayerMode, - /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) - /// are relayed. - #[structopt(long)] - only_mandatory_headers: bool, - #[structopt(flatten)] - prometheus_params: PrometheusParams, -} - -// The reason behind this macro is that 'normal' relays are using source and target chains -// terminology, which is unusable for both-way relays (if you're relaying headers from Rialto to -// Millau and from Millau to Rialto, then which chain is source?). -macro_rules! declare_bridge_options { - // chain, parachain, relay-chain-of-parachain - ($chain1:ident, $chain2:ident, $chain3:ident) => { - paste::item! { - #[doc = $chain1 ", " $chain2 " and " $chain3 " headers+parachains+messages relay params."] - #[derive(Debug, PartialEq, StructOpt)] - pub struct [<$chain1 $chain2 HeadersAndMessages>] { - #[structopt(flatten)] - shared: HeadersAndMessagesSharedParams, - #[structopt(flatten)] - left: [<$chain1 ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the left chain - #[structopt(flatten)] - left_sign: [<$chain1 SigningParams>], - // override for right_relay->left headers signer - #[structopt(flatten)] - right_relay_headers_to_left_sign_override: [<$chain3 HeadersTo $chain1 SigningParams>], - // override for right->left parachains signer - #[structopt(flatten)] - right_parachains_to_left_sign_override: [<$chain3 ParachainsTo $chain1 SigningParams>], - #[structopt(flatten)] - left_messages_pallet_owner: [<$chain1 MessagesPalletOwnerSigningParams>], - #[structopt(flatten)] - right: [<$chain2 ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the right chain - #[structopt(flatten)] - right_sign: [<$chain2 SigningParams>], - // override for left->right headers signer - #[structopt(flatten)] - left_headers_to_right_sign_override: [<$chain1 HeadersTo $chain2 SigningParams>], - #[structopt(flatten)] - right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>], - #[structopt(flatten)] - right_relay: [<$chain3 ConnectionParams>], - } - } - - declare_bridge_options!({ implement }, $chain1, $chain2); - }; - ($chain1:ident, $chain2:ident) => { - paste::item! { - #[doc = $chain1 " and " $chain2 " headers+messages relay params."] - #[derive(Debug, PartialEq, StructOpt)] - pub struct [<$chain1 $chain2 HeadersAndMessages>] { - #[structopt(flatten)] - shared: HeadersAndMessagesSharedParams, - // default signer, which is always used to sign messages relay transactions on the left chain - #[structopt(flatten)] - left: [<$chain1 ConnectionParams>], - // override for right->left headers signer - #[structopt(flatten)] - right_headers_to_left_sign_override: [<$chain2 HeadersTo $chain1 SigningParams>], - #[structopt(flatten)] - left_sign: [<$chain1 SigningParams>], - #[structopt(flatten)] - left_messages_pallet_owner: [<$chain1 MessagesPalletOwnerSigningParams>], - // default signer, which is always used to sign messages relay transactions on the right chain - #[structopt(flatten)] - right: [<$chain2 ConnectionParams>], - // override for left->right headers signer - #[structopt(flatten)] - left_headers_to_right_sign_override: [<$chain1 HeadersTo $chain2 SigningParams>], - #[structopt(flatten)] - right_sign: [<$chain2 SigningParams>], - #[structopt(flatten)] - right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>], - } - } - - declare_bridge_options!({ implement }, $chain1, $chain2); - }; - ({ implement }, $chain1:ident, $chain2:ident) => { - paste::item! { - impl From<RelayHeadersAndMessages> for [<$chain1 $chain2 HeadersAndMessages>] { - fn from(relay_params: RelayHeadersAndMessages) -> [<$chain1 $chain2 HeadersAndMessages>] { - match relay_params { - RelayHeadersAndMessages::[<$chain1 $chain2>](params) => params, - _ => unreachable!(), - } - } - } - } - }; -} - -macro_rules! select_bridge { - ($bridge: expr, $generic: tt) => { - match $bridge { - RelayHeadersAndMessages::MillauRialto(_) => { - type Params = MillauRialtoHeadersAndMessages; - - type Left = relay_millau_client::Millau; - type Right = relay_rialto_client::Rialto; - - use crate::chains::{ - millau_messages_to_rialto::MillauMessagesToRialto as LeftToRightMessageLane, - rialto_messages_to_millau::RialtoMessagesToMillau as RightToLeftMessageLane, - }; - - async fn start_on_demand_relays( - params: &Params, - left_client: Client<Left>, - right_client: Client<Right>, - at_left_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<Left>>>, - at_right_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<Right>>>, - ) -> anyhow::Result<( - Arc<dyn OnDemandRelay<BlockNumberOf<Left>>>, - Arc<dyn OnDemandRelay<BlockNumberOf<Right>>>, - )> { - start_on_demand_relay_to_relay::< - Left, - Right, - crate::chains::millau_headers_to_rialto::MillauFinalityToRialto, - crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau, - >( - left_client, - right_client, - params.left_headers_to_right_sign_override.transaction_params_or::<Right, _>(¶ms.right_sign)?, - params.right_headers_to_left_sign_override.transaction_params_or::<Left, _>(¶ms.left_sign)?, - params.shared.only_mandatory_headers, - params.shared.only_mandatory_headers, - params.left.can_start_version_guard(), - params.right.can_start_version_guard(), - at_left_relay_accounts, - at_right_relay_accounts, - ).await - } - - $generic - }, - RelayHeadersAndMessages::MillauRialtoParachain(_) => { - type Params = MillauRialtoParachainHeadersAndMessages; - - type Left = relay_millau_client::Millau; - type Right = relay_rialto_parachain_client::RialtoParachain; - - use crate::chains::{ - millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain as LeftToRightMessageLane, - rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau as RightToLeftMessageLane, - }; - - async fn start_on_demand_relays( - params: &Params, - left_client: Client<Left>, - right_client: Client<Right>, - at_left_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<Left>>>, - at_right_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<Right>>>, - ) -> anyhow::Result<( - Arc<dyn OnDemandRelay<BlockNumberOf<Left>>>, - Arc<dyn OnDemandRelay<BlockNumberOf<Right>>>, - )> { - type RightRelayChain = relay_rialto_client::Rialto; - let rialto_relay_chain_client = params.right_relay.to_client::<RightRelayChain>().await?; - - start_on_demand_relay_to_parachain::< - Left, - Right, - RightRelayChain, - crate::chains::millau_headers_to_rialto_parachain::MillauFinalityToRialtoParachain, - crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau, - crate::chains::rialto_parachains_to_millau::RialtoParachainsToMillau, - >( - left_client, - right_client, - rialto_relay_chain_client, - params.left_headers_to_right_sign_override.transaction_params_or::<Right, _>(¶ms.right_sign)?, - params.right_relay_headers_to_left_sign_override.transaction_params_or::<Left, _>(¶ms.left_sign)?, - params.right_parachains_to_left_sign_override.transaction_params_or::<Left, _>(¶ms.left_sign)?, - params.shared.only_mandatory_headers, - params.shared.only_mandatory_headers, - params.left.can_start_version_guard(), - params.right.can_start_version_guard(), - at_left_relay_accounts, - at_right_relay_accounts, - ).await - } - - $generic - }, - } - }; -} - -// All supported chains. -declare_chain_options!(Millau, millau); -declare_chain_options!(Rialto, rialto); -declare_chain_options!(RialtoParachain, rialto_parachain); -// Means to override signers of different layer transactions. -declare_chain_options!(MillauHeadersToRialto, millau_headers_to_rialto); -declare_chain_options!(MillauHeadersToRialtoParachain, millau_headers_to_rialto_parachain); -declare_chain_options!(RialtoHeadersToMillau, rialto_headers_to_millau); -declare_chain_options!(RialtoParachainsToMillau, rialto_parachains_to_millau); -// All supported bridges. -declare_bridge_options!(Millau, Rialto); -declare_bridge_options!(Millau, RialtoParachain, Rialto); - -impl RelayHeadersAndMessages { - /// Run the command. - pub async fn run(self) -> anyhow::Result<()> { - select_bridge!(self, { - let params: Params = self.into(); - - let left_client = params.left.to_client::<Left>().await?; - let left_transactions_mortality = params.left_sign.transactions_mortality()?; - let left_sign = params.left_sign.to_keypair::<Left>()?; - let left_messages_pallet_owner = - params.left_messages_pallet_owner.to_keypair::<Left>()?; - let right_client = params.right.to_client::<Right>().await?; - let right_transactions_mortality = params.right_sign.transactions_mortality()?; - let right_sign = params.right_sign.to_keypair::<Right>()?; - let right_messages_pallet_owner = - params.right_messages_pallet_owner.to_keypair::<Right>()?; - - let lanes = params.shared.lane.clone(); - let relayer_mode = params.shared.relayer_mode.into(); - let relay_strategy = MixStrategy::new(relayer_mode); - - // create metrics registry and register standalone metrics - let metrics_params: MetricsParams = 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::< - LeftToRightMessageLane, - >(left_client.clone(), right_client.clone())?; - let right_to_left_metrics = left_to_right_metrics.clone().reverse(); - let mut at_left_relay_accounts = vec![TaggedAccount::Messages { - id: left_sign.public().into(), - bridged_chain: Right::NAME.to_string(), - }]; - let mut at_right_relay_accounts = vec![TaggedAccount::Messages { - id: right_sign.public().into(), - bridged_chain: Left::NAME.to_string(), - }]; - - // start conversion rate update loops for left/right chains - if let Some(left_messages_pallet_owner) = left_messages_pallet_owner.clone() { - let left_client = left_client.clone(); - let format_err = || { - anyhow::format_err!( - "Cannon run conversion rate updater: {} -> {}", - Right::NAME, - Left::NAME - ) - }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< - LeftToRightMessageLane, - Left, - >( - left_client, - TransactionParams { - signer: left_messages_pallet_owner.clone(), - mortality: left_transactions_mortality, - }, - left_to_right_metrics - .target_to_source_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - left_to_right_metrics - .target_to_base_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - left_to_right_metrics - .source_to_base_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - ); - at_left_relay_accounts.push(TaggedAccount::MessagesPalletOwner { - id: left_messages_pallet_owner.public().into(), - bridged_chain: Right::NAME.to_string(), - }); - } - if let Some(right_messages_pallet_owner) = right_messages_pallet_owner.clone() { - let right_client = right_client.clone(); - let format_err = || { - anyhow::format_err!( - "Cannon run conversion rate updater: {} -> {}", - Left::NAME, - Right::NAME - ) - }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< - RightToLeftMessageLane, - Right, - >( - right_client, - TransactionParams { - signer: right_messages_pallet_owner.clone(), - mortality: right_transactions_mortality, - }, - right_to_left_metrics - .target_to_source_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - right_to_left_metrics - .target_to_base_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - right_to_left_metrics - .source_to_base_conversion_rate - .as_ref() - .ok_or_else(format_err)? - .shared_value_ref(), - CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - ); - at_right_relay_accounts.push(TaggedAccount::MessagesPalletOwner { - id: right_messages_pallet_owner.public().into(), - bridged_chain: Left::NAME.to_string(), - }); - } - - // start on-demand header relays - let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) = - start_on_demand_relays( - ¶ms, - left_client.clone(), - right_client.clone(), - &mut at_left_relay_accounts, - &mut at_right_relay_accounts, - ) - .await?; - - // add balance-related metrics - let metrics_params = - substrate_relay_helper::messages_metrics::add_relay_balances_metrics( - left_client.clone(), - metrics_params, - at_left_relay_accounts, - ) - .await?; - let metrics_params = - substrate_relay_helper::messages_metrics::add_relay_balances_metrics( - right_client.clone(), - metrics_params, - at_right_relay_accounts, - ) - .await?; - - // Need 2x capacity since we consider both directions for each lane - let mut message_relays = Vec::with_capacity(lanes.len() * 2); - for lane in lanes { - let lane = lane.into(); - let left_to_right_messages = substrate_relay_helper::messages_lane::run::< - LeftToRightMessageLane, - >(MessagesRelayParams { - source_client: left_client.clone(), - source_transaction_params: TransactionParams { - signer: left_sign.clone(), - mortality: left_transactions_mortality, - }, - target_client: right_client.clone(), - target_transaction_params: TransactionParams { - signer: right_sign.clone(), - mortality: right_transactions_mortality, - }, - source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()), - target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()), - lane_id: lane, - metrics_params: metrics_params.clone().disable(), - standalone_metrics: Some(left_to_right_metrics.clone()), - relay_strategy: relay_strategy.clone(), - }) - .map_err(|e| anyhow::format_err!("{}", e)) - .boxed(); - let right_to_left_messages = substrate_relay_helper::messages_lane::run::< - RightToLeftMessageLane, - >(MessagesRelayParams { - source_client: right_client.clone(), - source_transaction_params: TransactionParams { - signer: right_sign.clone(), - mortality: right_transactions_mortality, - }, - target_client: left_client.clone(), - target_transaction_params: TransactionParams { - signer: left_sign.clone(), - mortality: left_transactions_mortality, - }, - source_to_target_headers_relay: Some(right_to_left_on_demand_headers.clone()), - target_to_source_headers_relay: Some(left_to_right_on_demand_headers.clone()), - lane_id: lane, - metrics_params: metrics_params.clone().disable(), - standalone_metrics: Some(right_to_left_metrics.clone()), - relay_strategy: relay_strategy.clone(), - }) - .map_err(|e| anyhow::format_err!("{}", e)) - .boxed(); - - message_relays.push(left_to_right_messages); - message_relays.push(right_to_left_messages); - } - - relay_utils::relay_metrics(metrics_params) - .expose() - .await - .map_err(|e| anyhow::format_err!("{}", e))?; - - futures::future::select_all(message_relays).await.0 - }) - } -} - -/// Start bidirectional on-demand headers <> headers relay. -#[allow(clippy::too_many_arguments)] // TODO: https://github.com/paritytech/parity-bridges-common/issues/1415 -async fn start_on_demand_relay_to_relay<LC, RC, LR, RL>( - left_client: Client<LC>, - right_client: Client<RC>, - left_to_right_transaction_params: TransactionParams<AccountKeyPairOf<RC>>, - right_to_left_transaction_params: TransactionParams<AccountKeyPairOf<LC>>, - left_to_right_only_mandatory_headers: bool, - right_to_left_only_mandatory_headers: bool, - left_can_start_version_guard: bool, - right_can_start_version_guard: bool, - at_left_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<LC>>>, - at_right_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<RC>>>, -) -> anyhow::Result<( - Arc<dyn OnDemandRelay<BlockNumberOf<LC>>>, - Arc<dyn OnDemandRelay<BlockNumberOf<RC>>>, -)> -where - LC: Chain + TransactionSignScheme<Chain = LC> + CliChain<KeyPair = AccountKeyPairOf<LC>>, - RC: Chain + TransactionSignScheme<Chain = RC> + CliChain<KeyPair = AccountKeyPairOf<RC>>, - LR: SubstrateFinalitySyncPipeline< - SourceChain = LC, - TargetChain = RC, - TransactionSignScheme = RC, - >, - RL: SubstrateFinalitySyncPipeline< - SourceChain = RC, - TargetChain = LC, - TransactionSignScheme = LC, - >, - AccountIdOf<LC>: From<<<LC as TransactionSignScheme>::AccountKeyPair as Pair>::Public>, - AccountIdOf<RC>: From<<<RC as TransactionSignScheme>::AccountKeyPair as Pair>::Public>, -{ - at_left_relay_accounts.push(TaggedAccount::Headers { - id: right_to_left_transaction_params.signer.public().into(), - bridged_chain: RC::NAME.to_string(), - }); - at_right_relay_accounts.push(TaggedAccount::Headers { - id: left_to_right_transaction_params.signer.public().into(), - bridged_chain: LC::NAME.to_string(), - }); - - LR::start_relay_guards( - &right_client, - &left_to_right_transaction_params, - right_can_start_version_guard, - ) - .await?; - RL::start_relay_guards( - &left_client, - &right_to_left_transaction_params, - left_can_start_version_guard, - ) - .await?; - let left_to_right_on_demand_headers = OnDemandHeadersRelay::new::<LR>( - left_client.clone(), - right_client.clone(), - left_to_right_transaction_params, - left_to_right_only_mandatory_headers, - ); - let right_to_left_on_demand_headers = OnDemandHeadersRelay::new::<RL>( - right_client.clone(), - left_client.clone(), - right_to_left_transaction_params, - right_to_left_only_mandatory_headers, - ); - - Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) -} - -/// Start bidirectional on-demand headers <> parachains relay. -#[allow(clippy::too_many_arguments)] // TODO: https://github.com/paritytech/parity-bridges-common/issues/1415 -async fn start_on_demand_relay_to_parachain<LC, RC, RRC, LR, RRF, RL>( - left_client: Client<LC>, - right_client: Client<RC>, - right_relay_client: Client<RRC>, - left_headers_to_right_transaction_params: TransactionParams<AccountKeyPairOf<RC>>, - right_headers_to_left_transaction_params: TransactionParams<AccountKeyPairOf<LC>>, - right_parachains_to_left_transaction_params: TransactionParams<AccountKeyPairOf<LC>>, - left_to_right_only_mandatory_headers: bool, - right_to_left_only_mandatory_headers: bool, - left_can_start_version_guard: bool, - right_can_start_version_guard: bool, - at_left_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<LC>>>, - at_right_relay_accounts: &mut Vec<TaggedAccount<AccountIdOf<RC>>>, -) -> anyhow::Result<( - Arc<dyn OnDemandRelay<BlockNumberOf<LC>>>, - Arc<dyn OnDemandRelay<BlockNumberOf<RC>>>, -)> -where - LC: Chain + TransactionSignScheme<Chain = LC> + CliChain<KeyPair = AccountKeyPairOf<LC>>, - RC: Chain<Hash = ParaHash> - + TransactionSignScheme<Chain = RC> - + CliChain<KeyPair = AccountKeyPairOf<RC>>, - RRC: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher> - + TransactionSignScheme<Chain = RRC> - + CliChain<KeyPair = AccountKeyPairOf<RRC>>, - LR: SubstrateFinalitySyncPipeline< - SourceChain = LC, - TargetChain = RC, - TransactionSignScheme = RC, - >, - RRF: SubstrateFinalitySyncPipeline< - SourceChain = RRC, - TargetChain = LC, - TransactionSignScheme = LC, - >, - RL: SubstrateParachainsPipeline< - SourceRelayChain = RRC, - SourceParachain = RC, - TargetChain = LC, - TransactionSignScheme = LC, - >, - AccountIdOf<LC>: From<<<LC as TransactionSignScheme>::AccountKeyPair as Pair>::Public>, - AccountIdOf<RC>: From<<<RC as TransactionSignScheme>::AccountKeyPair as Pair>::Public>, -{ - at_left_relay_accounts.push(TaggedAccount::Headers { - id: right_headers_to_left_transaction_params.signer.public().into(), - bridged_chain: RRC::NAME.to_string(), - }); - at_left_relay_accounts.push(TaggedAccount::Parachains { - id: right_parachains_to_left_transaction_params.signer.public().into(), - bridged_chain: RRC::NAME.to_string(), - }); - at_right_relay_accounts.push(TaggedAccount::Headers { - id: left_headers_to_right_transaction_params.signer.public().into(), - bridged_chain: LC::NAME.to_string(), - }); - - LR::start_relay_guards( - &right_client, - &left_headers_to_right_transaction_params, - right_can_start_version_guard, - ) - .await?; - RRF::start_relay_guards( - &left_client, - &right_headers_to_left_transaction_params, - left_can_start_version_guard, - ) - .await?; - let left_to_right_on_demand_headers = OnDemandHeadersRelay::new::<LR>( - left_client.clone(), - right_client, - left_headers_to_right_transaction_params, - left_to_right_only_mandatory_headers, - ); - let right_relay_to_left_on_demand_headers = OnDemandHeadersRelay::new::<RRF>( - right_relay_client.clone(), - left_client.clone(), - right_headers_to_left_transaction_params, - right_to_left_only_mandatory_headers, - ); - let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::<RL>( - right_relay_client, - left_client, - right_parachains_to_left_transaction_params, - Arc::new(right_relay_to_left_on_demand_headers), - ); - - Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_parachains))) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_parse_relay_to_relay_options() { - // when - let res = RelayHeadersAndMessages::from_iter(vec![ - "relay-headers-and-messages", - "millau-rialto", - "--millau-host", - "millau-node-alice", - "--millau-port", - "9944", - "--millau-signer", - "//Charlie", - "--millau-messages-pallet-owner", - "//Rialto.MessagesOwner", - "--millau-transactions-mortality", - "64", - "--rialto-host", - "rialto-node-alice", - "--rialto-port", - "9944", - "--rialto-signer", - "//Charlie", - "--rialto-messages-pallet-owner", - "//Millau.MessagesOwner", - "--rialto-transactions-mortality", - "64", - "--lane", - "00000000", - "--lane", - "73776170", - "--prometheus-host", - "0.0.0.0", - ]); - - // then - assert_eq!( - res, - RelayHeadersAndMessages::MillauRialto(MillauRialtoHeadersAndMessages { - shared: HeadersAndMessagesSharedParams { - lane: vec![ - HexLaneId([0x00, 0x00, 0x00, 0x00]), - HexLaneId([0x73, 0x77, 0x61, 0x70]) - ], - relayer_mode: RelayerMode::Rational, - only_mandatory_headers: false, - prometheus_params: PrometheusParams { - no_prometheus: false, - prometheus_host: "0.0.0.0".into(), - prometheus_port: 9616, - }, - }, - left: MillauConnectionParams { - millau_host: "millau-node-alice".into(), - millau_port: 9944, - millau_secure: false, - millau_runtime_version: MillauRuntimeVersionParams { - millau_version_mode: RuntimeVersionType::Bundle, - millau_spec_version: None, - millau_transaction_version: None, - }, - }, - left_sign: MillauSigningParams { - millau_signer: Some("//Charlie".into()), - millau_signer_password: None, - millau_signer_file: None, - millau_signer_password_file: None, - millau_transactions_mortality: Some(64), - }, - left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { - millau_messages_pallet_owner: Some("//Rialto.MessagesOwner".into()), - millau_messages_pallet_owner_password: None, - }, - left_headers_to_right_sign_override: MillauHeadersToRialtoSigningParams { - millau_headers_to_rialto_signer: None, - millau_headers_to_rialto_signer_password: None, - millau_headers_to_rialto_signer_file: None, - millau_headers_to_rialto_signer_password_file: None, - millau_headers_to_rialto_transactions_mortality: None, - }, - right: RialtoConnectionParams { - rialto_host: "rialto-node-alice".into(), - rialto_port: 9944, - rialto_secure: false, - rialto_runtime_version: RialtoRuntimeVersionParams { - rialto_version_mode: RuntimeVersionType::Bundle, - rialto_spec_version: None, - rialto_transaction_version: None, - }, - }, - right_sign: RialtoSigningParams { - rialto_signer: Some("//Charlie".into()), - rialto_signer_password: None, - rialto_signer_file: None, - rialto_signer_password_file: None, - rialto_transactions_mortality: Some(64), - }, - right_messages_pallet_owner: RialtoMessagesPalletOwnerSigningParams { - rialto_messages_pallet_owner: Some("//Millau.MessagesOwner".into()), - rialto_messages_pallet_owner_password: None, - }, - right_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { - rialto_headers_to_millau_signer: None, - rialto_headers_to_millau_signer_password: None, - rialto_headers_to_millau_signer_file: None, - rialto_headers_to_millau_signer_password_file: None, - rialto_headers_to_millau_transactions_mortality: None, - }, - }), - ); - } - - #[test] - fn should_parse_relay_to_parachain_options() { - // when - let res = RelayHeadersAndMessages::from_iter(vec![ - "relay-headers-and-messages", - "millau-rialto-parachain", - "--millau-host", - "millau-node-alice", - "--millau-port", - "9944", - "--millau-signer", - "//Iden", - "--rialto-headers-to-millau-signer", - "//Ken", - "--millau-messages-pallet-owner", - "//RialtoParachain.MessagesOwner", - "--millau-transactions-mortality", - "64", - "--rialto-parachain-host", - "rialto-parachain-collator-charlie", - "--rialto-parachain-port", - "9944", - "--rialto-parachain-signer", - "//George", - "--rialto-parachain-messages-pallet-owner", - "//Millau.MessagesOwner", - "--rialto-parachain-transactions-mortality", - "64", - "--rialto-host", - "rialto-node-alice", - "--rialto-port", - "9944", - "--lane", - "00000000", - "--prometheus-host", - "0.0.0.0", - ]); - - // then - assert_eq!( - res, - RelayHeadersAndMessages::MillauRialtoParachain( - MillauRialtoParachainHeadersAndMessages { - shared: HeadersAndMessagesSharedParams { - lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], - relayer_mode: RelayerMode::Rational, - only_mandatory_headers: false, - prometheus_params: PrometheusParams { - no_prometheus: false, - prometheus_host: "0.0.0.0".into(), - prometheus_port: 9616, - }, - }, - left: MillauConnectionParams { - millau_host: "millau-node-alice".into(), - millau_port: 9944, - millau_secure: false, - millau_runtime_version: MillauRuntimeVersionParams { - millau_version_mode: RuntimeVersionType::Bundle, - millau_spec_version: None, - millau_transaction_version: None, - }, - }, - left_sign: MillauSigningParams { - millau_signer: Some("//Iden".into()), - millau_signer_password: None, - millau_signer_file: None, - millau_signer_password_file: None, - millau_transactions_mortality: Some(64), - }, - left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { - millau_messages_pallet_owner: Some( - "//RialtoParachain.MessagesOwner".into() - ), - millau_messages_pallet_owner_password: None, - }, - left_headers_to_right_sign_override: - MillauHeadersToRialtoParachainSigningParams { - millau_headers_to_rialto_parachain_signer: None, - millau_headers_to_rialto_parachain_signer_password: None, - millau_headers_to_rialto_parachain_signer_file: None, - millau_headers_to_rialto_parachain_signer_password_file: None, - millau_headers_to_rialto_parachain_transactions_mortality: None, - }, - right: RialtoParachainConnectionParams { - rialto_parachain_host: "rialto-parachain-collator-charlie".into(), - rialto_parachain_port: 9944, - rialto_parachain_secure: false, - rialto_parachain_runtime_version: RialtoParachainRuntimeVersionParams { - rialto_parachain_version_mode: RuntimeVersionType::Bundle, - rialto_parachain_spec_version: None, - rialto_parachain_transaction_version: None, - }, - }, - right_sign: RialtoParachainSigningParams { - rialto_parachain_signer: Some("//George".into()), - rialto_parachain_signer_password: None, - rialto_parachain_signer_file: None, - rialto_parachain_signer_password_file: None, - rialto_parachain_transactions_mortality: Some(64), - }, - right_messages_pallet_owner: RialtoParachainMessagesPalletOwnerSigningParams { - rialto_parachain_messages_pallet_owner: Some( - "//Millau.MessagesOwner".into() - ), - rialto_parachain_messages_pallet_owner_password: None, - }, - right_relay_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { - rialto_headers_to_millau_signer: Some("//Ken".into()), - rialto_headers_to_millau_signer_password: None, - rialto_headers_to_millau_signer_file: None, - rialto_headers_to_millau_signer_password_file: None, - rialto_headers_to_millau_transactions_mortality: None, - }, - right_parachains_to_left_sign_override: RialtoParachainsToMillauSigningParams { - rialto_parachains_to_millau_signer: None, - rialto_parachains_to_millau_signer_password: None, - rialto_parachains_to_millau_signer_file: None, - rialto_parachains_to_millau_signer_password_file: None, - rialto_parachains_to_millau_transactions_mortality: None, - }, - right_relay: RialtoConnectionParams { - rialto_host: "rialto-node-alice".into(), - rialto_port: 9944, - rialto_secure: false, - rialto_runtime_version: RialtoRuntimeVersionParams { - rialto_version_mode: RuntimeVersionType::Bundle, - rialto_spec_version: None, - rialto_transaction_version: None, - }, - }, - } - ), - ); - } -} diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..82f55a6d6c7aafd59090f0ae87fbcd1b909f8a11 --- /dev/null +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -0,0 +1,694 @@ +// Copyright 2019-2022 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/>. + +//! Complex 2-ways headers+messages relays support. +//! +//! To add new complex relay between `ChainA` and `ChainB`, you must: +//! +//! 1) ensure that there's a `declare_chain_cli_schema!(...)` for both chains. +//! 2) add `declare_chain_to_chain_bridge_schema!(...)` or +//! `declare_chain_to_parachain_bridge_schema` for the bridge. +//! 3) declare a new struct for the added bridge and implement the `Full2WayBridge` trait for it. + +#[macro_use] +mod relay_to_relay; +#[macro_use] +mod relay_to_parachain; + +use async_trait::async_trait; +use std::sync::Arc; +use structopt::StructOpt; +use strum::VariantNames; + +use futures::{FutureExt, TryFutureExt}; +use relay_to_parachain::*; +use relay_to_relay::*; + +use crate::{ + cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, MillauToRialtoCliBridge, + MillauToRialtoParachainCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, RialtoParachainToMillauCliBridge, + RialtoToMillauCliBridge, + }, + chain_schema::*, + relay_messages::RelayerMode, + CliChain, HexLaneId, PrometheusParams, + }, + declare_chain_cli_schema, +}; +use bp_runtime::{BalanceOf, BlockNumberOf}; +use messages_relay::relay_strategy::MixStrategy; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, Client, TransactionSignScheme, +}; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use substrate_relay_helper::{ + 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)] + pub only_mandatory_headers: bool, + #[structopt(flatten)] + pub prometheus_params: PrometheusParams, +} + +pub struct Full2WayBridgeCommonParams< + Left: TransactionSignScheme + CliChain, + Right: TransactionSignScheme + CliChain, +> { + pub shared: HeadersAndMessagesSharedParams, + + pub left: Client<Left>, + // default signer, which is always used to sign messages relay transactions on the left chain + pub left_sign: AccountKeyPairOf<Left>, + pub left_transactions_mortality: Option<u32>, + pub left_messages_pallet_owner: Option<AccountKeyPairOf<Left>>, + pub at_left_accounts: Vec<TaggedAccount<AccountIdOf<Left>>>, + + pub right: Client<Right>, + // default signer, which is always used to sign messages relay transactions on the right chain + pub right_sign: AccountKeyPairOf<Right>, + pub right_transactions_mortality: Option<u32>, + pub right_messages_pallet_owner: Option<AccountKeyPairOf<Right>>, + pub at_right_accounts: Vec<TaggedAccount<AccountIdOf<Right>>>, +} + +// All supported chains. +declare_chain_cli_schema!(Millau, millau); +declare_chain_cli_schema!(Rialto, rialto); +declare_chain_cli_schema!(RialtoParachain, rialto_parachain); +// Means to override signers of different layer transactions. +declare_chain_cli_schema!(MillauHeadersToRialto, millau_headers_to_rialto); +declare_chain_cli_schema!(MillauHeadersToRialtoParachain, millau_headers_to_rialto_parachain); +declare_chain_cli_schema!(RialtoHeadersToMillau, rialto_headers_to_millau); +declare_chain_cli_schema!(RialtoParachainsToMillau, rialto_parachains_to_millau); +// All supported bridges. +declare_relay_to_relay_bridge_schema!(Millau, Rialto); +declare_relay_to_parachain_bridge_schema!(Millau, RialtoParachain, Rialto); + +#[async_trait] +trait Full2WayBridgeBase: Sized + Send + Sync { + /// The CLI params for the bridge. + type Params; + /// The left relay chain. + type Left: TransactionSignScheme<Chain = Self::Left> + + CliChain<KeyPair = AccountKeyPairOf<Self::Left>>; + /// The right destination chain (it can be a relay or a parachain). + type Right: TransactionSignScheme<Chain = Self::Right> + + CliChain<KeyPair = AccountKeyPairOf<Self::Right>>; + + fn common(&self) -> &Full2WayBridgeCommonParams<Self::Left, Self::Right>; + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right>; + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Left>>>, + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Right>>>, + )>; +} + +#[async_trait] +trait Full2WayBridge: Sized + Sync +where + AccountIdOf<Self::Left>: From<<AccountKeyPairOf<Self::Left> as Pair>::Public>, + AccountIdOf<Self::Right>: From<<AccountKeyPairOf<Self::Right> as Pair>::Public>, + BalanceOf<Self::Left>: TryFrom<BalanceOf<Self::Right>> + Into<u128>, + BalanceOf<Self::Right>: TryFrom<BalanceOf<Self::Left>> + Into<u128>, +{ + type Base: Full2WayBridgeBase<Left = Self::Left, Right = Self::Right>; + + /// The left relay chain. + type Left: Chain + + ChainWithBalances + + TransactionSignScheme<Chain = Self::Left> + + CliChain<KeyPair = AccountKeyPairOf<Self::Left>>; + /// The right relay chain. + type Right: Chain + + ChainWithBalances + + TransactionSignScheme<Chain = Self::Right> + + CliChain<KeyPair = AccountKeyPairOf<Self::Right>>; + + // Left to Right bridge + type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right>; + // Right to Left bridge + type R2L: MessagesCliBridge<Source = Self::Right, Target = Self::Left>; + + fn new(params: <Self::Base as Full2WayBridgeBase>::Params) -> anyhow::Result<Self>; + + fn base(&self) -> &Self::Base; + + fn mut_base(&mut self) -> &mut Self::Base; + + async fn run(&mut self) -> anyhow::Result<()> { + let left_client = self.base().common().left.clone(); + let left_transactions_mortality = self.base().common().left_transactions_mortality; + let left_sign = self.base().common().left_sign.clone(); + let left_messages_pallet_owner = self.base().common().left_messages_pallet_owner.clone(); + let right_client = self.base().common().right.clone(); + let right_transactions_mortality = self.base().common().right_transactions_mortality; + let right_sign = self.base().common().right_sign.clone(); + let right_messages_pallet_owner = self.base().common().right_messages_pallet_owner.clone(); + + let lanes = self.base().common().shared.lane.clone(); + let relayer_mode = self.base().common().shared.relayer_mode.into(); + let relay_strategy = MixStrategy::new(relayer_mode); + + // create metrics registry and register standalone metrics + let metrics_params: MetricsParams = + self.base().common().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::< + <Self::L2R as MessagesCliBridge>::MessagesLane, + >(left_client.clone(), right_client.clone())?; + let right_to_left_metrics = left_to_right_metrics.clone().reverse(); + self.mut_base().mut_common().at_left_accounts.push(TaggedAccount::Messages { + id: left_sign.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + self.mut_base().mut_common().at_right_accounts.push(TaggedAccount::Messages { + id: right_sign.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + + // start conversion rate update loops for left/right chains + if let Some(left_messages_pallet_owner) = left_messages_pallet_owner.clone() { + let left_client = left_client.clone(); + let format_err = || { + anyhow::format_err!( + "Cannon run conversion rate updater: {} -> {}", + Self::Right::NAME, + Self::Left::NAME + ) + }; + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + <Self::L2R as MessagesCliBridge>::MessagesLane, + Self::Left, + >( + left_client, + TransactionParams { + signer: left_messages_pallet_owner.clone(), + mortality: left_transactions_mortality, + }, + left_to_right_metrics + .target_to_source_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + left_to_right_metrics + .target_to_base_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + left_to_right_metrics + .source_to_base_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, + ); + self.mut_base().mut_common().at_left_accounts.push( + TaggedAccount::MessagesPalletOwner { + id: left_messages_pallet_owner.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }, + ); + } + if let Some(right_messages_pallet_owner) = right_messages_pallet_owner.clone() { + let right_client = right_client.clone(); + let format_err = || { + anyhow::format_err!( + "Cannon run conversion rate updater: {} -> {}", + Self::Left::NAME, + Self::Right::NAME + ) + }; + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + <Self::R2L as MessagesCliBridge>::MessagesLane, + Self::Right, + >( + right_client, + TransactionParams { + signer: right_messages_pallet_owner.clone(), + mortality: right_transactions_mortality, + }, + right_to_left_metrics + .target_to_source_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + right_to_left_metrics + .target_to_base_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + right_to_left_metrics + .source_to_base_conversion_rate + .as_ref() + .ok_or_else(format_err)? + .shared_value_ref(), + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, + ); + self.mut_base().mut_common().at_right_accounts.push( + TaggedAccount::MessagesPalletOwner { + id: right_messages_pallet_owner.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }, + ); + } + + // 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?; + + // add balance-related metrics + let metrics_params = substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + left_client.clone(), + metrics_params, + &self.base().common().at_left_accounts, + ) + .await?; + let metrics_params = substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + right_client.clone(), + metrics_params, + &self.base().common().at_right_accounts, + ) + .await?; + + // Need 2x capacity since we consider both directions for each lane + let mut message_relays = Vec::with_capacity(lanes.len() * 2); + for lane in lanes { + let lane = lane.into(); + let left_to_right_messages = substrate_relay_helper::messages_lane::run::< + <Self::L2R as MessagesCliBridge>::MessagesLane, + >(MessagesRelayParams { + source_client: left_client.clone(), + source_transaction_params: TransactionParams { + signer: left_sign.clone(), + mortality: left_transactions_mortality, + }, + target_client: right_client.clone(), + target_transaction_params: TransactionParams { + signer: right_sign.clone(), + mortality: right_transactions_mortality, + }, + source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()), + target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()), + lane_id: lane, + metrics_params: metrics_params.clone().disable(), + standalone_metrics: Some(left_to_right_metrics.clone()), + relay_strategy: relay_strategy.clone(), + }) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + let right_to_left_messages = substrate_relay_helper::messages_lane::run::< + <Self::R2L as MessagesCliBridge>::MessagesLane, + >(MessagesRelayParams { + source_client: right_client.clone(), + source_transaction_params: TransactionParams { + signer: right_sign.clone(), + mortality: right_transactions_mortality, + }, + target_client: left_client.clone(), + target_transaction_params: TransactionParams { + signer: left_sign.clone(), + mortality: left_transactions_mortality, + }, + source_to_target_headers_relay: Some(right_to_left_on_demand_headers.clone()), + target_to_source_headers_relay: Some(left_to_right_on_demand_headers.clone()), + lane_id: lane, + metrics_params: metrics_params.clone().disable(), + standalone_metrics: Some(right_to_left_metrics.clone()), + relay_strategy: relay_strategy.clone(), + }) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + + message_relays.push(left_to_right_messages); + message_relays.push(right_to_left_messages); + } + + relay_utils::relay_metrics(metrics_params) + .expose() + .await + .map_err(|e| anyhow::format_err!("{}", e))?; + + futures::future::select_all(message_relays).await.0 + } +} + +pub struct MillauRialtoFull2WayBridge { + base: <Self as Full2WayBridge>::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoFull2WayBridge { + type Base = RelayToRelayBridge<Self::L2R, Self::R2L>; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_client::Rialto; + type L2R = MillauToRialtoCliBridge; + type R2L = RialtoToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result<Self> { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +pub struct MillauRialtoParachainFull2WayBridge { + base: <Self as Full2WayBridge>::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoParachainFull2WayBridge { + type Base = RelayToParachainBridge<Self::L2R, Self::R2L>; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_parachain_client::RialtoParachain; + type L2R = MillauToRialtoParachainCliBridge; + type R2L = RialtoParachainToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result<Self> { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// Start headers+messages relayer process. +#[derive(Debug, PartialEq, StructOpt)] +pub enum RelayHeadersAndMessages { + MillauRialto(MillauRialtoHeadersAndMessages), + MillauRialtoParachain(MillauRialtoParachainHeadersAndMessages), +} + +impl RelayHeadersAndMessages { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self { + RelayHeadersAndMessages::MillauRialto(params) => + MillauRialtoFull2WayBridge::new(params.into_bridge().await?)?.run().await, + RelayHeadersAndMessages::MillauRialtoParachain(params) => + MillauRialtoParachainFull2WayBridge::new(params.into_bridge().await?)? + .run() + .await, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_relay_to_relay_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Charlie", + "--millau-messages-pallet-owner", + "//RialtoMessagesOwner", + "--millau-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--rialto-signer", + "//Charlie", + "--rialto-messages-pallet-owner", + "//MillauMessagesOwner", + "--rialto-transactions-mortality", + "64", + "--lane", + "00000000", + "--lane", + "73776170", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialto(MillauRialtoHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![ + HexLaneId([0x00, 0x00, 0x00, 0x00]), + HexLaneId([0x73, 0x77, 0x61, 0x70]) + ], + relayer_mode: RelayerMode::Rational, + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Charlie".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { + millau_messages_pallet_owner: Some("//RialtoMessagesOwner".into()), + millau_messages_pallet_owner_password: None, + }, + left_headers_to_right_sign_override: MillauHeadersToRialtoSigningParams { + millau_headers_to_rialto_signer: None, + millau_headers_to_rialto_signer_password: None, + millau_headers_to_rialto_signer_file: None, + millau_headers_to_rialto_signer_password_file: None, + millau_headers_to_rialto_transactions_mortality: None, + }, + right: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + right_sign: RialtoSigningParams { + rialto_signer: Some("//Charlie".into()), + rialto_signer_password: None, + rialto_signer_file: None, + rialto_signer_password_file: None, + rialto_transactions_mortality: Some(64), + }, + right_messages_pallet_owner: RialtoMessagesPalletOwnerSigningParams { + rialto_messages_pallet_owner: Some("//MillauMessagesOwner".into()), + rialto_messages_pallet_owner_password: None, + }, + right_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: None, + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + }), + ); + } + + #[test] + fn should_parse_relay_to_parachain_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto-parachain", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Iden", + "--rialto-headers-to-millau-signer", + "//Ken", + "--millau-messages-pallet-owner", + "//RialtoParachainMessagesOwner", + "--millau-transactions-mortality", + "64", + "--rialto-parachain-host", + "rialto-parachain-collator-charlie", + "--rialto-parachain-port", + "9944", + "--rialto-parachain-signer", + "//George", + "--rialto-parachain-messages-pallet-owner", + "//MillauMessagesOwner", + "--rialto-parachain-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--lane", + "00000000", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialtoParachain( + MillauRialtoParachainHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + relayer_mode: RelayerMode::Rational, + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Iden".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { + millau_messages_pallet_owner: Some("//RialtoParachainMessagesOwner".into()), + millau_messages_pallet_owner_password: None, + }, + left_headers_to_right_sign_override: + MillauHeadersToRialtoParachainSigningParams { + millau_headers_to_rialto_parachain_signer: None, + millau_headers_to_rialto_parachain_signer_password: None, + millau_headers_to_rialto_parachain_signer_file: None, + millau_headers_to_rialto_parachain_signer_password_file: None, + millau_headers_to_rialto_parachain_transactions_mortality: None, + }, + right: RialtoParachainConnectionParams { + rialto_parachain_host: "rialto-parachain-collator-charlie".into(), + rialto_parachain_port: 9944, + rialto_parachain_secure: false, + rialto_parachain_runtime_version: RialtoParachainRuntimeVersionParams { + rialto_parachain_version_mode: RuntimeVersionType::Bundle, + rialto_parachain_spec_version: None, + rialto_parachain_transaction_version: None, + }, + }, + right_sign: RialtoParachainSigningParams { + rialto_parachain_signer: Some("//George".into()), + rialto_parachain_signer_password: None, + rialto_parachain_signer_file: None, + rialto_parachain_signer_password_file: None, + rialto_parachain_transactions_mortality: Some(64), + }, + right_messages_pallet_owner: RialtoParachainMessagesPalletOwnerSigningParams { + rialto_parachain_messages_pallet_owner: Some( + "//MillauMessagesOwner".into() + ), + rialto_parachain_messages_pallet_owner_password: None, + }, + right_relay_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: Some("//Ken".into()), + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + right_parachains_to_left_sign_override: RialtoParachainsToMillauSigningParams { + rialto_parachains_to_millau_signer: None, + rialto_parachains_to_millau_signer_password: None, + rialto_parachains_to_millau_signer_file: None, + rialto_parachains_to_millau_signer_password_file: None, + rialto_parachains_to_millau_transactions_mortality: None, + }, + right_relay: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + } + ), + ); + } +} diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d847141d4a681d0b2317cff651b4de8509e2538 --- /dev/null +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2022 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 async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, + }, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::BlockNumberOf; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, Client, TransactionSignScheme}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{ + headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, + }, + TaggedAccount, TransactionParams, +}; + +pub struct RelayToParachainBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, +> { + pub common: + Full2WayBridgeCommonParams<<R2L as CliBridgeBase>::Target, <L2R as CliBridgeBase>::Target>, + pub right_relay: Client<<R2L as ParachainToRelayHeadersCliBridge>::SourceRelay>, + + // override for right_relay->left headers signer + pub right_headers_to_left_transaction_params: + TransactionParams<AccountKeyPairOf<<R2L as CliBridgeBase>::Target>>, + // override for right->left parachains signer + pub right_parachains_to_left_transaction_params: + TransactionParams<AccountKeyPairOf<<R2L as CliBridgeBase>::Target>>, + // override for left->right headers signer + pub left_headers_to_right_transaction_params: + TransactionParams<AccountKeyPairOf<<L2R as CliBridgeBase>::Target>>, +} + +macro_rules! declare_relay_to_parachain_bridge_schema { + // chain, parachain, relay-chain-of-parachain + ($left_chain:ident, $right_parachain:ident, $right_chain:ident) => { + paste::item! { + #[doc = $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_parachain HeadersAndMessages>] { + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + // override for right_relay->left headers signer + #[structopt(flatten)] + right_relay_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + // override for right->left parachains signer + #[structopt(flatten)] + right_parachains_to_left_sign_override: [<$right_chain ParachainsTo $left_chain SigningParams>], + #[structopt(flatten)] + left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], + #[structopt(flatten)] + right: [<$right_parachain ConnectionParams>], + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right_sign: [<$right_parachain SigningParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_parachain SigningParams>], + #[structopt(flatten)] + right_messages_pallet_owner: [<$right_parachain MessagesPalletOwnerSigningParams>], + #[structopt(flatten)] + right_relay: [<$right_chain ConnectionParams>], + } + + impl [<$left_chain $right_parachain HeadersAndMessages>] { + async fn into_bridge< + Left: TransactionSignScheme + CliChain<KeyPair = AccountKeyPairOf<Left>>, + Right: TransactionSignScheme + CliChain<KeyPair = AccountKeyPairOf<Right>>, + RightRelay: TransactionSignScheme + CliChain, + L2R: CliBridgeBase<Source = Left, Target = Right> + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase<Source = Right, Target = Left> + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge<SourceRelay = RightRelay>, + >( + self, + ) -> anyhow::Result<RelayToParachainBridge<L2R, R2L>> { + Ok(RelayToParachainBridge { + common: Full2WayBridgeCommonParams { + shared: self.shared, + left: self.left.into_client::<Left>().await?, + left_sign: self.left_sign.to_keypair::<Left>()?, + left_transactions_mortality: self.left_sign.transactions_mortality()?, + left_messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::<Left>()?, + at_left_accounts: vec![], + right: self.right.into_client::<Right>().await?, + right_sign: self.right_sign.to_keypair::<Right>()?, + right_transactions_mortality: self.right_sign.transactions_mortality()?, + right_messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::<Right>()?, + at_right_accounts: vec![], + }, + right_relay: self.right_relay.into_client::<RightRelay>().await?, + right_headers_to_left_transaction_params: self + .right_relay_headers_to_left_sign_override + .transaction_params_or::<Left, _>( + &self.left_sign, + )?, + right_parachains_to_left_transaction_params: self + .right_parachains_to_left_sign_override + .transaction_params_or::<Left, _>( + &self.left_sign, + )?, + left_headers_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::<Right, _>(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: Chain + TransactionSignScheme<Chain = Left> + CliChain<KeyPair = AccountKeyPairOf<Left>>, + Right: Chain<Hash = ParaHash> + + TransactionSignScheme<Chain = Right> + + CliChain<KeyPair = AccountKeyPairOf<Right>>, + RightRelay: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher> + + TransactionSignScheme + + CliChain, + L2R: CliBridgeBase<Source = Left, Target = Right> + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase<Source = Right, Target = Left> + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge<SourceRelay = RightRelay>, + > Full2WayBridgeBase for RelayToParachainBridge<L2R, R2L> +where + AccountIdOf<Left>: From<<AccountKeyPairOf<Left> as Pair>::Public>, + AccountIdOf<Right>: From<<AccountKeyPairOf<Right> as Pair>::Public>, +{ + type Params = RelayToParachainBridge<L2R, R2L>; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams<Left, Right> { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right> { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Left>>>, + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Right>>>, + )> { + self.common.at_left_accounts.push(TaggedAccount::Headers { + id: self.right_headers_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.at_left_accounts.push(TaggedAccount::Parachains { + id: self.right_parachains_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.at_right_accounts.push(TaggedAccount::Headers { + id: self.left_headers_to_right_transaction_params.signer.public().into(), + bridged_chain: Left::NAME.to_string(), + }); + + <L2R as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards( + &self.common.right, + &self.left_headers_to_right_transaction_params, + self.common.right.can_start_version_guard(), + ) + .await?; + <R2L as ParachainToRelayHeadersCliBridge>::RelayFinality::start_relay_guards( + &self.common.left, + &self.right_headers_to_left_transaction_params, + self.common.left.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::new::<<L2R as RelayToRelayHeadersCliBridge>::Finality>( + self.common.left.clone(), + self.common.right.clone(), + self.left_headers_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_relay_to_left_on_demand_headers = + OnDemandHeadersRelay::new::<<R2L as ParachainToRelayHeadersCliBridge>::RelayFinality>( + self.right_relay.clone(), + self.common.left.clone(), + self.right_headers_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::< + <R2L as ParachainToRelayHeadersCliBridge>::ParachainFinality, + >( + self.right_relay.clone(), + self.common.left.clone(), + self.right_parachains_to_left_transaction_params.clone(), + Arc::new(right_relay_to_left_on_demand_headers), + ); + + Ok(( + Arc::new(left_to_right_on_demand_headers), + Arc::new(right_to_left_on_demand_parachains), + )) + } +} diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs new file mode 100644 index 0000000000000000000000000000000000000000..4e9ecd2b581221168922d69850f1c78e4e11f824 --- /dev/null +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs @@ -0,0 +1,187 @@ +// Copyright 2019-2022 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 async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_runtime::BlockNumberOf; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, TransactionSignScheme}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{headers::OnDemandHeadersRelay, OnDemandRelay}, + TaggedAccount, TransactionParams, +}; + +pub struct RelayToRelayBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + RelayToRelayHeadersCliBridge, +> { + pub common: + Full2WayBridgeCommonParams<<R2L as CliBridgeBase>::Target, <L2R as CliBridgeBase>::Target>, + // override for right->left headers signer + pub right_to_left_transaction_params: + TransactionParams<AccountKeyPairOf<<R2L as CliBridgeBase>::Target>>, + // override for left->right headers signer + pub left_to_right_transaction_params: + TransactionParams<AccountKeyPairOf<<L2R as CliBridgeBase>::Target>>, +} + +macro_rules! declare_relay_to_relay_bridge_schema { + ($left_chain:ident, $right_chain:ident) => { + paste::item! { + #[doc = $left_chain " and " $right_chain " headers+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_chain HeadersAndMessages>] { + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + // override for right->left headers signer + #[structopt(flatten)] + right_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + #[structopt(flatten)] + left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right: [<$right_chain ConnectionParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_chain SigningParams>], + #[structopt(flatten)] + right_sign: [<$right_chain SigningParams>], + #[structopt(flatten)] + right_messages_pallet_owner: [<$right_chain MessagesPalletOwnerSigningParams>], + } + + impl [<$left_chain $right_chain HeadersAndMessages>] { + async fn into_bridge< + Left: TransactionSignScheme + CliChain<KeyPair = AccountKeyPairOf<Left>>, + Right: TransactionSignScheme + CliChain<KeyPair = AccountKeyPairOf<Right>>, + L2R: CliBridgeBase<Source = Left, Target = Right> + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase<Source = Right, Target = Left> + MessagesCliBridge + RelayToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result<RelayToRelayBridge<L2R, R2L>> { + Ok(RelayToRelayBridge { + common: Full2WayBridgeCommonParams { + shared: self.shared, + left: self.left.into_client::<Left>().await?, + left_sign: self.left_sign.to_keypair::<Left>()?, + left_transactions_mortality: self.left_sign.transactions_mortality()?, + left_messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::<Left>()?, + at_left_accounts: vec![], + right: self.right.into_client::<Right>().await?, + right_sign: self.right_sign.to_keypair::<Right>()?, + right_transactions_mortality: self.right_sign.transactions_mortality()?, + right_messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::<Right>()?, + at_right_accounts: vec![], + }, + + right_to_left_transaction_params: self + .right_headers_to_left_sign_override + .transaction_params_or::<Left, _>(&self.left_sign)?, + left_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::<Right, _>(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: Chain + TransactionSignScheme<Chain = Left> + CliChain<KeyPair = AccountKeyPairOf<Left>>, + Right: Chain + TransactionSignScheme<Chain = Right> + CliChain<KeyPair = AccountKeyPairOf<Right>>, + L2R: CliBridgeBase<Source = Left, Target = Right> + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase<Source = Right, Target = Left> + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + > Full2WayBridgeBase for RelayToRelayBridge<L2R, R2L> +where + AccountIdOf<Left>: From<<AccountKeyPairOf<Left> as Pair>::Public>, + AccountIdOf<Right>: From<<AccountKeyPairOf<Right> as Pair>::Public>, +{ + type Params = RelayToRelayBridge<L2R, R2L>; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams<Left, Right> { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right> { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Left>>>, + Arc<dyn OnDemandRelay<BlockNumberOf<Self::Right>>>, + )> { + self.common.at_right_accounts.push(TaggedAccount::Headers { + id: self.left_to_right_transaction_params.signer.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + self.common.at_left_accounts.push(TaggedAccount::Headers { + id: self.right_to_left_transaction_params.signer.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + + <L2R as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards( + &self.common.right, + &self.left_to_right_transaction_params, + self.common.right.can_start_version_guard(), + ) + .await?; + <R2L as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards( + &self.common.left, + &self.right_to_left_transaction_params, + self.common.left.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::new::<<L2R as RelayToRelayHeadersCliBridge>::Finality>( + self.common.left.clone(), + self.common.right.clone(), + self.left_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_to_left_on_demand_headers = + OnDemandHeadersRelay::new::<<R2L as RelayToRelayHeadersCliBridge>::Finality>( + self.common.right.clone(), + self.common.left.clone(), + self.right_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + + Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) + } +} diff --git a/bridges/relays/bin-substrate/src/cli/relay_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_messages.rs index 7242d1318520368fe9df32cf9cf127edd6c7d241..df21abac991ffacbcfa1bf9d58c951d0ad89a9f1 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_messages.rs @@ -20,16 +20,10 @@ use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use messages_relay::relay_strategy::MixStrategy; -use relay_substrate_client::{AccountKeyPairOf, ChainBase, TransactionSignScheme}; -use substrate_relay_helper::{ - messages_lane::{MessagesRelayParams, SubstrateMessageLane}, - TransactionParams, -}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, TransactionSignScheme}; +use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams}; -use crate::cli::{ - bridge::*, CliChain, HexLaneId, PrometheusParams, SourceConnectionParams, SourceSigningParams, - TargetConnectionParams, TargetSigningParams, -}; +use crate::cli::{bridge::*, chain_schema::*, CliChain, HexLaneId, PrometheusParams}; /// Relayer operating mode. #[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq, Eq)] @@ -79,16 +73,15 @@ trait MessagesRelayer: MessagesCliBridge where Self::Source: TransactionSignScheme<Chain = Self::Source> + CliChain<KeyPair = AccountKeyPairOf<Self::Source>>, - <Self::Source as ChainBase>::AccountId: From<<AccountKeyPairOf<Self::Source> as Pair>::Public>, - <Self::Target as ChainBase>::AccountId: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>, - <Self::Source as ChainBase>::Balance: TryFrom<<Self::Target as ChainBase>::Balance>, - Self::MessagesLane: SubstrateMessageLane<RelayStrategy = MixStrategy>, + AccountIdOf<Self::Source>: From<<AccountKeyPairOf<Self::Source> as Pair>::Public>, + AccountIdOf<Self::Target>: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>, + BalanceOf<Self::Source>: TryFrom<BalanceOf<Self::Target>>, { async fn relay_messages(data: RelayMessages) -> anyhow::Result<()> { - let source_client = data.source.to_client::<Self::Source>().await?; + let source_client = data.source.into_client::<Self::Source>().await?; let source_sign = data.source_sign.to_keypair::<Self::Source>()?; let source_transactions_mortality = data.source_sign.transactions_mortality()?; - let target_client = data.target.to_client::<Self::Target>().await?; + 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(); diff --git a/bridges/relays/bin-substrate/src/cli/relay_parachains.rs b/bridges/relays/bin-substrate/src/cli/relay_parachains.rs index eb46909b3a1bed71b7f126704623d7c30125ba95..f2d56b7d6e249cefb4fcdb39740e6238418a611a 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_parachains.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_parachains.rs @@ -16,27 +16,22 @@ use async_trait::async_trait; use bp_polkadot_core::parachains::ParaId; -use pallet_bridge_parachains::RelayBlockNumber; -use parachains_relay::{ - parachains_loop::{ParachainSyncParams, SourceClient, TargetClient}, - ParachainsPipeline, -}; -use relay_substrate_client::{Chain, RelayChain}; +use parachains_relay::parachains_loop::{ParachainSyncParams, SourceClient, TargetClient}; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use substrate_relay_helper::{ - parachains::{ - source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, - SubstrateParachainsPipeline, - }, + parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter}, TransactionParams, }; use crate::cli::{ - bridge::{CliBridgeBase, RialtoParachainToMillauCliBridge, WestmintToMillauCliBridge}, - CliChain, PrometheusParams, SourceConnectionParams, TargetConnectionParams, - TargetSigningParams, + bridge::{ + ParachainToRelayHeadersCliBridge, RialtoParachainToMillauCliBridge, + WestmintToMillauCliBridge, + }, + chain_schema::*, + PrometheusParams, }; /// Start parachain heads relayer process. @@ -64,29 +59,23 @@ pub enum RelayParachainsBridge { } #[async_trait] -trait ParachainsRelayer: CliBridgeBase +trait ParachainsRelayer: ParachainToRelayHeadersCliBridge where - ParachainsSource<Self::Pipeline>: SourceClient<ParachainsPipelineAdapter<Self::Pipeline>>, - ParachainsTarget<Self::Pipeline>: TargetClient<ParachainsPipelineAdapter<Self::Pipeline>>, + ParachainsSource<Self::ParachainFinality>: + SourceClient<ParachainsPipelineAdapter<Self::ParachainFinality>>, + ParachainsTarget<Self::ParachainFinality>: + TargetClient<ParachainsPipelineAdapter<Self::ParachainFinality>>, { - type SourceRelay: Chain<BlockNumber = RelayBlockNumber> + CliChain + RelayChain; - type Pipeline: SubstrateParachainsPipeline< - SourceParachain = Self::Source, - TargetChain = Self::Target, - SourceRelayChain = Self::SourceRelay, - TransactionSignScheme = Self::Target, - > + ParachainsPipeline<SourceChain = Self::SourceRelay, TargetChain = Self::Target>; - async fn relay_headers(data: RelayParachains) -> anyhow::Result<()> { - let source_client = data.source.to_client::<Self::SourceRelay>().await?; - let source_client = ParachainsSource::<Self::Pipeline>::new(source_client, None); + let source_client = data.source.into_client::<Self::SourceRelay>().await?; + let source_client = ParachainsSource::<Self::ParachainFinality>::new(source_client, None); let target_transaction_params = TransactionParams { signer: data.target_sign.to_keypair::<Self::Target>()?, mortality: data.target_sign.target_transactions_mortality, }; - let target_client = data.target.to_client::<Self::Target>().await?; - let target_client = ParachainsTarget::<Self::Pipeline>::new( + let target_client = data.target.into_client::<Self::Target>().await?; + let target_client = ParachainsTarget::<Self::ParachainFinality>::new( target_client.clone(), target_transaction_params, ); @@ -110,15 +99,9 @@ where } } -impl ParachainsRelayer for RialtoParachainToMillauCliBridge { - type SourceRelay = relay_rialto_client::Rialto; - type Pipeline = crate::chains::rialto_parachains_to_millau::RialtoParachainsToMillau; -} +impl ParachainsRelayer for RialtoParachainToMillauCliBridge {} -impl ParachainsRelayer for WestmintToMillauCliBridge { - type SourceRelay = relay_westend_client::Westend; - type Pipeline = crate::chains::westend_parachains_to_millau::WestendParachainsToMillau; -} +impl ParachainsRelayer for WestmintToMillauCliBridge {} impl RelayParachains { /// Run the command. diff --git a/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs b/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs index 62af6afe937209a37595cda842ab0007dcaec626..a4cec796552e12185cfcbc83ec09fa8c13428ac2 100644 --- a/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs +++ b/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -14,7 +14,7 @@ // 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::cli::{Balance, TargetConnectionParams, TargetSigningParams}; +use crate::cli::{chain_schema::*, Balance}; use codec::{Decode, Encode}; use num_traits::{One, Zero}; @@ -102,7 +102,7 @@ impl ResubmitTransactions { pub async fn run(self) -> anyhow::Result<()> { select_bridge!(self.chain, { let relay_loop_name = format!("ResubmitTransactions{}", Target::NAME); - let client = self.target.to_client::<Target>().await?; + let client = self.target.into_client::<Target>().await?; let transaction_params = TransactionParams { signer: self.target_sign.to_keypair::<Target>()?, mortality: self.target_sign.target_transactions_mortality, diff --git a/bridges/relays/bin-substrate/src/cli/send_message.rs b/bridges/relays/bin-substrate/src/cli/send_message.rs index ee17fb3f31fea676e8b70d6e4b0bd8d36197bd4f..da844172c984eb084dda12dda1ae6ad2641d420c 100644 --- a/bridges/relays/bin-substrate/src/cli/send_message.rs +++ b/bridges/relays/bin-substrate/src/cli/send_message.rs @@ -16,15 +16,16 @@ use crate::cli::{ bridge::{FullBridge, MessagesCliBridge, *}, + chain_schema::*, encode_message::{self, CliEncodeMessage}, estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride}, - Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams, SourceSigningParams, + Balance, CliChain, HexBytes, HexLaneId, }; use async_trait::async_trait; -use bp_runtime::AccountIdOf; use codec::Encode; use relay_substrate_client::{ - AccountKeyPairOf, Chain, ChainBase, SignParam, TransactionSignScheme, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, Chain, ChainBase, SignParam, TransactionSignScheme, + UnsignedTransaction, }; use sp_core::{Bytes, Pair}; use sp_runtime::AccountId32; @@ -95,7 +96,7 @@ where async fn send_message(data: SendMessage) -> anyhow::Result<()> { let payload = encode_message::encode_message::<Self::Source, Self::Target>(&data.message)?; - let source_client = data.source.to_client::<Self::Source>().await?; + 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(); diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 448c33c5ba3979d69cbec5ef5fca55633b72d1bc..5619645d0a485ed5f9b8edc133c893e765cdeca2 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -756,6 +756,15 @@ impl<C: Chain> Client<C> { let client = self.client.clone(); self.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await? } + + /// Returns `true` if version guard can be started. + /// + /// There's no reason to run version guard when version mode is set to `Auto`. It can + /// lead to relay shutdown when chain is upgraded, even though we have explicitly + /// said that we don't want to shutdown. + pub fn can_start_version_guard(&self) -> bool { + !matches!(self.chain_runtime_version, ChainRuntimeVersion::Auto) + } } impl<T: DeserializeOwned> Subscription<T> { diff --git a/bridges/relays/lib-substrate-relay/src/messages_metrics.rs b/bridges/relays/lib-substrate-relay/src/messages_metrics.rs index 7c621b4c39a51741d312398af13efcd0b7795cbb..4210c7d981bd580000c2fae55039fe1ce711a295 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_metrics.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_metrics.rs @@ -274,7 +274,7 @@ pub fn standalone_metrics<P: SubstrateMessageLane>( pub async fn add_relay_balances_metrics<C: ChainWithBalances>( client: Client<C>, metrics: MetricsParams, - relay_accounts: Vec<TaggedAccount<AccountIdOf<C>>>, + relay_accounts: &Vec<TaggedAccount<AccountIdOf<C>>>, ) -> anyhow::Result<MetricsParams> where BalanceOf<C>: Into<u128> + std::fmt::Debug,