Move generic CLI logic to different crate (#2885)

* Move generic CLI logic to separate crate

* Move and rename `CliChain` trait definition

Move it to `relay-substrate-client`

* Move generic cli logic to substrate-relay-helper

* Fix docs warnings
This commit is contained in:
Serban Iorga
2024-03-20 09:45:43 +01:00
committed by Bastian Köcher
parent bfce7a250f
commit 3643f721d4
63 changed files with 1746 additions and 1438 deletions
@@ -10,14 +10,17 @@ workspace = true
[dependencies]
anyhow = "1.0"
thiserror = { workspace = true }
async-std = "1.9.0"
async-trait = "0.1"
codec = { package = "parity-scale-codec", version = "3.1.5" }
futures = "0.3.30"
hex = "0.4"
num-traits = "0.2"
log = { workspace = true }
num-traits = "0.2"
rbtag = "0.3"
structopt = "0.3"
strum = { version = "0.26.2", features = ["derive"] }
thiserror = { workspace = true }
# Bridge dependencies
@@ -0,0 +1,110 @@
// 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/>.
//! Basic traits for exposing bridges in the CLI.
use crate::{
equivocation::SubstrateEquivocationDetectionPipeline,
finality::SubstrateFinalitySyncPipeline,
messages_lane::{MessagesRelayLimits, SubstrateMessageLane},
parachains::SubstrateParachainsPipeline,
};
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use relay_substrate_client::{
Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain,
};
/// Minimal bridge representation that can be used from the CLI.
/// It connects a source chain to a target chain.
pub trait CliBridgeBase: Sized {
/// The source chain.
type Source: Chain + ChainWithRuntimeVersion;
/// The target chain.
type Target: ChainWithTransactions + ChainWithRuntimeVersion;
}
/// Bridge representation that can be used from the CLI for relaying headers
/// from a relay chain to a relay chain.
pub trait RelayToRelayHeadersCliBridge: CliBridgeBase {
/// Finality proofs synchronization pipeline.
type Finality: SubstrateFinalitySyncPipeline<
SourceChain = Self::Source,
TargetChain = Self::Target,
>;
}
/// Convenience trait that adds bounds to `CliBridgeBase`.
pub trait RelayToRelayEquivocationDetectionCliBridgeBase: CliBridgeBase {
/// The source chain with extra bounds.
type BoundedSource: ChainWithTransactions;
}
impl<T> RelayToRelayEquivocationDetectionCliBridgeBase for T
where
T: CliBridgeBase,
T::Source: ChainWithTransactions,
{
type BoundedSource = T::Source;
}
/// Bridge representation that can be used from the CLI for detecting equivocations
/// in the headers synchronized from a relay chain to a relay chain.
pub trait RelayToRelayEquivocationDetectionCliBridge:
RelayToRelayEquivocationDetectionCliBridgeBase
{
/// Equivocation detection pipeline.
type Equivocation: SubstrateEquivocationDetectionPipeline<
SourceChain = Self::Source,
TargetChain = Self::Target,
>;
}
/// Bridge representation that can be used from the CLI for relaying headers
/// from a parachain to a relay chain.
pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase
where
Self::Source: Parachain,
{
/// 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>
+ ChainWithRuntimeVersion
+ RelayChain;
/// Finality proofs synchronization pipeline (source parachain -> target).
type ParachainFinality: SubstrateParachainsPipeline<
SourceRelayChain = Self::SourceRelay,
SourceParachain = Self::Source,
TargetChain = Self::Target,
>;
/// Finality proofs synchronization pipeline (source relay chain -> target).
type RelayFinality: SubstrateFinalitySyncPipeline<
SourceChain = Self::SourceRelay,
TargetChain = Self::Target,
>;
}
/// Bridge representation that can be used from the CLI for relaying messages.
pub trait MessagesCliBridge: CliBridgeBase {
/// The Source -> Destination messages synchronization pipeline.
type MessagesLane: SubstrateMessageLane<SourceChain = Self::Source, TargetChain = Self::Target>;
/// Optional messages delivery transaction limits that the messages relay is going
/// to use. If it returns `None`, limits are estimated using `TransactionPayment` API
/// at the target chain.
fn maybe_messages_limits() -> Option<MessagesRelayLimits> {
None
}
}
@@ -0,0 +1,250 @@
// 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/>.
//! Primitives related to chain CLI options.
use relay_substrate_client::{AccountKeyPairOf, ChainWithTransactions};
use structopt::StructOpt;
use strum::{EnumString, VariantNames};
use relay_substrate_client::{ChainRuntimeVersion, ChainWithRuntimeVersion, SimpleRuntimeVersion};
use crate::TransactionParams;
#[doc = "Runtime version params."]
#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, VariantNames)]
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) => {
bp_runtime::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<SimpleRuntimeVersion>,
) -> anyhow::Result<ChainRuntimeVersion> {
Ok(match self.[<$chain_prefix _version_mode>] {
RuntimeVersionType::Auto => ChainRuntimeVersion::Auto,
RuntimeVersionType::Custom => {
let custom_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 custom_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(
SimpleRuntimeVersion {
spec_version: custom_spec_version,
transaction_version: custom_transaction_version
}
)
},
RuntimeVersionType::Bundle => match bundle_runtime_version {
Some(runtime_version) => ChainRuntimeVersion::Custom(runtime_version),
None => {
return Err(anyhow::format_err!("Cannot use bundled runtime version of {}: it is not known to the relay", stringify!($chain_prefix)));
}
},
})
}
}
}
};
}
/// Create chain-specific set of runtime version parameters.
#[macro_export]
macro_rules! declare_chain_connection_params_cli_schema {
($chain:ident, $chain_prefix:ident) => {
bp_runtime::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: ChainWithRuntimeVersion>(
self,
) -> anyhow::Result<relay_substrate_client::Client<Chain>> {
let chain_runtime_version = self
.[<$chain_prefix _runtime_version>]
.into_runtime_version(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
)
}
}
}
};
}
/// Create chain-specific set of signing parameters.
#[macro_export]
macro_rules! declare_chain_signing_params_cli_schema {
($chain:ident, $chain_prefix:ident) => {
bp_runtime::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: ChainWithTransactions>(&self) -> anyhow::Result<AccountKeyPairOf<Chain>> {
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;
AccountKeyPairOf::<Chain>::from_string(
&suri,
suri_password.as_deref()
).map_err(|e| anyhow::format_err!("{:?}", e))
}
/// Return transaction parameters.
#[allow(dead_code)]
pub fn transaction_params<Chain: ChainWithTransactions>(
&self,
) -> anyhow::Result<TransactionParams<AccountKeyPairOf<Chain>>> {
Ok(TransactionParams {
mortality: self.transactions_mortality()?,
signer: self.to_keypair::<Chain>()?,
})
}
}
}
};
}
/// 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);
};
}
declare_chain_cli_schema!(Source, source);
declare_chain_cli_schema!(Target, target);
@@ -0,0 +1,65 @@
// Copyright 2019-2023 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/>.
//! Primitives for exposing the equivocation detection functionality in the CLI.
use crate::{
cli::{bridge::*, chain_schema::*, PrometheusParams},
equivocation,
equivocation::SubstrateEquivocationDetectionPipeline,
};
use async_trait::async_trait;
use relay_substrate_client::ChainWithTransactions;
use structopt::StructOpt;
/// Start equivocation detection loop.
#[derive(StructOpt)]
pub struct DetectEquivocationsParams {
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
/// Trait used for starting the equivocation detection loop between 2 chains.
#[async_trait]
pub trait EquivocationsDetector: RelayToRelayEquivocationDetectionCliBridge
where
Self::Source: ChainWithTransactions,
{
/// Start the equivocation detection loop.
async fn start(data: DetectEquivocationsParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
Self::Equivocation::start_relay_guards(
&source_client,
source_client.can_start_version_guard(),
)
.await?;
equivocation::run::<Self::Equivocation>(
source_client,
data.target.into_client::<Self::Target>().await?,
data.source_sign.transaction_params::<Self::Source>()?,
data.prometheus_params.into_metrics_params()?,
)
.await
}
}
@@ -0,0 +1,85 @@
// 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/>.
//! Primitives for exposing the bridge initialization functionality in the CLI.
use async_trait::async_trait;
use codec::Encode;
use crate::{
cli::{bridge::CliBridgeBase, chain_schema::*},
finality_base::engine::Engine,
};
use bp_runtime::Chain as ChainBase;
use relay_substrate_client::{AccountKeyPairOf, Chain, UnsignedTransaction};
use sp_core::Pair;
use structopt::StructOpt;
/// Bridge initialization params.
#[derive(StructOpt)]
pub struct InitBridgeParams {
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
/// Generates all required data, but does not submit extrinsic
#[structopt(long)]
dry_run: bool,
}
/// Trait used for bridge initializing.
#[async_trait]
pub trait BridgeInitializer: CliBridgeBase
where
<Self::Target as ChainBase>::AccountId: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>,
{
/// The finality engine used by the source chain.
type Engine: Engine<Self::Source>;
/// Get the encoded call to init the bridge.
fn encode_init_bridge(
init_data: <Self::Engine as Engine<Self::Source>>::InitializationData,
) -> <Self::Target as Chain>::Call;
/// Initialize the bridge.
async fn init_bridge(data: InitBridgeParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let target_client = data.target.into_client::<Self::Target>().await?;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
let dry_run = data.dry_run;
crate::finality::initialize::initialize::<Self::Engine, _, _, _>(
source_client,
target_client.clone(),
target_sign,
move |transaction_nonce, initialization_data| {
let call = Self::encode_init_bridge(initialization_data);
log::info!(
target: "bridge",
"Initialize bridge call encoded as hex string: {:?}",
format!("0x{}", hex::encode(call.encode()))
);
Ok(UnsignedTransaction::new(call.into(), transaction_nonce))
},
dry_run,
)
.await;
Ok(())
}
}
@@ -0,0 +1,192 @@
// 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/>.
//! Deal with CLI args of substrate-to-substrate relay.
use codec::{Decode, Encode};
use rbtag::BuildInfo;
use structopt::StructOpt;
use strum::{EnumString, VariantNames};
use bp_messages::LaneId;
pub mod bridge;
pub mod chain_schema;
pub mod detect_equivocations;
pub mod init_bridge;
pub mod relay_headers;
pub mod relay_headers_and_messages;
pub mod relay_messages;
pub mod relay_parachains;
/// The target that will be used when publishing logs related to this pallet.
pub const LOG_TARGET: &str = "bridge";
/// Lane id.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HexLaneId(pub [u8; 4]);
impl From<HexLaneId> for LaneId {
fn from(lane_id: HexLaneId) -> LaneId {
LaneId(lane_id.0)
}
}
impl std::str::FromStr for HexLaneId {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lane_id = [0u8; 4];
hex::decode_to_slice(s, &mut lane_id)?;
Ok(HexLaneId(lane_id))
}
}
/// Nicer formatting for raw bytes vectors.
#[derive(Default, Encode, Decode, PartialEq, Eq)]
pub struct HexBytes(pub Vec<u8>);
impl std::str::FromStr for HexBytes {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hex::decode(s)?))
}
}
impl std::fmt::Debug for HexBytes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "0x{self}")
}
}
impl std::fmt::Display for HexBytes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", hex::encode(&self.0))
}
}
/// Prometheus metrics params.
#[derive(Clone, Debug, PartialEq, StructOpt)]
pub struct PrometheusParams {
/// Do not expose a Prometheus metric endpoint.
#[structopt(long)]
pub no_prometheus: bool,
/// Expose Prometheus endpoint at given interface.
#[structopt(long, default_value = "127.0.0.1")]
pub prometheus_host: String,
/// Expose Prometheus endpoint at given port.
#[structopt(long, default_value = "9616")]
pub prometheus_port: u16,
}
/// Struct to get git commit info and build time.
#[derive(BuildInfo)]
struct SubstrateRelayBuildInfo;
impl SubstrateRelayBuildInfo {
/// Get git commit in form `<short-sha-(clean|dirty)>`.
pub fn get_git_commit() -> String {
// on gitlab we use images without git installed, so we can't use `rbtag` there
// locally we don't have `CI_*` env variables, so we can't rely on them
// => we are using `CI_*` env variables or else `rbtag`
let maybe_sha_from_ci = option_env!("CI_COMMIT_SHORT_SHA");
maybe_sha_from_ci
.map(|short_sha| {
// we assume that on CI the copy is always clean
format!("{short_sha}-clean")
})
.unwrap_or_else(|| SubstrateRelayBuildInfo.get_build_commit().into())
}
}
impl PrometheusParams {
/// Tries to convert CLI metrics params into metrics params, used by the relay.
pub fn into_metrics_params(self) -> anyhow::Result<relay_utils::metrics::MetricsParams> {
let metrics_address = if !self.no_prometheus {
Some(relay_utils::metrics::MetricsAddress {
host: self.prometheus_host,
port: self.prometheus_port,
})
} else {
None
};
let relay_version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
let relay_commit = SubstrateRelayBuildInfo::get_git_commit();
relay_utils::metrics::MetricsParams::new(
metrics_address,
relay_version.into(),
relay_commit,
)
.map_err(|e| anyhow::format_err!("{:?}", e))
}
}
/// Either explicit or maximal allowed value.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExplicitOrMaximal<V> {
/// User has explicitly specified argument value.
Explicit(V),
/// Maximal allowed value for this argument.
Maximal,
}
impl<V: std::str::FromStr> std::str::FromStr for ExplicitOrMaximal<V>
where
V::Err: std::fmt::Debug,
{
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "max" {
return Ok(ExplicitOrMaximal::Maximal)
}
V::from_str(s)
.map(ExplicitOrMaximal::Explicit)
.map_err(|e| format!("Failed to parse '{e:?}'. Expected 'max' or explicit value"))
}
}
#[doc = "Runtime version params."]
#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, VariantNames)]
pub enum RuntimeVersionType {
/// Auto query version from chain
Auto,
/// Custom `spec_version` and `transaction_version`
Custom,
/// Read version from bundle dependencies directly.
Bundle,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hex_bytes_display_matches_from_str_for_clap() {
// given
let hex = HexBytes(vec![1, 2, 3, 4]);
let display = format!("{hex}");
// when
let hex2: HexBytes = display.parse().unwrap();
// then
assert_eq!(hex.0, hex2.0);
}
}
@@ -0,0 +1,76 @@
// 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/>.
//! Primitives for exposing the headers relaying functionality in the CLI.
use async_trait::async_trait;
use structopt::StructOpt;
use relay_utils::metrics::{GlobalMetrics, StandaloneMetric};
use crate::{
cli::{bridge::*, chain_schema::*, PrometheusParams},
finality::SubstrateFinalitySyncPipeline,
};
/// Chain headers relaying params.
#[derive(StructOpt)]
pub struct RelayHeadersParams {
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
only_mandatory_headers: bool,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
/// Trait used for relaying headers between 2 chains.
#[async_trait]
pub trait HeadersRelayer: RelayToRelayHeadersCliBridge {
/// Relay headers.
async fn relay_headers(data: RelayHeadersParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let target_client = data.target.into_client::<Self::Target>().await?;
let target_transactions_mortality = data.target_sign.target_transactions_mortality;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
let metrics_params: relay_utils::metrics::MetricsParams =
data.prometheus_params.into_metrics_params()?;
GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?;
let target_transactions_params = crate::TransactionParams {
signer: target_sign,
mortality: target_transactions_mortality,
};
Self::Finality::start_relay_guards(&target_client, target_client.can_start_version_guard())
.await?;
crate::finality::run::<Self::Finality>(
source_client,
target_client,
data.only_mandatory_headers,
target_transactions_params,
metrics_params,
)
.await
}
}
@@ -0,0 +1,484 @@
// 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]
pub mod parachain_to_parachain;
#[macro_use]
pub mod relay_to_relay;
#[macro_use]
pub mod relay_to_parachain;
use async_trait::async_trait;
use std::{marker::PhantomData, sync::Arc};
use structopt::StructOpt;
use futures::{FutureExt, TryFutureExt};
use crate::{
cli::{bridge::MessagesCliBridge, HexLaneId, PrometheusParams},
messages_lane::{MessagesRelayLimits, MessagesRelayParams},
on_demand::OnDemandRelay,
TaggedAccount, TransactionParams,
};
use bp_messages::LaneId;
use bp_runtime::BalanceOf;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages,
ChainWithRuntimeVersion, ChainWithTransactions, Client,
};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
/// 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>,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
pub only_mandatory_headers: bool,
#[structopt(flatten)]
/// Prometheus metrics params.
pub prometheus_params: PrometheusParams,
}
/// Bridge parameters, shared by all bridge types.
pub struct Full2WayBridgeCommonParams<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion,
> {
/// Shared parameters.
pub shared: HeadersAndMessagesSharedParams,
/// Parameters of the left chain.
pub left: BridgeEndCommonParams<Left>,
/// Parameters of the right chain.
pub right: BridgeEndCommonParams<Right>,
/// Common metric parameters.
pub metrics_params: MetricsParams,
}
impl<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion,
> Full2WayBridgeCommonParams<Left, Right>
{
/// Creates new bridge parameters from its components.
pub fn new<L2R: MessagesCliBridge<Source = Left, Target = Right>>(
shared: HeadersAndMessagesSharedParams,
left: BridgeEndCommonParams<Left>,
right: BridgeEndCommonParams<Right>,
) -> anyhow::Result<Self> {
// Create metrics registry.
let metrics_params = shared.prometheus_params.clone().into_metrics_params()?;
let metrics_params = relay_utils::relay_metrics(metrics_params).into_params();
Ok(Self { shared, left, right, metrics_params })
}
}
/// Parameters that are associated with one side of the bridge.
pub struct BridgeEndCommonParams<Chain: ChainWithTransactions + ChainWithRuntimeVersion> {
/// Chain client.
pub client: Client<Chain>,
/// Params used for sending transactions to the chain.
pub tx_params: TransactionParams<AccountKeyPairOf<Chain>>,
/// Accounts, which balances are exposed as metrics by the relay process.
pub accounts: Vec<TaggedAccount<AccountIdOf<Chain>>>,
}
/// All data of the bidirectional complex relay.
pub struct FullBridge<
'a,
Source: ChainWithTransactions + ChainWithRuntimeVersion,
Target: ChainWithTransactions + ChainWithRuntimeVersion,
Bridge: MessagesCliBridge<Source = Source, Target = Target>,
> {
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
_phantom_data: PhantomData<Bridge>,
}
impl<
'a,
Source: ChainWithTransactions + ChainWithRuntimeVersion,
Target: ChainWithTransactions + ChainWithRuntimeVersion,
Bridge: MessagesCliBridge<Source = Source, Target = Target>,
> FullBridge<'a, Source, Target, Bridge>
where
AccountIdOf<Source>: From<<AccountKeyPairOf<Source> as Pair>::Public>,
AccountIdOf<Target>: From<<AccountKeyPairOf<Target> as Pair>::Public>,
BalanceOf<Source>: TryFrom<BalanceOf<Target>> + Into<u128>,
{
/// Construct complex relay given it components.
fn new(
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
) -> Self {
Self { source, target, metrics_params, _phantom_data: Default::default() }
}
/// Returns message relay parameters.
fn messages_relay_params(
&self,
source_to_target_headers_relay: Arc<dyn OnDemandRelay<Source, Target>>,
target_to_source_headers_relay: Arc<dyn OnDemandRelay<Target, Source>>,
lane_id: LaneId,
maybe_limits: Option<MessagesRelayLimits>,
) -> MessagesRelayParams<Bridge::MessagesLane> {
MessagesRelayParams {
source_client: self.source.client.clone(),
source_transaction_params: self.source.tx_params.clone(),
target_client: self.target.client.clone(),
target_transaction_params: self.target.tx_params.clone(),
source_to_target_headers_relay: Some(source_to_target_headers_relay),
target_to_source_headers_relay: Some(target_to_source_headers_relay),
lane_id,
limits: maybe_limits,
metrics_params: self.metrics_params.clone().disable(),
}
}
}
/// Base portion of the bidirectional complex relay.
///
/// This main purpose of extracting this trait is that in different relays the implementation
/// of `start_on_demand_headers_relayers` method will be different. But the number of
/// implementations is limited to relay <> relay, parachain <> relay and parachain <> parachain.
/// This trait allows us to reuse these implementations in different bridges.
#[async_trait]
pub trait Full2WayBridgeBase: Sized + Send + Sync {
/// The CLI params for the bridge.
type Params;
/// The left relay chain.
type Left: ChainWithTransactions + ChainWithRuntimeVersion;
/// The right destination chain (it can be a relay or a parachain).
type Right: ChainWithTransactions + ChainWithRuntimeVersion;
/// Reference to common relay parameters.
fn common(&self) -> &Full2WayBridgeCommonParams<Self::Left, Self::Right>;
/// Mutable reference to common relay parameters.
fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right>;
/// Start on-demand headers relays.
async fn start_on_demand_headers_relayers(
&mut self,
) -> anyhow::Result<(
Arc<dyn OnDemandRelay<Self::Left, Self::Right>>,
Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
)>;
}
/// Bidirectional complex relay.
#[async_trait]
pub 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>,
{
/// Base portion of the bidirectional complex relay.
type Base: Full2WayBridgeBase<Left = Self::Left, Right = Self::Right>;
/// The left relay chain.
type Left: ChainWithTransactions
+ ChainWithBalances
+ ChainWithMessages
+ ChainWithRuntimeVersion;
/// The right relay chain.
type Right: ChainWithTransactions
+ ChainWithBalances
+ ChainWithMessages
+ ChainWithRuntimeVersion;
/// 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>;
/// Construct new bridge.
fn new(params: <Self::Base as Full2WayBridgeBase>::Params) -> anyhow::Result<Self>;
/// Reference to the base relay portion.
fn base(&self) -> &Self::Base;
/// Mutable reference to the base relay portion.
fn mut_base(&mut self) -> &mut Self::Base;
/// Creates and returns Left to Right complex relay.
fn left_to_right(&mut self) -> FullBridge<Self::Left, Self::Right, Self::L2R> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::L2R>::new(
&mut common.left,
&mut common.right,
&common.metrics_params,
)
}
/// Creates and returns Right to Left complex relay.
fn right_to_left(&mut self) -> FullBridge<Self::Right, Self::Left, Self::R2L> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::R2L>::new(
&mut common.right,
&mut common.left,
&common.metrics_params,
)
}
/// Start complex relay.
async fn run(&mut self) -> anyhow::Result<()> {
// Register standalone metrics.
{
let common = self.mut_base().mut_common();
common.left.accounts.push(TaggedAccount::Messages {
id: common.left.tx_params.signer.public().into(),
bridged_chain: Self::Right::NAME.to_string(),
});
common.right.accounts.push(TaggedAccount::Messages {
id: common.right.tx_params.signer.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 lanes = self
.base()
.common()
.shared
.lane
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>();
{
let common = self.mut_base().mut_common();
crate::messages_metrics::add_relay_balances_metrics::<_, Self::Right>(
common.left.client.clone(),
&common.metrics_params,
&common.left.accounts,
&lanes,
)
.await?;
crate::messages_metrics::add_relay_balances_metrics::<_, Self::Left>(
common.right.client.clone(),
&common.metrics_params,
&common.right.accounts,
&lanes,
)
.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 left_to_right_messages = crate::messages_lane::run::<
<Self::L2R as MessagesCliBridge>::MessagesLane,
>(self.left_to_right().messages_relay_params(
left_to_right_on_demand_headers.clone(),
right_to_left_on_demand_headers.clone(),
lane,
Self::L2R::maybe_messages_limits(),
))
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
message_relays.push(left_to_right_messages);
let right_to_left_messages = crate::messages_lane::run::<
<Self::R2L as MessagesCliBridge>::MessagesLane,
>(self.right_to_left().messages_relay_params(
right_to_left_on_demand_headers.clone(),
left_to_right_on_demand_headers.clone(),
lane,
Self::R2L::maybe_messages_limits(),
))
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
message_relays.push(right_to_left_messages);
}
relay_utils::relay_metrics(self.base().common().metrics_params.clone())
.expose()
.await
.map_err(|e| anyhow::format_err!("{}", e))?;
futures::future::select_all(message_relays).await.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema};
use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion};
#[test]
// We need `#[allow(dead_code)]` because some of the methods generated by the macros
// are not used.
#[allow(dead_code)]
fn should_parse_parachain_to_parachain_options() {
// Chains.
declare_chain_cli_schema!(Kusama, kusama);
declare_chain_cli_schema!(BridgeHubKusama, bridge_hub_kusama);
declare_chain_cli_schema!(Polkadot, polkadot);
declare_chain_cli_schema!(BridgeHubPolkadot, bridge_hub_polkadot);
// Means to override signers of different layer transactions.
declare_chain_cli_schema!(
KusamaHeadersToBridgeHubPolkadot,
kusama_headers_to_bridge_hub_polkadot
);
declare_chain_cli_schema!(
KusamaParachainsToBridgeHubPolkadot,
kusama_parachains_to_bridge_hub_polkadot
);
declare_chain_cli_schema!(
PolkadotHeadersToBridgeHubKusama,
polkadot_headers_to_bridge_hub_kusama
);
declare_chain_cli_schema!(
PolkadotParachainsToBridgeHubKusama,
polkadot_parachains_to_bridge_hub_kusama
);
// Bridges.
declare_parachain_to_parachain_bridge_schema!(
BridgeHubKusama,
Kusama,
BridgeHubPolkadot,
Polkadot
);
let res = BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages::from_iter(vec![
"bridge-hub-kusama-bridge-hub-polkadot-headers-and-messages",
"--bridge-hub-kusama-host",
"bridge-hub-kusama-node-collator1",
"--bridge-hub-kusama-port",
"9944",
"--bridge-hub-kusama-signer",
"//Iden",
"--bridge-hub-kusama-transactions-mortality",
"64",
"--kusama-host",
"kusama-alice",
"--kusama-port",
"9944",
"--bridge-hub-polkadot-host",
"bridge-hub-polkadot-collator1",
"--bridge-hub-polkadot-port",
"9944",
"--bridge-hub-polkadot-signer",
"//George",
"--bridge-hub-polkadot-transactions-mortality",
"64",
"--polkadot-host",
"polkadot-alice",
"--polkadot-port",
"9944",
"--lane",
"00000000",
"--prometheus-host",
"0.0.0.0",
]);
// then
assert_eq!(
res,
BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages {
shared: HeadersAndMessagesSharedParams {
lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])],
only_mandatory_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
prometheus_host: "0.0.0.0".into(),
prometheus_port: 9616,
},
},
left: BridgeHubKusamaConnectionParams {
bridge_hub_kusama_host: "bridge-hub-kusama-node-collator1".into(),
bridge_hub_kusama_port: 9944,
bridge_hub_kusama_secure: false,
bridge_hub_kusama_runtime_version: BridgeHubKusamaRuntimeVersionParams {
bridge_hub_kusama_version_mode: RuntimeVersionType::Bundle,
bridge_hub_kusama_spec_version: None,
bridge_hub_kusama_transaction_version: None,
},
},
left_sign: BridgeHubKusamaSigningParams {
bridge_hub_kusama_signer: Some("//Iden".into()),
bridge_hub_kusama_signer_password: None,
bridge_hub_kusama_signer_file: None,
bridge_hub_kusama_signer_password_file: None,
bridge_hub_kusama_transactions_mortality: Some(64),
},
left_relay: KusamaConnectionParams {
kusama_host: "kusama-alice".into(),
kusama_port: 9944,
kusama_secure: false,
kusama_runtime_version: KusamaRuntimeVersionParams {
kusama_version_mode: RuntimeVersionType::Bundle,
kusama_spec_version: None,
kusama_transaction_version: None,
},
},
right: BridgeHubPolkadotConnectionParams {
bridge_hub_polkadot_host: "bridge-hub-polkadot-collator1".into(),
bridge_hub_polkadot_port: 9944,
bridge_hub_polkadot_secure: false,
bridge_hub_polkadot_runtime_version: BridgeHubPolkadotRuntimeVersionParams {
bridge_hub_polkadot_version_mode: RuntimeVersionType::Bundle,
bridge_hub_polkadot_spec_version: None,
bridge_hub_polkadot_transaction_version: None,
},
},
right_sign: BridgeHubPolkadotSigningParams {
bridge_hub_polkadot_signer: Some("//George".into()),
bridge_hub_polkadot_signer_password: None,
bridge_hub_polkadot_signer_file: None,
bridge_hub_polkadot_signer_password_file: None,
bridge_hub_polkadot_transactions_mortality: Some(64),
},
right_relay: PolkadotConnectionParams {
polkadot_host: "polkadot-alice".into(),
polkadot_port: 9944,
polkadot_secure: false,
polkadot_runtime_version: PolkadotRuntimeVersionParams {
polkadot_version_mode: RuntimeVersionType::Bundle,
polkadot_spec_version: None,
polkadot_transaction_version: None,
},
},
}
);
}
}
@@ -0,0 +1,217 @@
// 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/>.
//! Parachain to parachain relayer CLI primitives.
use async_trait::async_trait;
use std::sync::Arc;
use crate::{
cli::{
bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge},
relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams},
},
finality::SubstrateFinalitySyncPipeline,
on_demand::{
headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay,
},
};
use bp_polkadot_core::parachains::ParaHash;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client,
Parachain,
};
use sp_core::Pair;
/// A base relay between two parachain from different consensus systems.
///
/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 2 on-demand
/// parachain heads relay.
pub struct ParachainToParachainBridge<
L2R: MessagesCliBridge + ParachainToRelayHeadersCliBridge,
R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge,
> where
<L2R as CliBridgeBase>::Source: Parachain,
<R2L as CliBridgeBase>::Source: Parachain,
{
/// Parameters that are shared by all bridge types.
pub common:
Full2WayBridgeCommonParams<<R2L as CliBridgeBase>::Target, <L2R as CliBridgeBase>::Target>,
/// Client of the left relay chain.
pub left_relay: Client<<L2R as ParachainToRelayHeadersCliBridge>::SourceRelay>,
/// Client of the right relay chain.
pub right_relay: Client<<R2L as ParachainToRelayHeadersCliBridge>::SourceRelay>,
}
/// Create set of configuration objects specific to parachain-to-parachain relayer.
#[macro_export]
macro_rules! declare_parachain_to_parachain_bridge_schema {
// left-parachain, relay-chain-of-left-parachain, right-parachain, relay-chain-of-right-parachain
($left_parachain:ident, $left_chain:ident, $right_parachain:ident, $right_chain:ident) => {
bp_runtime::paste::item! {
#[doc = $left_parachain ", " $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."]
#[derive(Debug, PartialEq, StructOpt)]
pub struct [<$left_parachain $right_parachain HeadersAndMessages>] {
// shared parameters
#[structopt(flatten)]
shared: HeadersAndMessagesSharedParams,
#[structopt(flatten)]
left: [<$left_parachain ConnectionParams>],
// default signer, which is always used to sign messages relay transactions on the left chain
#[structopt(flatten)]
left_sign: [<$left_parachain SigningParams>],
#[structopt(flatten)]
left_relay: [<$left_chain ConnectionParams>],
#[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>],
#[structopt(flatten)]
right_relay: [<$right_chain ConnectionParams>],
}
impl [<$left_parachain $right_parachain HeadersAndMessages>] {
async fn into_bridge<
Left: ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
LeftRelay: ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
RightRelay: ChainWithRuntimeVersion,
L2R: $crate::cli::bridge::CliBridgeBase<Source = Left, Target = Right>
+ MessagesCliBridge
+ $crate::cli::bridge::ParachainToRelayHeadersCliBridge<SourceRelay = LeftRelay>,
R2L: $crate::cli::bridge::CliBridgeBase<Source = Right, Target = Left>
+ MessagesCliBridge
+ $crate::cli::bridge::ParachainToRelayHeadersCliBridge<SourceRelay = RightRelay>,
>(
self,
) -> anyhow::Result<$crate::cli::relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge<L2R, R2L>> {
Ok($crate::cli::relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge {
common: Full2WayBridgeCommonParams::new::<L2R>(
self.shared,
BridgeEndCommonParams {
client: self.left.into_client::<Left>().await?,
tx_params: self.left_sign.transaction_params::<Left>()?,
accounts: vec![],
},
BridgeEndCommonParams {
client: self.right.into_client::<Right>().await?,
tx_params: self.right_sign.transaction_params::<Right>()?,
accounts: vec![],
},
)?,
left_relay: self.left_relay.into_client::<LeftRelay>().await?,
right_relay: self.right_relay.into_client::<RightRelay>().await?,
})
}
}
}
};
}
#[async_trait]
impl<
Left: Chain<Hash = ParaHash> + ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
Right: Chain<Hash = ParaHash> + ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
LeftRelay: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>
+ ChainWithRuntimeVersion,
RightRelay: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>
+ ChainWithRuntimeVersion,
L2R: CliBridgeBase<Source = Left, Target = Right>
+ MessagesCliBridge
+ ParachainToRelayHeadersCliBridge<SourceRelay = LeftRelay>,
R2L: CliBridgeBase<Source = Right, Target = Left>
+ MessagesCliBridge
+ ParachainToRelayHeadersCliBridge<SourceRelay = RightRelay>,
> Full2WayBridgeBase for ParachainToParachainBridge<L2R, R2L>
where
AccountIdOf<Left>: From<<AccountKeyPairOf<Left> as Pair>::Public>,
AccountIdOf<Right>: From<<AccountKeyPairOf<Right> as Pair>::Public>,
{
type Params = ParachainToParachainBridge<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<Self::Left, Self::Right>>,
Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
)> {
<L2R as ParachainToRelayHeadersCliBridge>::RelayFinality::start_relay_guards(
&self.common.right.client,
self.common.right.client.can_start_version_guard(),
)
.await?;
<R2L as ParachainToRelayHeadersCliBridge>::RelayFinality::start_relay_guards(
&self.common.left.client,
self.common.left.client.can_start_version_guard(),
)
.await?;
let left_relay_to_right_on_demand_headers =
OnDemandHeadersRelay::<<L2R as ParachainToRelayHeadersCliBridge>::RelayFinality>::new(
self.left_relay.clone(),
self.common.right.client.clone(),
self.common.right.tx_params.clone(),
self.common.shared.only_mandatory_headers,
Some(self.common.metrics_params.clone()),
);
let right_relay_to_left_on_demand_headers =
OnDemandHeadersRelay::<<R2L as ParachainToRelayHeadersCliBridge>::RelayFinality>::new(
self.right_relay.clone(),
self.common.left.client.clone(),
self.common.left.tx_params.clone(),
self.common.shared.only_mandatory_headers,
Some(self.common.metrics_params.clone()),
);
let left_to_right_on_demand_parachains = OnDemandParachainsRelay::<
<L2R as ParachainToRelayHeadersCliBridge>::ParachainFinality,
>::new(
self.left_relay.clone(),
self.common.right.client.clone(),
self.common.right.tx_params.clone(),
Arc::new(left_relay_to_right_on_demand_headers),
);
let right_to_left_on_demand_parachains = OnDemandParachainsRelay::<
<R2L as ParachainToRelayHeadersCliBridge>::ParachainFinality,
>::new(
self.right_relay.clone(),
self.common.left.client.clone(),
self.common.left.tx_params.clone(),
Arc::new(right_relay_to_left_on_demand_headers),
);
Ok((
Arc::new(left_to_right_on_demand_parachains),
Arc::new(right_to_left_on_demand_parachains),
))
}
}
@@ -0,0 +1,199 @@
// 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/>.
//! Relay chain to parachain relayer CLI primitives.
use async_trait::async_trait;
use std::sync::Arc;
use crate::{
cli::{
bridge::{
CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge,
RelayToRelayHeadersCliBridge,
},
relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams},
},
finality::SubstrateFinalitySyncPipeline,
on_demand::{
headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay,
},
};
use bp_polkadot_core::parachains::ParaHash;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client,
Parachain,
};
use sp_core::Pair;
/// A base relay between standalone (relay) chain and a parachain from another consensus system.
///
/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 1 on-demand
/// parachain heads relay.
pub struct RelayToParachainBridge<
L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge,
R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge,
> where
<R2L as CliBridgeBase>::Source: Parachain,
{
/// Parameters that are shared by all bridge types.
pub common:
Full2WayBridgeCommonParams<<R2L as CliBridgeBase>::Target, <L2R as CliBridgeBase>::Target>,
/// Client of the right relay chain.
pub right_relay: Client<<R2L as ParachainToRelayHeadersCliBridge>::SourceRelay>,
}
/// Create set of configuration objects specific to relay-to-parachain relayer.
#[macro_export]
macro_rules! declare_relay_to_parachain_bridge_schema {
// chain, parachain, relay-chain-of-parachain
($left_chain:ident, $right_parachain:ident, $right_chain:ident) => {
bp_runtime::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>] {
// shared parameters
#[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>],
#[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>],
#[structopt(flatten)]
right_relay: [<$right_chain ConnectionParams>],
}
impl [<$left_chain $right_parachain HeadersAndMessages>] {
async fn into_bridge<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
RightRelay: ChainWithRuntimeVersion,
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::new::<L2R>(
self.shared,
BridgeEndCommonParams {
client: self.left.into_client::<Left>().await?,
tx_params: self.left_sign.transaction_params::<Left>()?,
accounts: vec![],
},
BridgeEndCommonParams {
client: self.right.into_client::<Right>().await?,
tx_params: self.right_sign.transaction_params::<Right>()?,
accounts: vec![],
},
)?,
right_relay: self.right_relay.into_client::<RightRelay>().await?,
})
}
}
}
};
}
#[async_trait]
impl<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: Chain<Hash = ParaHash> + ChainWithTransactions + ChainWithRuntimeVersion + Parachain,
RightRelay: Chain<BlockNumber = RelayBlockNumber, Hash = RelayBlockHash, Hasher = RelayBlockHasher>
+ ChainWithRuntimeVersion,
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<Self::Left, Self::Right>>,
Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
)> {
<L2R as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards(
&self.common.right.client,
self.common.right.client.can_start_version_guard(),
)
.await?;
<R2L as ParachainToRelayHeadersCliBridge>::RelayFinality::start_relay_guards(
&self.common.left.client,
self.common.left.client.can_start_version_guard(),
)
.await?;
let left_to_right_on_demand_headers =
OnDemandHeadersRelay::<<L2R as RelayToRelayHeadersCliBridge>::Finality>::new(
self.common.left.client.clone(),
self.common.right.client.clone(),
self.common.right.tx_params.clone(),
self.common.shared.only_mandatory_headers,
None,
);
let right_relay_to_left_on_demand_headers =
OnDemandHeadersRelay::<<R2L as ParachainToRelayHeadersCliBridge>::RelayFinality>::new(
self.right_relay.clone(),
self.common.left.client.clone(),
self.common.left.tx_params.clone(),
self.common.shared.only_mandatory_headers,
Some(self.common.metrics_params.clone()),
);
let right_to_left_on_demand_parachains = OnDemandParachainsRelay::<
<R2L as ParachainToRelayHeadersCliBridge>::ParachainFinality,
>::new(
self.right_relay.clone(),
self.common.left.client.clone(),
self.common.left.tx_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,169 @@
// 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/>.
// we don't have any relay/standalone <> relay/standalone chain bridges, but we may need it in a
// future
#![allow(unused_macros)]
//! Relay chain to Relay chain relayer CLI primitives.
use async_trait::async_trait;
use std::sync::Arc;
use crate::{
cli::{
bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge},
relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams},
},
finality::SubstrateFinalitySyncPipeline,
on_demand::{headers::OnDemandHeadersRelay, OnDemandRelay},
};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, ChainWithRuntimeVersion, ChainWithTransactions,
};
use sp_core::Pair;
/// A base relay between two standalone (relay) chains.
///
/// Such relay starts 2 messages relay and 2 on-demand header relays.
pub struct RelayToRelayBridge<
L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge,
R2L: MessagesCliBridge + RelayToRelayHeadersCliBridge,
> {
/// Parameters that are shared by all bridge types.
pub common:
Full2WayBridgeCommonParams<<R2L as CliBridgeBase>::Target, <L2R as CliBridgeBase>::Target>,
}
/// Create set of configuration objects specific to relay-to-relay relayer.
macro_rules! declare_relay_to_relay_bridge_schema {
($left_chain:ident, $right_chain:ident) => {
bp_runtime::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,
#[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>],
#[structopt(flatten)]
right: [<$right_chain ConnectionParams>],
#[structopt(flatten)]
// default signer, which is always used to sign messages relay transactions on the right chain
right_sign: [<$right_chain SigningParams>],
}
impl [<$left_chain $right_chain HeadersAndMessages>] {
async fn into_bridge<
Left: ChainWithTransactions + CliChain,
Right: ChainWithTransactions + CliChain,
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::new::<L2R>(
self.shared,
BridgeEndCommonParams {
client: self.left.into_client::<Left>().await?,
tx_params: self.left_sign.transaction_params::<Left>()?,
accounts: vec![],
},
BridgeEndCommonParams {
client: self.right.into_client::<Right>().await?,
tx_params: self.right_sign.transaction_params::<Right>()?,
accounts: vec![],
},
)?,
right_to_left_transaction_params: self.left_sign.transaction_params::<Left>(),
left_to_right_transaction_params: self.right_sign.transaction_params::<Right>(),
})
}
}
}
};
}
#[async_trait]
impl<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion,
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<Self::Left, Self::Right>>,
Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
)> {
<L2R as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards(
&self.common.right.client,
self.common.right.client.can_start_version_guard(),
)
.await?;
<R2L as RelayToRelayHeadersCliBridge>::Finality::start_relay_guards(
&self.common.left.client,
self.common.left.client.can_start_version_guard(),
)
.await?;
let left_to_right_on_demand_headers =
OnDemandHeadersRelay::<<L2R as RelayToRelayHeadersCliBridge>::Finality>::new(
self.common.left.client.clone(),
self.common.right.client.clone(),
self.common.right.tx_params.clone(),
self.common.shared.only_mandatory_headers,
None,
);
let right_to_left_on_demand_headers =
OnDemandHeadersRelay::<<R2L as RelayToRelayHeadersCliBridge>::Finality>::new(
self.common.right.client.clone(),
self.common.left.client.clone(),
self.common.left.tx_params.clone(),
self.common.shared.only_mandatory_headers,
None,
);
Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers)))
}
}
@@ -0,0 +1,89 @@
// 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/>.
//! Primitives for exposing the messages relaying functionality in the CLI.
use crate::{
cli::{bridge::*, chain_schema::*, HexLaneId, PrometheusParams},
messages_lane::MessagesRelayParams,
TransactionParams,
};
use async_trait::async_trait;
use sp_core::Pair;
use structopt::StructOpt;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithRuntimeVersion, ChainWithTransactions,
};
/// Messages relaying params.
#[derive(StructOpt)]
pub struct RelayMessagesParams {
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
/// Trait used for relaying messages between 2 chains.
#[async_trait]
pub trait MessagesRelayer: MessagesCliBridge
where
Self::Source: ChainWithTransactions + ChainWithRuntimeVersion,
AccountIdOf<Self::Source>: From<<AccountKeyPairOf<Self::Source> as Pair>::Public>,
AccountIdOf<Self::Target>: From<<AccountKeyPairOf<Self::Target> as Pair>::Public>,
BalanceOf<Self::Source>: TryFrom<BalanceOf<Self::Target>>,
{
/// Start relaying messages.
async fn relay_messages(data: RelayMessagesParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let source_sign = data.source_sign.to_keypair::<Self::Source>()?;
let source_transactions_mortality = data.source_sign.transactions_mortality()?;
let target_client = data.target.into_client::<Self::Target>().await?;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
let target_transactions_mortality = data.target_sign.transactions_mortality()?;
crate::messages_lane::run::<Self::MessagesLane>(MessagesRelayParams {
source_client,
source_transaction_params: TransactionParams {
signer: source_sign,
mortality: source_transactions_mortality,
},
target_client,
target_transaction_params: TransactionParams {
signer: target_sign,
mortality: target_transactions_mortality,
},
source_to_target_headers_relay: None,
target_to_source_headers_relay: None,
lane_id: data.lane.into(),
limits: Self::maybe_messages_limits(),
metrics_params: data.prometheus_params.into_metrics_params()?,
})
.await
.map_err(|e| anyhow::format_err!("{}", e))
}
}
@@ -0,0 +1,91 @@
// 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/>.
//! Primitives for exposing the parachains finality relaying functionality in the CLI.
use async_std::sync::Mutex;
use async_trait::async_trait;
use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient};
use relay_substrate_client::Parachain;
use relay_utils::metrics::{GlobalMetrics, StandaloneMetric};
use std::sync::Arc;
use structopt::StructOpt;
use crate::{
cli::{
bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge},
chain_schema::*,
PrometheusParams,
},
parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter},
TransactionParams,
};
/// Parachains heads relaying params.
#[derive(StructOpt)]
pub struct RelayParachainsParams {
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
/// Trait used for relaying parachains finality between 2 chains.
#[async_trait]
pub trait ParachainsRelayer: ParachainToRelayHeadersCliBridge
where
ParachainsSource<Self::ParachainFinality>:
SourceClient<ParachainsPipelineAdapter<Self::ParachainFinality>>,
ParachainsTarget<Self::ParachainFinality>:
TargetClient<ParachainsPipelineAdapter<Self::ParachainFinality>>,
<Self as CliBridgeBase>::Source: Parachain,
{
/// Start relaying parachains finality.
async fn relay_parachains(data: RelayParachainsParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::SourceRelay>().await?;
let source_client = ParachainsSource::<Self::ParachainFinality>::new(
source_client,
Arc::new(Mutex::new(AvailableHeader::Missing)),
);
let target_transaction_params = TransactionParams {
signer: data.target_sign.to_keypair::<Self::Target>()?,
mortality: data.target_sign.target_transactions_mortality,
};
let target_client = data.target.into_client::<Self::Target>().await?;
let target_client = ParachainsTarget::<Self::ParachainFinality>::new(
target_client.clone(),
target_transaction_params,
);
let metrics_params: relay_utils::metrics::MetricsParams =
data.prometheus_params.into_metrics_params()?;
GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?;
parachains_relay::parachains_loop::run(
source_client,
target_client,
metrics_params,
futures::future::pending(),
)
.await
.map_err(|e| anyhow::format_err!("{}", e))
}
}
@@ -22,6 +22,7 @@ use relay_substrate_client::{Chain, ChainWithUtilityPallet, UtilityPallet};
use std::marker::PhantomData;
pub mod cli;
pub mod equivocation;
pub mod error;
pub mod finality;