diff --git a/bridges/relays/client-substrate/src/client/rpc.rs b/bridges/relays/client-substrate/src/client/rpc.rs index 7df9aec24770..381946c614ff 100644 --- a/bridges/relays/client-substrate/src/client/rpc.rs +++ b/bridges/relays/client-substrate/src/client/rpc.rs @@ -91,6 +91,15 @@ struct ClientData { client: Arc, } +/// Already encoded value. +struct PreEncoded(Vec); + +impl Encode for PreEncoded { + fn encode(&self) -> Vec { + self.0.clone() + } +} + impl std::fmt::Debug for RpcClient { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_fmt(format_args!("RpcClient<{}>", C::NAME)) @@ -430,6 +439,14 @@ impl Client for RpcClient { } 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_hash().await?; + self.validate_transaction(best_header_hash, PreEncoded(transaction.0.clone())) + .await + .map_err(|e| Error::failed_to_submit_transaction::(e))? + .map_err(|e| Error::failed_to_submit_transaction::(Error::TransactionInvalid(e)))?; + self.jsonrpsee_execute(move |client| async move { let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, transaction) .await @@ -489,14 +506,23 @@ impl Client for RpcClient { 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.hash(), PreEncoded(signed_extrinsic.clone())) + .await + .map_err(|e| Error::failed_to_submit_transaction::(e))? + .map_err(|e| Error::failed_to_submit_transaction::(Error::TransactionInvalid(e)))?; + 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: jsonrpsee::core::client::Subscription<_> = SubstrateAuthorClient::::submit_and_watch_extrinsic(