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