Skip to content

Commit

Permalink
Bridge: added subcommand to relay single parachain header (#4365)
Browse files Browse the repository at this point in the history
Related to
paritytech/parity-bridges-common#2962
Relay companion:
paritytech/parity-bridges-common#2978

Example usage:
```
./target/release/substrate-relay relay-parachain-head rococo-to-bridge-hub-westend \
    --source-host localhost --source-port 9942 \
    --target-host localhost --target-port 8945 --target-signer //Alice \
    --at-relay-block 61
```
  • Loading branch information
svyatonik authored May 3, 2024
1 parent 8712817 commit 1680977
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 39 deletions.
51 changes: 51 additions & 0 deletions bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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::<Self::SourceRelay>().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::<Self::ParachainFinality>::new(
source_chain_client.clone(),
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_chain_client = data.target.into_client::<Self::Target>().await?;
let target_client = ParachainsTarget::<Self::ParachainFinality>::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"))
}
}
116 changes: 77 additions & 39 deletions bridges/relays/parachains/src/parachains_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,33 @@ pub fn metrics_prefix<P: ParachainsPipeline>() -> String {
)
}

/// Relay single parachain head.
pub async fn relay_single_head<P: ParachainsPipeline>(
source_client: impl SourceClient<P>,
target_client: impl TargetClient<P>,
at_relay_block: HeaderIdOf<P::SourceRelayChain>,
) -> Result<(), ()>
where
P::SourceRelayChain: Chain<BlockNumber = RelayBlockNumber>,
{
let tx_tracker =
submit_selected_head::<P, _>(&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<P: ParachainsPipeline>(
source_client: impl SourceClient<P>,
Expand Down Expand Up @@ -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::<P, _>(
&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::<P>::new(head_at_source, transaction_tracker));
}
}
}

/// Prove and submit parachain head at given relay chain block.
async fn submit_selected_head<P: ParachainsPipeline, TC: TargetClient<P>>(
source_client: &impl SourceClient<P>,
target_client: &TC,
prove_at_relay_block: HeaderIdOf<P::SourceRelayChain>,
only_free_headers: bool,
) -> Result<TC::TransactionTracker, FailedClient> {
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<P: ParachainsPipeline>(
head_at_source: AvailableHeader<HeaderIdOf<P::SourceParachain>>,
Expand Down

0 comments on commit 1680977

Please sign in to comment.