diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 688303fb7379..8328e1ce8bec 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -138,6 +138,15 @@ struct ClientData { client: Arc, } +/// Already encoded value. +struct PreEncoded(Vec); + +impl Encode for PreEncoded { + fn encode(&self) -> Vec { + self.0.clone() + } +} + #[async_trait] impl relay_utils::relay_loop::Client for Client { type Error = Error; @@ -438,6 +447,16 @@ impl Client { /// /// Note: The given transaction needs to be SCALE encoded beforehand. pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result { + // one last check that the transaction is valid. Most of checks happen in the relay loop and + // it is the "final" check before submission. + let best_header_hash = self.best_header().await?.hash(); + self.validate_transaction(best_header_hash, PreEncoded(transaction.0.clone())) + .await + .map_err(|e| { + log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); + e + })??; + self.jsonrpsee_execute(move |client| async move { let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, transaction) .await @@ -494,9 +513,19 @@ impl Client { // will be dropped from the pool. let best_header_id = best_header.parent_id().unwrap_or_else(|| best_header.id()); + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + + // one last check that the transaction is valid. Most of checks happen in the relay loop and + // it is the "final" check before submission. + self.validate_transaction(best_header_id.1, PreEncoded(signed_extrinsic.clone())) + .await + .map_err(|e| { + log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); + e + })??; + self.jsonrpsee_execute(move |client| async move { - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; - let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, Bytes(signed_extrinsic)) .await @@ -529,16 +558,27 @@ impl Client { let transaction_nonce = self.next_account_index(signer.public().into()).await?; let best_header = self.best_header().await?; let best_header_id = best_header.id(); + + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let stall_timeout = transaction_stall_timeout( + extrinsic.era.mortality_period(), + C::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + + // one last check that the transaction is valid. Most of checks happen in the relay loop and + // it is the "final" check before submission. + self.validate_transaction(best_header_id.1, PreEncoded(signed_extrinsic.clone())) + .await + .map_err(|e| { + log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); + e + })??; + let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); let (tracker, subscription) = self .jsonrpsee_execute(move |client| async move { - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; - let stall_timeout = transaction_stall_timeout( - extrinsic.era.mortality_period(), - C::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); let tx_hash = C::Hasher::hash(&signed_extrinsic); let subscription = SubstrateAuthorClient::::submit_and_watch_extrinsic( &*client,