From 0a752afa8646ae7b5818493f64ceab3011faf236 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky <svyatonik@gmail.com> Date: Mon, 27 May 2024 10:33:14 +0300 Subject: [PATCH] Bridge: add subcommand to relay messages delivery confirmation (#4453) related to https://github.com/paritytech/parity-bridges-common/issues/2962 on top of #4383 Example: ```sh RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ./target/release/substrate-relay relay-messages-delivery-confirmation bridge-hub-rococo-to-bridge-hub-westend \ --source-host localhost \ --source-port 8943 \ --source-version-mode Auto \ --source-signer //Eve \ --source-transactions-mortality 4 \ --target-host localhost \ --target-port 8945 \ --target-version-mode Auto \ --lane 00000002 \ --at-target-block 49 ``` --- .../src/cli/relay_messages.rs | 51 +++++++++++++++++++ .../lib-substrate-relay/src/messages_lane.rs | 42 ++++++++++++++- .../src/messages_target.rs | 19 ++++--- bridges/relays/messages/src/lib.rs | 1 + .../messages/src/message_race_receiving.rs | 39 +++++++++++++- 5 files changed, 143 insertions(+), 9 deletions(-) diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs index e5b07b24158..943feba072e 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs @@ -80,6 +80,24 @@ pub struct RelayMessagesRangeParams { target_sign: TargetSigningParams, } +/// Messages delivery confirmation relaying params. +#[derive(StructOpt)] +pub struct RelayMessagesDeliveryConfirmationParams { + /// Number of the target chain header that we will use to prepare a messages + /// delivery proof. This header must be previously proved to the source chain. + #[structopt(long)] + at_target_block: u128, + /// 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, +} + /// Trait used for relaying messages between 2 chains. #[async_trait] pub trait MessagesRelayer: MessagesCliBridge @@ -154,4 +172,37 @@ where ) .await } + + /// Relay a messages delivery confirmation. + async fn relay_messages_delivery_confirmation( + data: RelayMessagesDeliveryConfirmationParams, + ) -> anyhow::Result<()> { + let source_client = data.source.into_client::<Self::Source>().await?; + let target_client = data.target.into_client::<Self::Target>().await?; + let source_sign = data.source_sign.to_keypair::<Self::Source>()?; + let source_transactions_mortality = data.source_sign.transactions_mortality()?; + + let at_target_block = target_client + .header_by_number(data.at_target_block.unique_saturated_into()) + .await + .map_err(|e| { + log::trace!( + target: "bridge", + "Failed to read {} header with number {}: {e:?}", + Self::Target::NAME, + data.at_target_block, + ); + anyhow::format_err!("The command has failed") + })? + .id(); + + crate::messages_lane::relay_messages_delivery_confirmation::<Self::MessagesLane>( + source_client, + target_client, + TransactionParams { signer: source_sign, mortality: source_transactions_mortality }, + at_target_block, + data.lane.into(), + ) + .await + } } diff --git a/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/bridges/relays/lib-substrate-relay/src/messages_lane.rs index a34b165289b..08550d19bae 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_lane.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_lane.rs @@ -262,7 +262,7 @@ where source_client, params.lane_id, relayer_id_at_source, - params.target_transaction_params, + Some(params.target_transaction_params), params.source_to_target_headers_relay, ), { @@ -307,7 +307,7 @@ where source_client, lane_id, relayer_id_at_source, - target_transaction_params, + Some(target_transaction_params), None, ), at_source_block, @@ -318,6 +318,44 @@ where .map_err(|_| anyhow::format_err!("The command has failed")) } +/// Relay messages delivery confirmation of Substrate-to-Substrate messages. +/// No checks are made to ensure that transaction will succeed. +pub async fn relay_messages_delivery_confirmation<P: SubstrateMessageLane>( + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, + source_transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>, + at_target_block: HeaderIdOf<P::TargetChain>, + lane_id: LaneId, +) -> anyhow::Result<()> +where + AccountIdOf<P::SourceChain>: From<<AccountKeyPairOf<P::SourceChain> as Pair>::Public>, + AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TargetChain> as Pair>::Public>, + BalanceOf<P::SourceChain>: TryFrom<BalanceOf<P::TargetChain>>, +{ + let relayer_id_at_source: AccountIdOf<P::SourceChain> = + source_transaction_params.signer.public().into(); + messages_relay::relay_messages_delivery_confirmation( + SubstrateMessagesSource::<P>::new( + source_client.clone(), + target_client.clone(), + lane_id, + source_transaction_params, + None, + ), + SubstrateMessagesTarget::<P>::new( + target_client, + source_client, + lane_id, + relayer_id_at_source, + None, + None, + ), + at_target_block, + ) + .await + .map_err(|_| anyhow::format_err!("The command has failed")) +} + /// Different ways of building `receive_messages_proof` calls. pub trait ReceiveMessagesProofCallBuilder<P: SubstrateMessageLane> { /// Given messages proof, build call of `receive_messages_proof` function of bridge diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index 633b11f0b80..5ffb2b6c771 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -40,8 +40,8 @@ use messages_relay::{ message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, }; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, Client, Error as SubstrateError, HashOf, - TransactionEra, TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, Chain, Client, Error as SubstrateError, + HashOf, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; @@ -57,7 +57,7 @@ pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> { source_client: Client<P::SourceChain>, lane_id: LaneId, relayer_id_at_source: AccountIdOf<P::SourceChain>, - transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>, + transaction_params: Option<TransactionParams<AccountKeyPairOf<P::TargetChain>>>, source_to_target_headers_relay: Option<Arc<dyn OnDemandRelay<P::SourceChain, P::TargetChain>>>, } @@ -68,7 +68,7 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> { source_client: Client<P::SourceChain>, lane_id: LaneId, relayer_id_at_source: AccountIdOf<P::SourceChain>, - transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>, + transaction_params: Option<TransactionParams<AccountKeyPairOf<P::TargetChain>>>, source_to_target_headers_relay: Option< Arc<dyn OnDemandRelay<P::SourceChain, P::TargetChain>>, >, @@ -249,11 +249,18 @@ where None => messages_proof_call, }; - let transaction_params = self.transaction_params.clone(); + let transaction_params = self.transaction_params.clone().map(Ok).unwrap_or_else(|| { + // this error shall never happen in practice, so it not deserves + // a separate error variant + Err(SubstrateError::Custom(format!( + "Cannot sign transaction of {} chain", + P::TargetChain::NAME, + ))) + })?; let tx_tracker = self .target_client .submit_and_watch_signed_extrinsic( - &self.transaction_params.signer, + &transaction_params.signer, move |best_block_id, transaction_nonce| { Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) diff --git a/bridges/relays/messages/src/lib.rs b/bridges/relays/messages/src/lib.rs index 7c18b6b148f..78a3237ba4f 100644 --- a/bridges/relays/messages/src/lib.rs +++ b/bridges/relays/messages/src/lib.rs @@ -37,3 +37,4 @@ mod message_race_receiving; mod message_race_strategy; pub use message_race_delivery::relay_messages_range; +pub use message_race_receiving::relay_messages_delivery_confirmation; diff --git a/bridges/relays/messages/src/message_race_receiving.rs b/bridges/relays/messages/src/message_race_receiving.rs index e6497a1b79e..ac4149b22d7 100644 --- a/bridges/relays/messages/src/message_race_receiving.rs +++ b/bridges/relays/messages/src/message_race_receiving.rs @@ -30,7 +30,7 @@ use crate::{ use async_trait::async_trait; use bp_messages::MessageNonce; use futures::stream::FusedStream; -use relay_utils::FailedClient; +use relay_utils::{FailedClient, TrackedTransactionStatus, TransactionTracker}; use std::{marker::PhantomData, ops::RangeInclusive}; /// Message receiving confirmations delivery strategy. @@ -69,6 +69,43 @@ pub async fn run<P: MessageLane>( .await } +/// Relay messages delivery confirmation. +pub async fn relay_messages_delivery_confirmation<P: MessageLane>( + source_client: impl MessageLaneSourceClient<P>, + target_client: impl MessageLaneTargetClient<P>, + at: TargetHeaderIdOf<P>, +) -> Result<(), ()> { + // prepare messages delivery proof + let (at, proof) = target_client.prove_messages_receiving(at.clone()).await.map_err(|e| { + log::error!( + target: "bridge", + "Failed to generate messages delivery proof at {:?}: {:?}", + at, + e, + ); + })?; + // submit messages delivery proof to the source node + let tx_tracker = + source_client + .submit_messages_receiving_proof(None, at, proof) + .await + .map_err(|e| { + log::error!( + target: "bridge", + "Failed to submit messages delivery proof: {:?}", + e, + ); + })?; + + match tx_tracker.wait().await { + TrackedTransactionStatus::Finalized(_) => Ok(()), + TrackedTransactionStatus::Lost => { + log::error!("Transaction with messages delivery proof is considered lost"); + Err(()) + }, + } +} + /// Messages receiving confirmations race. struct ReceivingConfirmationsRace<P>(std::marker::PhantomData<P>); -- GitLab