CLI refactoring

* [CLI] Make bridge definitions more complete

* [CLI] Refactor relay_headers_and_messages

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