diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs index 1425233add1e..00f8cf79ef1f 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs @@ -18,6 +18,8 @@ use async_std::sync::Mutex; use async_trait::async_trait; +use bp_polkadot_core::BlockNumber as RelayBlockNumber; +use bp_runtime::HeaderIdProvider; use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::Parachain; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; @@ -51,6 +53,21 @@ pub struct RelayParachainsParams { prometheus_params: PrometheusParams, } +/// Single parachains head relaying params. +#[derive(StructOpt)] +pub struct RelayParachainHeadParams { + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + /// Prove parachain head at that relay block number. This relay header must be previously + /// proved to the target chain. + #[structopt(long)] + at_relay_block: RelayBlockNumber, +} + /// Trait used for relaying parachains finality between 2 chains. #[async_trait] pub trait ParachainsRelayer: ParachainToRelayHeadersCliBridge @@ -94,4 +111,38 @@ where .await .map_err(|e| anyhow::format_err!("{}", e)) } + + /// Relay single parachain head. No checks are made to ensure that transaction will succeed. + async fn relay_parachain_head(data: RelayParachainHeadParams) -> anyhow::Result<()> { + let source_chain_client = data.source.into_client::().await?; + let at_relay_block = source_chain_client + .header_by_number(data.at_relay_block) + .await + .map_err(|e| anyhow::format_err!("{}", e))? + .id(); + + let source_client = ParachainsSource::::new( + source_chain_client.clone(), + Arc::new(Mutex::new(AvailableHeader::Missing)), + ); + + let target_transaction_params = TransactionParams { + signer: data.target_sign.to_keypair::()?, + mortality: data.target_sign.target_transactions_mortality, + }; + let target_chain_client = data.target.into_client::().await?; + let target_client = ParachainsTarget::::new( + source_chain_client, + target_chain_client, + target_transaction_params, + ); + + parachains_relay::parachains_loop::relay_single_head( + source_client, + target_client, + at_relay_block, + ) + .await + .map_err(|_| anyhow::format_err!("The command has failed")) + } } diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 55f236eeac1d..fd73ca2d46c0 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -139,6 +139,33 @@ pub fn metrics_prefix() -> String { ) } +/// Relay single parachain head. +pub async fn relay_single_head( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + at_relay_block: HeaderIdOf, +) -> Result<(), ()> +where + P::SourceRelayChain: Chain, +{ + let tx_tracker = + submit_selected_head::(&source_client, &target_client, at_relay_block, false) + .await + .map_err(drop)?; + match tx_tracker.wait().await { + TrackedTransactionStatus::Finalized(_) => Ok(()), + TrackedTransactionStatus::Lost => { + log::error!( + "Transaction with {} header at relay header {:?} is considered lost at {}", + P::SourceParachain::NAME, + at_relay_block, + P::TargetChain::NAME, + ); + Err(()) + }, + } +} + /// Run parachain heads synchronization. pub async fn run( source_client: impl SourceClient

, @@ -361,52 +388,63 @@ where ); if is_update_required { - let (head_proof, head_hash) = - source_client.prove_parachain_head(prove_at_relay_block).await.map_err(|e| { - log::warn!( - target: "bridge", - "Failed to prove {} parachain ParaId({}) heads: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - e, - ); - FailedClient::Source - })?; - log::info!( - target: "bridge", - "Submitting {} parachain ParaId({}) head update transaction to {}. Para hash at source relay {:?}: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, + let transaction_tracker = submit_selected_head::( + &source_client, + &target_client, prove_at_relay_block, - head_hash, - ); - - let transaction_tracker = target_client - .submit_parachain_head_proof( - prove_at_relay_block, - head_hash, - head_proof, - only_free_headers, - ) - .await - .map_err(|e| { - log::warn!( - target: "bridge", - "Failed to submit {} parachain ParaId({}) heads proof to {}: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - e, - ); - FailedClient::Target - })?; + only_free_headers, + ) + .await?; submitted_heads_tracker = Some(SubmittedHeadsTracker::

::new(head_at_source, transaction_tracker)); } } } +/// Prove and submit parachain head at given relay chain block. +async fn submit_selected_head>( + source_client: &impl SourceClient

, + target_client: &TC, + prove_at_relay_block: HeaderIdOf, + only_free_headers: bool, +) -> Result { + let (head_proof, head_hash) = + source_client.prove_parachain_head(prove_at_relay_block).await.map_err(|e| { + log::warn!( + target: "bridge", + "Failed to prove {} parachain ParaId({}) heads: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + e, + ); + FailedClient::Source + })?; + log::info!( + target: "bridge", + "Submitting {} parachain ParaId({}) head update transaction to {}. Para hash at source relay {:?}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + prove_at_relay_block, + head_hash, + ); + + target_client + .submit_parachain_head_proof(prove_at_relay_block, head_hash, head_proof, only_free_headers) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to submit {} parachain ParaId({}) heads proof to {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + e, + ); + FailedClient::Target + }) +} + /// Returns `true` if we need to submit parachain-head-update transaction. fn is_update_required( head_at_source: AvailableHeader>,