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 e5b07b241581e..943feba072e40 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::().await?; + let target_client = data.target.into_client::().await?; + let source_sign = data.source_sign.to_keypair::()?; + 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::( + 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 a34b165289b2d..08550d19bae03 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( + source_client: Client, + target_client: Client, + source_transaction_params: TransactionParams>, + at_target_block: HeaderIdOf, + lane_id: LaneId, +) -> anyhow::Result<()> +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + let relayer_id_at_source: AccountIdOf = + source_transaction_params.signer.public().into(); + messages_relay::relay_messages_delivery_confirmation( + SubstrateMessagesSource::

::new( + source_client.clone(), + target_client.clone(), + lane_id, + source_transaction_params, + None, + ), + SubstrateMessagesTarget::

::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 { /// 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 633b11f0b8028..5ffb2b6c771e0 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 { source_client: Client, lane_id: LaneId, relayer_id_at_source: AccountIdOf, - transaction_params: TransactionParams>, + transaction_params: Option>>, source_to_target_headers_relay: Option>>, } @@ -68,7 +68,7 @@ impl SubstrateMessagesTarget

{ source_client: Client, lane_id: LaneId, relayer_id_at_source: AccountIdOf, - transaction_params: TransactionParams>, + transaction_params: Option>>, source_to_target_headers_relay: Option< Arc>, >, @@ -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 7c18b6b148f34..78a3237ba4fe0 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 e6497a1b79eb7..ac4149b22d7b1 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( .await } +/// Relay messages delivery confirmation. +pub async fn relay_messages_delivery_confirmation( + source_client: impl MessageLaneSourceClient

, + target_client: impl MessageLaneTargetClient

, + at: TargetHeaderIdOf

, +) -> 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

(std::marker::PhantomData

);