diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 1902875c93810..443c80598c76e 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -34,7 +34,7 @@ use jsonrpsee_ws_client::{ }, WsClient as RpcClient, WsClientBuilder as RpcClientBuilder, }; -use num_traits::{Bounded, Zero}; +use num_traits::{Bounded, CheckedSub, One, Zero}; use pallet_balances::AccountData; use pallet_transaction_payment::InclusionFee; use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId}; @@ -349,7 +349,17 @@ impl Client { let _guard = self.submit_signed_extrinsic_lock.lock().await; let transaction_nonce = self.next_account_index(extrinsic_signer).await?; let best_header = self.best_header().await?; - let best_header_id = HeaderId(*best_header.number(), best_header.hash()); + + // By using parent of best block here, we are protecing again best-block reorganizations. + // E.g. transaction my have been submitted when the best block was `A[num=100]`. Then it has + // been changed to `B[num=100]`. Hash of `A` has been included into transaction signature + // payload. So when signature will be checked, the check will fail and transaction will be + // dropped from the pool. + let best_header_id = match best_header.number().checked_sub(&One::one()) { + Some(parent_block_number) => HeaderId(parent_block_number, *best_header.parent_hash()), + None => HeaderId(*best_header.number(), best_header.hash()), + }; + self.jsonrpsee_execute(move |client| async move { let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce); let tx_hash = Substrate::::author_submit_extrinsic(&*client, extrinsic).await?; diff --git a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs index 5a170d5cbf8bf..5f98d1441d64e 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs @@ -220,7 +220,11 @@ async fn background_task( P::TargetChain::AVERAGE_BLOCK_INTERVAL, ), recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout: STALL_TIMEOUT, + stall_timeout: relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ), only_mandatory_headers, }, MetricsParams::disabled(),