diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs
index e8f8fc1f7f..173a501032 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 3ef324265c..e0d65e3caf 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 .
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>;
}
-/// 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
+ + 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;
+ /// 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 0000000000..f6edd26db7
--- /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 .
+
+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,
+ #[doc = "The custom transaction_version for chain " $chain]
+ #[structopt(long)]
+ pub [<$chain_prefix _transaction_version>]: Option,
+ }
+
+ impl [<$chain RuntimeVersionParams>] {
+ /// Converts self into `ChainRuntimeVersion`.
+ pub fn into_runtime_version(
+ self,
+ bundle_runtime_version: Option,
+ ) -> anyhow::Result {
+ 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(
+ self,
+ ) -> anyhow::Result> {
+ 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(
+ &self,
+ ) -> anyhow::Result>;
+
+ /// Returns transaction parameters, defined by `self` provider or, if they're not defined,
+ /// defined by `other` provider.
+ fn transaction_params_or(
+ &self,
+ other: &T,
+ ) -> anyhow::Result> {
+ if self.is_defined() {
+ self.transaction_params::()
+ } else {
+ other.transaction_params::()
+ }
+ }
+}
+
+/// 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,
+ #[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,
+
+ #[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,
+ #[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,
+
+ #[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,
+ }
+
+ impl [<$chain SigningParams>] {
+ /// Return transactions mortality.
+ #[allow(dead_code)]
+ pub fn transactions_mortality(&self) -> anyhow::Result> {
+ 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(&self) -> anyhow::Result {
+ 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(&self) -> anyhow::Result> {
+ Ok(TransactionParams {
+ mortality: self.transactions_mortality()?,
+ signer: self.to_keypair::()?,
+ })
+ }
+ }
+ }
+ };
+}
+
+/// 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,
+ #[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,
+ }
+
+ #[allow(dead_code)]
+ impl [<$chain MessagesPalletOwnerSigningParams>] {
+ /// Parse signing params into chain-specific KeyPair.
+ pub fn to_keypair(&self) -> anyhow::Result> {
+ 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::()
+ .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::()
+ .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::()
+ .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::()
+ .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 30f20b681d..0f4fd765f4 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
::Balance: Display + Into,
{
async fn estimate_fee(data: EstimateFee) -> anyhow::Result<()> {
- let source_client = data.source.to_client::().await?;
+ let source_client = data.source.into_client::().await?;
let lane = data.lane.into();
let payload =
crate::cli::encode_message::encode_message::(&data.payload)
@@ -239,7 +240,6 @@ async fn do_estimate_message_delivery_and_dispatch_fee
#[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 b3110e69ff..98f30b9f7b 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::().await?;
- let target_client = data.target.to_client::().await?;
+ let source_client = data.source.into_client::().await?;
+ let target_client = data.target.into_client::().await?;
let target_sign = data.target_sign.to_keypair::()?;
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 6f394092e1..e35548c347 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(
- &self,
- ) -> anyhow::Result>;
-
- /// Returns transaction parameters, defined by `self` provider or, if they're not defined,
- /// defined by `other` provider.
- fn transaction_params_or(
- &self,
- other: &T,
- ) -> anyhow::Result> {
- if self.is_defined() {
- self.transaction_params::()
- } else {
- other.transaction_params::()
- }
- }
-}
-
-/// 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,
- #[doc = "The custom transaction_version for chain " $chain]
- #[structopt(long)]
- pub [<$chain_prefix _transaction_version>]: Option,
- }
-
- #[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,
- #[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,
-
- #[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,
- #[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,
-
- #[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,
- }
-
- #[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,
- #[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,
- }
-
- impl [<$chain SigningParams>] {
- /// Return transactions mortality.
- #[allow(dead_code)]
- pub fn transactions_mortality(&self) -> anyhow::Result> {
- 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(&self) -> anyhow::Result {
- 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(&self) -> anyhow::Result> {
- Ok(TransactionParams {
- mortality: self.transactions_mortality()?,
- signer: self.to_keypair::()?,
- })
- }
- }
-
- #[allow(dead_code)]
- impl [<$chain MessagesPalletOwnerSigningParams>] {
- /// Parse signing params into chain-specific KeyPair.
- pub fn to_keypair(&self) -> anyhow::Result> {
- 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(
- &self,
- ) -> anyhow::Result> {
- 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(
- &self,
- ) -> anyhow::Result {
- 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::()
- .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,
- ) -> anyhow::Result {
- 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::()
- .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::()
- .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::()
- .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::()
- .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 d164781205..9573cde303 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 .
-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::().await?;
+ let relay_client = self.relay_connection.into_client::().await?;
let relay_sign = self.relay_sign.to_keypair::()?;
- let para_client = self.para_connection.to_client::().await?;
+ let para_client = self.para_connection.into_client::().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(
#[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 f8d7eeb541..59325e452e 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 .
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
- ::AccountId: From< as Pair>::Public>,
+ AccountIdOf: From< as Pair>::Public>,
{
/// Relay headers.
async fn relay_headers(data: RelayHeaders) -> anyhow::Result<()> {
- let source_client = data.source.to_client::().await?;
- let target_client = data.target.to_client::().await?;
+ let source_client = data.source.into_client::().await?;
+ let target_client = data.target.into_client::().await?;
let target_transactions_mortality = data.target_sign.target_transactions_mortality;
let target_sign = data.target_sign.to_keypair::()?;
@@ -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 4d6456cc61..0000000000
--- 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 .
-
-//! 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,
- #[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 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,
- right_client: Client,
- at_left_relay_accounts: &mut Vec>>,
- at_right_relay_accounts: &mut Vec>>,
- ) -> anyhow::Result<(
- Arc>>,
- Arc>>,
- )> {
- 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::(¶ms.right_sign)?,
- params.right_headers_to_left_sign_override.transaction_params_or::(¶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,
- right_client: Client,
- at_left_relay_accounts: &mut Vec>>,
- at_right_relay_accounts: &mut Vec>>,
- ) -> anyhow::Result<(
- Arc>>,
- Arc>>,
- )> {
- type RightRelayChain = relay_rialto_client::Rialto;
- let rialto_relay_chain_client = params.right_relay.to_client::().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::(¶ms.right_sign)?,
- params.right_relay_headers_to_left_sign_override.transaction_params_or::(¶ms.left_sign)?,
- params.right_parachains_to_left_sign_override.transaction_params_or::(¶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::().await?;
- let left_transactions_mortality = params.left_sign.transactions_mortality()?;
- let left_sign = params.left_sign.to_keypair::()?;
- let left_messages_pallet_owner =
- params.left_messages_pallet_owner.to_keypair::()?;
- let right_client = params.right.to_client::().await?;
- let right_transactions_mortality = params.right_sign.transactions_mortality()?;
- let right_sign = params.right_sign.to_keypair::()?;
- let right_messages_pallet_owner =
- params.right_messages_pallet_owner.to_keypair::()?;
-
- 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(
- left_client: Client,
- right_client: Client,
- left_to_right_transaction_params: TransactionParams>,
- right_to_left_transaction_params: TransactionParams>,
- 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>>,
- at_right_relay_accounts: &mut Vec>>,
-) -> anyhow::Result<(
- Arc>>,
- Arc>>,
-)>
-where
- LC: Chain + TransactionSignScheme + CliChain>,
- RC: Chain + TransactionSignScheme + CliChain>,
- LR: SubstrateFinalitySyncPipeline<
- SourceChain = LC,
- TargetChain = RC,
- TransactionSignScheme = RC,
- >,
- RL: SubstrateFinalitySyncPipeline<
- SourceChain = RC,
- TargetChain = LC,
- TransactionSignScheme = LC,
- >,
- AccountIdOf: From<<::AccountKeyPair as Pair>::Public>,
- AccountIdOf: From<<::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::(
- 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::(
- 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(
- left_client: Client,
- right_client: Client,
- right_relay_client: Client,
- left_headers_to_right_transaction_params: TransactionParams>,
- right_headers_to_left_transaction_params: TransactionParams>,
- right_parachains_to_left_transaction_params: TransactionParams>,
- 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>>,
- at_right_relay_accounts: &mut Vec>>,
-) -> anyhow::Result<(
- Arc>>,
- Arc>>,
-)>
-where
- LC: Chain + TransactionSignScheme + CliChain>,
- RC: Chain
- + TransactionSignScheme
- + CliChain>,
- RRC: Chain
- + TransactionSignScheme
- + CliChain>,
- 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: From<<::AccountKeyPair as Pair>::Public>,
- AccountIdOf: From<<::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::(
- 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::(
- 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::(
- 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 0000000000..82f55a6d6c
--- /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 .
+
+//! 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,
+ #[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,
+ // default signer, which is always used to sign messages relay transactions on the left chain
+ pub left_sign: AccountKeyPairOf,
+ pub left_transactions_mortality: Option,
+ pub left_messages_pallet_owner: Option>,
+ pub at_left_accounts: Vec>>,
+
+ pub right: Client,
+ // default signer, which is always used to sign messages relay transactions on the right chain
+ pub right_sign: AccountKeyPairOf,
+ pub right_transactions_mortality: Option,
+ pub right_messages_pallet_owner: Option>,
+ pub at_right_accounts: Vec>>,
+}
+
+// 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
+ + CliChain>;
+ /// The right destination chain (it can be a relay or a parachain).
+ type Right: TransactionSignScheme
+ + CliChain>;
+
+ fn common(&self) -> &Full2WayBridgeCommonParams;
+
+ fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams;
+
+ async fn start_on_demand_headers_relayers(
+ &mut self,
+ ) -> anyhow::Result<(
+ Arc>>,
+ Arc>>,
+ )>;
+}
+
+#[async_trait]
+trait Full2WayBridge: Sized + Sync
+where
+ AccountIdOf: From< as Pair>::Public>,
+ AccountIdOf: From< as Pair>::Public>,
+ BalanceOf: TryFrom> + Into,
+ BalanceOf: TryFrom> + Into,
+{
+ type Base: Full2WayBridgeBase;
+
+ /// The left relay chain.
+ type Left: Chain
+ + ChainWithBalances
+ + TransactionSignScheme
+ + CliChain>;
+ /// The right relay chain.
+ type Right: Chain
+ + ChainWithBalances
+ + TransactionSignScheme
+ + CliChain>;
+
+ // Left to Right bridge
+ type L2R: MessagesCliBridge;
+ // Right to Left bridge
+ type R2L: MessagesCliBridge;
+
+ fn new(params: ::Params) -> anyhow::Result;
+
+ 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::<
+ ::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::<
+ ::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::<
+ ::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::<
+ ::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::<
+ ::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: ::Base,
+}
+
+#[async_trait]
+impl Full2WayBridge for MillauRialtoFull2WayBridge {
+ type Base = RelayToRelayBridge;
+ 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 {
+ 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: ::Base,
+}
+
+#[async_trait]
+impl Full2WayBridge for MillauRialtoParachainFull2WayBridge {
+ type Base = RelayToParachainBridge;
+ 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 {
+ 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 0000000000..0d847141d4
--- /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 .
+
+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<::Target, ::Target>,
+ pub right_relay: Client<::SourceRelay>,
+
+ // override for right_relay->left headers signer
+ pub right_headers_to_left_transaction_params:
+ TransactionParams::Target>>,
+ // override for right->left parachains signer
+ pub right_parachains_to_left_transaction_params:
+ TransactionParams::Target>>,
+ // override for left->right headers signer
+ pub left_headers_to_right_transaction_params:
+ TransactionParams::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>,
+ Right: TransactionSignScheme + CliChain>,
+ RightRelay: TransactionSignScheme + CliChain,
+ L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge,
+ R2L: CliBridgeBase
+ + MessagesCliBridge
+ + ParachainToRelayHeadersCliBridge,
+ >(
+ self,
+ ) -> anyhow::Result> {
+ Ok(RelayToParachainBridge {
+ common: Full2WayBridgeCommonParams {
+ shared: self.shared,
+ left: self.left.into_client::().await?,
+ left_sign: self.left_sign.to_keypair::()?,
+ left_transactions_mortality: self.left_sign.transactions_mortality()?,
+ left_messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?,
+ at_left_accounts: vec![],
+ right: self.right.into_client::().await?,
+ right_sign: self.right_sign.to_keypair::()?,
+ right_transactions_mortality: self.right_sign.transactions_mortality()?,
+ right_messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?,
+ at_right_accounts: vec![],
+ },
+ right_relay: self.right_relay.into_client::().await?,
+ right_headers_to_left_transaction_params: self
+ .right_relay_headers_to_left_sign_override
+ .transaction_params_or::(
+ &self.left_sign,
+ )?,
+ right_parachains_to_left_transaction_params: self
+ .right_parachains_to_left_sign_override
+ .transaction_params_or::(
+ &self.left_sign,
+ )?,
+ left_headers_to_right_transaction_params: self
+ .left_headers_to_right_sign_override
+ .transaction_params_or::(&self.right_sign)?,
+ })
+ }
+ }
+ }
+ };
+}
+
+#[async_trait]
+impl<
+ Left: Chain + TransactionSignScheme + CliChain>,
+ Right: Chain
+ + TransactionSignScheme
+ + CliChain>,
+ RightRelay: Chain
+ + TransactionSignScheme
+ + CliChain,
+ L2R: CliBridgeBase
+ + MessagesCliBridge
+ + RelayToRelayHeadersCliBridge,
+ R2L: CliBridgeBase
+ + MessagesCliBridge
+ + ParachainToRelayHeadersCliBridge,
+ > Full2WayBridgeBase for RelayToParachainBridge
+where
+ AccountIdOf: From< as Pair>::Public>,
+ AccountIdOf: From< as Pair>::Public>,
+{
+ type Params = RelayToParachainBridge;
+ type Left = Left;
+ type Right = Right;
+
+ fn common(&self) -> &Full2WayBridgeCommonParams {
+ &self.common
+ }
+
+ fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams {
+ &mut self.common
+ }
+
+ async fn start_on_demand_headers_relayers(
+ &mut self,
+ ) -> anyhow::Result<(
+ Arc>>,
+ Arc