#[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::{fmt::Debug, marker::PhantomData, sync::Arc};
use structopt::StructOpt;
use futures::{FutureExt, TryFutureExt};
use crate::{
cli::{
bridge::{MessagesCliBridge, MessagesLaneIdOf},
DefaultClient, HexLaneId, PrometheusParams,
},
messages::{MessagesRelayLimits, MessagesRelayParams},
on_demand::OnDemandRelay,
HeadersToRelay, TaggedAccount, TransactionParams,
};
use bp_runtime::BalanceOf;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages,
ChainWithRuntimeVersion, ChainWithTransactions,
};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use sp_runtime::traits::TryConvert;
#[derive(Debug, PartialEq, StructOpt)]
pub struct HeadersAndMessagesSharedParams {
#[structopt(long)]
pub lane: Vec<HexLaneId>,
#[structopt(long)]
pub only_mandatory_headers: bool,
#[structopt(long)]
pub only_free_headers: bool,
#[structopt(flatten)]
pub prometheus_params: PrometheusParams,
}
impl HeadersAndMessagesSharedParams {
fn headers_to_relay(&self) -> HeadersToRelay {
match (self.only_mandatory_headers, self.only_free_headers) {
(_, true) => HeadersToRelay::Free,
(true, false) => HeadersToRelay::Mandatory,
_ => HeadersToRelay::All,
}
}
}
pub struct Full2WayBridgeCommonParams<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion,
> {
pub shared: HeadersAndMessagesSharedParams,
pub left: BridgeEndCommonParams<Left>,
pub right: BridgeEndCommonParams<Right>,
pub metrics_params: MetricsParams,
}
impl<
Left: ChainWithTransactions + ChainWithRuntimeVersion,
Right: ChainWithTransactions + ChainWithRuntimeVersion,
> Full2WayBridgeCommonParams<Left, Right>
{
pub fn new<L2R: MessagesCliBridge<Source = Left, Target = Right>>(
shared: HeadersAndMessagesSharedParams,
left: BridgeEndCommonParams<Left>,
right: BridgeEndCommonParams<Right>,
) -> anyhow::Result<Self> {
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 })
}
}
pub struct BridgeEndCommonParams<Chain: ChainWithTransactions + ChainWithRuntimeVersion> {
pub client: DefaultClient<Chain>,
pub tx_params: TransactionParams<AccountKeyPairOf<Chain>>,
pub accounts: Vec<TaggedAccount<AccountIdOf<Chain>>>,
}
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>,
{
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() }
}
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: MessagesLaneIdOf<Bridge>,
maybe_limits: Option<MessagesRelayLimits>,
) -> MessagesRelayParams<Bridge::MessagesLane, DefaultClient<Source>, DefaultClient<Target>> {
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(),
}
}
}
#[async_trait]
pub trait Full2WayBridgeBase: Sized + Send + Sync {
type Params;
type Left: ChainWithTransactions + ChainWithRuntimeVersion;
type Right: ChainWithTransactions + ChainWithRuntimeVersion;
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<Self::Left, Self::Right>>,
Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
)>;
}
#[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>,
{
type Base: Full2WayBridgeBase<Left = Self::Left, Right = Self::Right>;
type Left: ChainWithTransactions
+ ChainWithBalances
+ ChainWithMessages
+ ChainWithRuntimeVersion;
type Right: ChainWithTransactions
+ ChainWithBalances
+ ChainWithMessages
+ ChainWithRuntimeVersion;
type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right>;
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;
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,
)
}
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,
)
}
async fn run(&mut self) -> anyhow::Result<()> {
{
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(),
});
}
let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) =
self.mut_base().start_on_demand_headers_relayers().await?;
let lanes_l2r: Vec<MessagesLaneIdOf<Self::L2R>> = self
.base()
.common()
.shared
.lane
.iter()
.cloned()
.map(HexLaneId::try_convert)
.collect::<Result<Vec<_>, HexLaneId>>()
.map_err(|e| {
anyhow::format_err!("Conversion failed for L2R lanes with error: {:?}!", e)
})?;
let lanes_r2l: Vec<MessagesLaneIdOf<Self::R2L>> = self
.base()
.common()
.shared
.lane
.iter()
.cloned()
.map(HexLaneId::try_convert)
.collect::<Result<Vec<_>, HexLaneId>>()
.map_err(|e| {
anyhow::format_err!("Conversion failed for R2L lanes with error: {:?}!", e)
})?;
{
let common = self.mut_base().mut_common();
crate::messages::metrics::add_relay_balances_metrics::<
_,
Self::Right,
MessagesLaneIdOf<Self::L2R>,
>(
common.left.client.clone(), &common.metrics_params, &common.left.accounts, &lanes_l2r
)
.await?;
crate::messages::metrics::add_relay_balances_metrics::<
_,
Self::Left,
MessagesLaneIdOf<Self::R2L>,
>(
common.right.client.clone(),
&common.metrics_params,
&common.right.accounts,
&lanes_r2l,
)
.await?;
}
let mut message_relays =
Vec::with_capacity(lanes_l2r.len().saturating_add(lanes_r2l.len()));
for lane in lanes_l2r {
let left_to_right_messages =
crate::messages::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);
}
for lane in lanes_r2l {
let right_to_left_messages =
crate::messages::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]
#[allow(dead_code)]
fn should_parse_parachain_to_parachain_options() {
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);
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
);
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",
"0000000000000000000000000000000000000000000000000000000000000000",
"--prometheus-host",
"0.0.0.0",
]);
assert_eq!(
res,
BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages {
shared: HeadersAndMessagesSharedParams {
lane: vec![HexLaneId(vec![0x00u8; 32])],
only_mandatory_headers: false,
only_free_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
prometheus_host: "0.0.0.0".into(),
prometheus_port: 9616,
},
},
left: BridgeHubKusamaConnectionParams {
bridge_hub_kusama_uri: None,
bridge_hub_kusama_host: "bridge-hub-kusama-node-collator1".into(),
bridge_hub_kusama_port: 9944,
bridge_hub_kusama_path: None,
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_uri: None,
kusama_host: "kusama-alice".into(),
kusama_port: 9944,
kusama_path: None,
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_uri: None,
bridge_hub_polkadot_host: "bridge-hub-polkadot-collator1".into(),
bridge_hub_polkadot_port: 9944,
bridge_hub_polkadot_path: None,
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_uri: None,
polkadot_host: "polkadot-alice".into(),
polkadot_port: 9944,
polkadot_path: None,
polkadot_secure: false,
polkadot_runtime_version: PolkadotRuntimeVersionParams {
polkadot_version_mode: RuntimeVersionType::Bundle,
polkadot_spec_version: None,
polkadot_transaction_version: None,
},
},
}
);
}
}