From ab3a3bc2786673bfda47646a20f871b8a2e4d59d Mon Sep 17 00:00:00 2001
From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Date: Wed, 27 Sep 2023 11:58:39 +0200
Subject: [PATCH] `BlockId` removal: `tx-pool` refactor (#1678)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

It changes following APIs:
- trait `ChainApi`
-- `validate_transaction`

- trait `TransactionPool`
--`submit_at`
--`submit_one`
--`submit_and_watch`

and some implementation details, in particular:
- impl `Pool`
--`submit_at`
--`resubmit_at`
--`submit_one`
--`submit_and_watch`
--`prune_known`
--`prune`
--`prune_tags`
--`resolve_block_number`
--`verify`
--`verify_one`

- revalidation queue

All tests are also adjusted.

---------

Co-authored-by: command-bot <>
Co-authored-by: Bastian Köcher <git@kchr.de>
---
 .../service/benches/transaction_throughput.rs |   4 +-
 substrate/bin/node/bench/src/construct.rs     |   8 +-
 substrate/bin/node/bench/src/txpool.rs        |   6 +-
 .../bin/node/cli/benches/transaction_pool.rs  |   4 +-
 .../basic-authorship/src/basic_authorship.rs  |  47 +-
 .../client/consensus/manual-seal/src/lib.rs   |  23 +-
 .../src/transaction/transaction.rs            |   8 +-
 substrate/client/rpc/src/author/mod.rs        |  30 +-
 substrate/client/service/src/lib.rs           |  14 +-
 substrate/client/service/test/src/lib.rs      |  11 +-
 .../client/transaction-pool/api/src/lib.rs    |  11 +-
 .../client/transaction-pool/benches/basics.rs |  37 +-
 substrate/client/transaction-pool/src/api.rs  |  27 +-
 .../client/transaction-pool/src/graph/pool.rs | 168 +++---
 substrate/client/transaction-pool/src/lib.rs  |  44 +-
 .../transaction-pool/src/revalidation.rs      | 109 +++-
 .../client/transaction-pool/src/tests.rs      |  20 +-
 .../client/transaction-pool/tests/pool.rs     | 480 ++++++++++--------
 .../runtime/transaction-pool/src/lib.rs       |  12 +-
 substrate/utils/frame/rpc/system/src/lib.rs   |   6 +-
 20 files changed, 609 insertions(+), 460 deletions(-)

diff --git a/cumulus/test/service/benches/transaction_throughput.rs b/cumulus/test/service/benches/transaction_throughput.rs
index 83981a91d46..81ecc84db7b 100644
--- a/cumulus/test/service/benches/transaction_throughput.rs
+++ b/cumulus/test/service/benches/transaction_throughput.rs
@@ -21,7 +21,7 @@ use cumulus_test_runtime::{AccountId, BalancesCall, ExistentialDeposit, SudoCall
 use futures::{future, StreamExt};
 use sc_transaction_pool_api::{TransactionPool as _, TransactionSource, TransactionStatus};
 use sp_core::{crypto::Pair, sr25519};
-use sp_runtime::{generic::BlockId, OpaqueExtrinsic};
+use sp_runtime::OpaqueExtrinsic;
 
 use cumulus_primitives_core::ParaId;
 use cumulus_test_service::{
@@ -117,7 +117,7 @@ async fn submit_tx_and_wait_for_inclusion(
 	let best_hash = client.chain_info().best_hash;
 
 	let mut watch = tx_pool
-		.submit_and_watch(&BlockId::Hash(best_hash), TransactionSource::External, tx.clone())
+		.submit_and_watch(best_hash, TransactionSource::External, tx.clone())
 		.await
 		.expect("Submits tx to pool")
 		.fuse();
diff --git a/substrate/bin/node/bench/src/construct.rs b/substrate/bin/node/bench/src/construct.rs
index f14f89fcd3a..23d0a0cc1ee 100644
--- a/substrate/bin/node/bench/src/construct.rs
+++ b/substrate/bin/node/bench/src/construct.rs
@@ -35,7 +35,7 @@ use sc_transaction_pool_api::{
 };
 use sp_consensus::{Environment, Proposer};
 use sp_inherents::InherentDataProvider;
-use sp_runtime::{generic::BlockId, traits::NumberFor, OpaqueExtrinsic};
+use sp_runtime::{traits::NumberFor, OpaqueExtrinsic};
 
 use crate::{
 	common::SizeType,
@@ -233,7 +233,7 @@ impl sc_transaction_pool_api::TransactionPool for Transactions {
 	/// Returns a future that imports a bunch of unverified transactions to the pool.
 	fn submit_at(
 		&self,
-		_at: &BlockId<Self::Block>,
+		_at: Self::Hash,
 		_source: TransactionSource,
 		_xts: Vec<TransactionFor<Self>>,
 	) -> PoolFuture<Vec<Result<node_primitives::Hash, Self::Error>>, Self::Error> {
@@ -243,7 +243,7 @@ impl sc_transaction_pool_api::TransactionPool for Transactions {
 	/// Returns a future that imports one unverified transaction to the pool.
 	fn submit_one(
 		&self,
-		_at: &BlockId<Self::Block>,
+		_at: Self::Hash,
 		_source: TransactionSource,
 		_xt: TransactionFor<Self>,
 	) -> PoolFuture<TxHash<Self>, Self::Error> {
@@ -252,7 +252,7 @@ impl sc_transaction_pool_api::TransactionPool for Transactions {
 
 	fn submit_and_watch(
 		&self,
-		_at: &BlockId<Self::Block>,
+		_at: Self::Hash,
 		_source: TransactionSource,
 		_xt: TransactionFor<Self>,
 	) -> PoolFuture<Pin<Box<TransactionStatusStreamFor<Self>>>, Self::Error> {
diff --git a/substrate/bin/node/bench/src/txpool.rs b/substrate/bin/node/bench/src/txpool.rs
index a3524ac5bc8..e56bb559a7b 100644
--- a/substrate/bin/node/bench/src/txpool.rs
+++ b/substrate/bin/node/bench/src/txpool.rs
@@ -27,7 +27,6 @@ use node_testing::bench::{BenchDb, BlockType, DatabaseType, KeyTypes};
 
 use sc_transaction_pool::BasicPool;
 use sc_transaction_pool_api::{TransactionPool, TransactionSource};
-use sp_runtime::generic::BlockId;
 
 use crate::core::{self, Mode, Path};
 
@@ -58,10 +57,11 @@ impl core::BenchmarkDescription for PoolBenchmarkDescription {
 impl core::Benchmark for PoolBenchmark {
 	fn run(&mut self, mode: Mode) -> std::time::Duration {
 		let context = self.database.create_context();
+		let genesis_hash = context.client.chain_info().genesis_hash;
 
 		let _ = context
 			.client
-			.runtime_version_at(context.client.chain_info().genesis_hash)
+			.runtime_version_at(genesis_hash)
 			.expect("Failed to get runtime version")
 			.spec_version;
 
@@ -90,7 +90,7 @@ impl core::Benchmark for PoolBenchmark {
 		let start = std::time::Instant::now();
 		let submissions = generated_transactions
 			.into_iter()
-			.map(|tx| txpool.submit_one(&BlockId::Number(0), TransactionSource::External, tx));
+			.map(|tx| txpool.submit_one(genesis_hash, TransactionSource::External, tx));
 		futures::executor::block_on(futures::future::join_all(submissions));
 		let elapsed = start.elapsed();
 
diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs
index d3e8c02a958..d21edc55bba 100644
--- a/substrate/bin/node/cli/benches/transaction_pool.rs
+++ b/substrate/bin/node/cli/benches/transaction_pool.rs
@@ -34,7 +34,7 @@ use sc_transaction_pool::PoolLimit;
 use sc_transaction_pool_api::{TransactionPool as _, TransactionSource, TransactionStatus};
 use sp_core::{crypto::Pair, sr25519};
 use sp_keyring::Sr25519Keyring;
-use sp_runtime::{generic::BlockId, OpaqueExtrinsic};
+use sp_runtime::OpaqueExtrinsic;
 use tokio::runtime::Handle;
 
 fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
@@ -191,7 +191,7 @@ async fn submit_tx_and_wait_for_inclusion(
 	let best_hash = client.chain_info().best_hash;
 
 	let mut watch = tx_pool
-		.submit_and_watch(&BlockId::Hash(best_hash), TransactionSource::External, tx.clone())
+		.submit_and_watch(best_hash, TransactionSource::External, tx.clone())
 		.await
 		.expect("Submits tx to pool")
 		.fuse();
diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs
index b3a8f0d8970..0fb61b6fab1 100644
--- a/substrate/client/basic-authorship/src/basic_authorship.rs
+++ b/substrate/client/basic-authorship/src/basic_authorship.rs
@@ -642,8 +642,8 @@ mod tests {
 			client.clone(),
 		);
 
-		block_on(txpool.submit_at(&BlockId::number(0), SOURCE, vec![extrinsic(0), extrinsic(1)]))
-			.unwrap();
+		let hashof0 = client.info().genesis_hash;
+		block_on(txpool.submit_at(hashof0, SOURCE, vec![extrinsic(0), extrinsic(1)])).unwrap();
 
 		block_on(
 			txpool.maintain(chain_event(
@@ -658,7 +658,7 @@ mod tests {
 
 		let cell = Mutex::new((false, time::Instant::now()));
 		let proposer = proposer_factory.init_with_now(
-			&client.expect_header(client.info().genesis_hash).unwrap(),
+			&client.expect_header(hashof0).unwrap(),
 			Box::new(move || {
 				let mut value = cell.lock();
 				if !value.0 {
@@ -736,7 +736,7 @@ mod tests {
 
 		let genesis_hash = client.info().best_hash;
 
-		block_on(txpool.submit_at(&BlockId::number(0), SOURCE, vec![extrinsic(0)])).unwrap();
+		block_on(txpool.submit_at(genesis_hash, SOURCE, vec![extrinsic(0)])).unwrap();
 
 		block_on(
 			txpool.maintain(chain_event(
@@ -800,7 +800,7 @@ mod tests {
 		};
 
 		block_on(txpool.submit_at(
-			&BlockId::number(0),
+			client.info().genesis_hash,
 			SOURCE,
 			vec![medium(0), medium(1), huge(2), medium(3), huge(4), medium(5), medium(6)],
 		))
@@ -897,9 +897,8 @@ mod tests {
 			spawner.clone(),
 			client.clone(),
 		);
-		let genesis_header = client
-			.expect_header(client.info().genesis_hash)
-			.expect("there should be header");
+		let genesis_hash = client.info().genesis_hash;
+		let genesis_header = client.expect_header(genesis_hash).expect("there should be header");
 
 		let extrinsics_num = 5;
 		let extrinsics = std::iter::once(
@@ -922,7 +921,7 @@ mod tests {
 				.sum::<usize>() +
 			Vec::<Extrinsic>::new().encoded_size();
 
-		block_on(txpool.submit_at(&BlockId::number(0), SOURCE, extrinsics.clone())).unwrap();
+		block_on(txpool.submit_at(genesis_hash, SOURCE, extrinsics.clone())).unwrap();
 
 		block_on(txpool.maintain(chain_event(genesis_header.clone())));
 
@@ -999,6 +998,7 @@ mod tests {
 			spawner.clone(),
 			client.clone(),
 		);
+		let genesis_hash = client.info().genesis_hash;
 
 		let tiny = |nonce| {
 			ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY)).nonce(nonce).build()
@@ -1011,7 +1011,7 @@ mod tests {
 
 		block_on(
 			txpool.submit_at(
-				&BlockId::number(0),
+				genesis_hash,
 				SOURCE,
 				// add 2 * MAX_SKIPPED_TRANSACTIONS that exhaust resources
 				(0..MAX_SKIPPED_TRANSACTIONS * 2)
@@ -1024,13 +1024,9 @@ mod tests {
 		)
 		.unwrap();
 
-		block_on(
-			txpool.maintain(chain_event(
-				client
-					.expect_header(client.info().genesis_hash)
-					.expect("there should be header"),
-			)),
-		);
+		block_on(txpool.maintain(chain_event(
+			client.expect_header(genesis_hash).expect("there should be header"),
+		)));
 		assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 3);
 
 		let mut proposer_factory =
@@ -1038,7 +1034,7 @@ mod tests {
 
 		let cell = Mutex::new(time::Instant::now());
 		let proposer = proposer_factory.init_with_now(
-			&client.expect_header(client.info().genesis_hash).unwrap(),
+			&client.expect_header(genesis_hash).unwrap(),
 			Box::new(move || {
 				let mut value = cell.lock();
 				let old = *value;
@@ -1071,6 +1067,7 @@ mod tests {
 			spawner.clone(),
 			client.clone(),
 		);
+		let genesis_hash = client.info().genesis_hash;
 
 		let tiny = |who| {
 			ExtrinsicBuilder::new_fill_block(Perbill::from_parts(TINY))
@@ -1086,7 +1083,7 @@ mod tests {
 
 		block_on(
 			txpool.submit_at(
-				&BlockId::number(0),
+				genesis_hash,
 				SOURCE,
 				(0..MAX_SKIPPED_TRANSACTIONS + 2)
 					.into_iter()
@@ -1098,13 +1095,9 @@ mod tests {
 		)
 		.unwrap();
 
-		block_on(
-			txpool.maintain(chain_event(
-				client
-					.expect_header(client.info().genesis_hash)
-					.expect("there should be header"),
-			)),
-		);
+		block_on(txpool.maintain(chain_event(
+			client.expect_header(genesis_hash).expect("there should be header"),
+		)));
 		assert_eq!(txpool.ready().count(), MAX_SKIPPED_TRANSACTIONS * 2 + 4);
 
 		let mut proposer_factory =
@@ -1114,7 +1107,7 @@ mod tests {
 		let cell = Arc::new(Mutex::new((0, time::Instant::now())));
 		let cell2 = cell.clone();
 		let proposer = proposer_factory.init_with_now(
-			&client.expect_header(client.info().genesis_hash).unwrap(),
+			&client.expect_header(genesis_hash).unwrap(),
 			Box::new(move || {
 				let mut value = cell.lock();
 				let (called, old) = *value;
diff --git a/substrate/client/consensus/manual-seal/src/lib.rs b/substrate/client/consensus/manual-seal/src/lib.rs
index 1e5db966e66..41cd5f3127e 100644
--- a/substrate/client/consensus/manual-seal/src/lib.rs
+++ b/substrate/client/consensus/manual-seal/src/lib.rs
@@ -351,7 +351,7 @@ mod tests {
 	use sc_transaction_pool::{BasicPool, FullChainApi, Options, RevalidationType};
 	use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool, TransactionSource};
 	use sp_inherents::InherentData;
-	use sp_runtime::generic::{BlockId, Digest, DigestItem};
+	use sp_runtime::generic::{Digest, DigestItem};
 	use substrate_test_runtime_client::{
 		AccountKeyring::*, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
 	};
@@ -400,10 +400,11 @@ mod tests {
 		let client = Arc::new(client);
 		let spawner = sp_core::testing::TaskExecutor::new();
 		let genesis_hash = client.info().genesis_hash;
+		let pool_api = Arc::new(FullChainApi::new(client.clone(), None, &spawner.clone()));
 		let pool = Arc::new(BasicPool::with_revalidation_type(
 			Options::default(),
 			true.into(),
-			api(),
+			pool_api,
 			None,
 			RevalidationType::Full,
 			spawner.clone(),
@@ -444,7 +445,7 @@ mod tests {
 			rt.block_on(future);
 		});
 		// submit a transaction to pool.
-		let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await;
+		let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await;
 		// assert that it was successfully imported
 		assert!(result.is_ok());
 		// assert that the background task returns ok
@@ -475,10 +476,11 @@ mod tests {
 		let client = Arc::new(client);
 		let spawner = sp_core::testing::TaskExecutor::new();
 		let genesis_hash = client.info().genesis_hash;
+		let pool_api = Arc::new(FullChainApi::new(client.clone(), None, &spawner.clone()));
 		let pool = Arc::new(BasicPool::with_revalidation_type(
 			Options::default(),
 			true.into(),
-			api(),
+			pool_api,
 			None,
 			RevalidationType::Full,
 			spawner.clone(),
@@ -535,7 +537,7 @@ mod tests {
 
 		let mut finality_stream = client.finality_notification_stream();
 		// submit a transaction to pool.
-		let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await;
+		let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await;
 		// assert that it was successfully imported
 		assert!(result.is_ok());
 		// assert that the background task returns ok
@@ -571,10 +573,11 @@ mod tests {
 		let client = Arc::new(client);
 		let spawner = sp_core::testing::TaskExecutor::new();
 		let genesis_hash = client.info().genesis_hash;
+		let pool_api = Arc::new(FullChainApi::new(client.clone(), None, &spawner.clone()));
 		let pool = Arc::new(BasicPool::with_revalidation_type(
 			Options::default(),
 			true.into(),
-			api(),
+			pool_api,
 			None,
 			RevalidationType::Full,
 			spawner.clone(),
@@ -602,7 +605,7 @@ mod tests {
 			rt.block_on(future);
 		});
 		// submit a transaction to pool.
-		let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await;
+		let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await;
 		// assert that it was successfully imported
 		assert!(result.is_ok());
 		let (tx, rx) = futures::channel::oneshot::channel();
@@ -688,7 +691,7 @@ mod tests {
 			rt.block_on(future);
 		});
 		// submit a transaction to pool.
-		let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await;
+		let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await;
 		// assert that it was successfully imported
 		assert!(result.is_ok());
 
@@ -719,7 +722,7 @@ mod tests {
 			}
 		);
 
-		assert!(pool.submit_one(&BlockId::Number(1), SOURCE, uxt(Alice, 1)).await.is_ok());
+		assert!(pool.submit_one(created_block.hash, SOURCE, uxt(Alice, 1)).await.is_ok());
 
 		let header = client.header(created_block.hash).expect("db error").expect("imported above");
 		assert_eq!(header.number, 1);
@@ -741,7 +744,7 @@ mod tests {
 			.is_ok());
 		assert_matches::assert_matches!(rx1.await.expect("should be no error receiving"), Ok(_));
 
-		assert!(pool.submit_one(&BlockId::Number(1), SOURCE, uxt(Bob, 0)).await.is_ok());
+		assert!(pool.submit_one(created_block.hash, SOURCE, uxt(Bob, 0)).await.is_ok());
 		let (tx2, rx2) = futures::channel::oneshot::channel();
 		assert!(sink
 			.send(EngineCommand::SealNewBlock {
diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs
index 44f4bd36c8b..fe16310aeff 100644
--- a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs
+++ b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs
@@ -46,7 +46,7 @@ use std::sync::Arc;
 use sp_api::ProvideRuntimeApi;
 use sp_blockchain::HeaderBackend;
 use sp_core::Bytes;
-use sp_runtime::{generic, traits::Block as BlockT};
+use sp_runtime::traits::Block as BlockT;
 
 use codec::Decode;
 use futures::{FutureExt, StreamExt, TryFutureExt};
@@ -110,11 +110,7 @@ where
 
 		let submit = self
 			.pool
-			.submit_and_watch(
-				&generic::BlockId::hash(best_block_hash),
-				TX_SOURCE,
-				decoded_extrinsic,
-			)
+			.submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic)
 			.map_err(|e| {
 				e.into_pool_error()
 					.map(Error::from)
diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs
index feee22641ef..55d0a504aa6 100644
--- a/substrate/client/rpc/src/author/mod.rs
+++ b/substrate/client/rpc/src/author/mod.rs
@@ -41,7 +41,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi};
 use sp_blockchain::HeaderBackend;
 use sp_core::Bytes;
 use sp_keystore::{KeystoreExt, KeystorePtr};
-use sp_runtime::{generic, traits::Block as BlockT};
+use sp_runtime::traits::Block as BlockT;
 use sp_session::SessionKeys;
 
 use self::error::{Error, Result};
@@ -97,15 +97,12 @@ where
 			Err(err) => return Err(Error::Client(Box::new(err)).into()),
 		};
 		let best_block_hash = self.client.info().best_hash;
-		self.pool
-			.submit_one(&generic::BlockId::hash(best_block_hash), TX_SOURCE, xt)
-			.await
-			.map_err(|e| {
-				e.into_pool_error()
-					.map(|e| Error::Pool(e))
-					.unwrap_or_else(|e| Error::Verification(Box::new(e)))
-					.into()
-			})
+		self.pool.submit_one(best_block_hash, TX_SOURCE, xt).await.map_err(|e| {
+			e.into_pool_error()
+				.map(|e| Error::Pool(e))
+				.unwrap_or_else(|e| Error::Verification(Box::new(e)))
+				.into()
+		})
 	}
 
 	fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()> {
@@ -191,14 +188,11 @@ where
 			},
 		};
 
-		let submit = self
-			.pool
-			.submit_and_watch(&generic::BlockId::hash(best_block_hash), TX_SOURCE, dxt)
-			.map_err(|e| {
-				e.into_pool_error()
-					.map(error::Error::from)
-					.unwrap_or_else(|e| error::Error::Verification(Box::new(e)))
-			});
+		let submit = self.pool.submit_and_watch(best_block_hash, TX_SOURCE, dxt).map_err(|e| {
+			e.into_pool_error()
+				.map(error::Error::from)
+				.unwrap_or_else(|e| error::Error::Verification(Box::new(e)))
+		});
 
 		let fut = async move {
 			let stream = match submit.await {
diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs
index cd720e1c1e0..ff9eb982b86 100644
--- a/substrate/client/service/src/lib.rs
+++ b/substrate/client/service/src/lib.rs
@@ -48,10 +48,7 @@ use sc_network_sync::SyncingService;
 use sc_utils::mpsc::TracingUnboundedReceiver;
 use sp_blockchain::HeaderMetadata;
 use sp_consensus::SyncOracle;
-use sp_runtime::{
-	generic::BlockId,
-	traits::{Block as BlockT, Header as HeaderT},
-};
+use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
 
 pub use self::{
 	builder::{
@@ -481,10 +478,8 @@ where
 			},
 		};
 
-		let best_block_id = BlockId::hash(self.client.info().best_hash);
-
 		let import_future = self.pool.submit_one(
-			&best_block_id,
+			self.client.info().best_hash,
 			sc_transaction_pool_api::TransactionSource::External,
 			uxt,
 		);
@@ -549,10 +544,9 @@ mod tests {
 			to: AccountKeyring::Bob.into(),
 		}
 		.into_unchecked_extrinsic();
-		block_on(pool.submit_one(&BlockId::hash(best.hash()), source, transaction.clone()))
-			.unwrap();
+		block_on(pool.submit_one(best.hash(), source, transaction.clone())).unwrap();
 		block_on(pool.submit_one(
-			&BlockId::hash(best.hash()),
+			best.hash(),
 			source,
 			ExtrinsicBuilder::new_call_do_not_propagate().nonce(1).build(),
 		))
diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs
index 38a811acc74..9700c7643c4 100644
--- a/substrate/client/service/test/src/lib.rs
+++ b/substrate/client/service/test/src/lib.rs
@@ -34,7 +34,6 @@ use sc_service::{
 	RuntimeGenesis, SpawnTaskHandle, TaskManager,
 };
 use sc_transaction_pool_api::TransactionPool;
-use sp_api::BlockId;
 use sp_blockchain::HeaderBackend;
 use sp_runtime::traits::Block as BlockT;
 use std::{iter, net::Ipv4Addr, pin::Pin, sync::Arc, task::Context, time::Duration};
@@ -501,15 +500,13 @@ pub fn sync<G, E, Fb, F, B, ExF, U>(
 	info!("Checking extrinsic propagation");
 	let first_service = network.full_nodes[0].1.clone();
 	let first_user_data = &network.full_nodes[0].2;
-	let best_block = BlockId::number(first_service.client().info().best_number);
+	let best_block = first_service.client().info().best_hash;
 	let extrinsic = extrinsic_factory(&first_service, first_user_data);
 	let source = sc_transaction_pool_api::TransactionSource::External;
 
-	futures::executor::block_on(first_service.transaction_pool().submit_one(
-		&best_block,
-		source,
-		extrinsic,
-	))
+	futures::executor::block_on(
+		first_service.transaction_pool().submit_one(best_block, source, extrinsic),
+	)
 	.expect("failed to submit extrinsic");
 
 	network.run_until_all_full(|_index, service| service.transaction_pool().ready().count() == 1);
diff --git a/substrate/client/transaction-pool/api/src/lib.rs b/substrate/client/transaction-pool/api/src/lib.rs
index 73cc513708d..a795917528f 100644
--- a/substrate/client/transaction-pool/api/src/lib.rs
+++ b/substrate/client/transaction-pool/api/src/lib.rs
@@ -26,10 +26,7 @@ use codec::Codec;
 use futures::{Future, Stream};
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use sp_core::offchain::TransactionPoolExt;
-use sp_runtime::{
-	generic::BlockId,
-	traits::{Block as BlockT, Member, NumberFor},
-};
+use sp_runtime::traits::{Block as BlockT, Member, NumberFor};
 use std::{collections::HashMap, hash::Hash, marker::PhantomData, pin::Pin, sync::Arc};
 
 const LOG_TARGET: &str = "txpool::api";
@@ -202,7 +199,7 @@ pub trait TransactionPool: Send + Sync {
 	/// Returns a future that imports a bunch of unverified transactions to the pool.
 	fn submit_at(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xts: Vec<TransactionFor<Self>>,
 	) -> PoolFuture<Vec<Result<TxHash<Self>, Self::Error>>, Self::Error>;
@@ -210,7 +207,7 @@ pub trait TransactionPool: Send + Sync {
 	/// Returns a future that imports one unverified transaction to the pool.
 	fn submit_one(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: TransactionFor<Self>,
 	) -> PoolFuture<TxHash<Self>, Self::Error>;
@@ -219,7 +216,7 @@ pub trait TransactionPool: Send + Sync {
 	/// pool.
 	fn submit_and_watch(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: TransactionFor<Self>,
 	) -> PoolFuture<Pin<Box<TransactionStatusStreamFor<Self>>>, Self::Error>;
diff --git a/substrate/client/transaction-pool/benches/basics.rs b/substrate/client/transaction-pool/benches/basics.rs
index d114acc343d..0caf00bf295 100644
--- a/substrate/client/transaction-pool/benches/basics.rs
+++ b/substrate/client/transaction-pool/benches/basics.rs
@@ -33,6 +33,7 @@ use sp_runtime::{
 		ValidTransaction,
 	},
 };
+use std::sync::Arc;
 use substrate_test_runtime::{AccountId, Block, Extrinsic, ExtrinsicBuilder, TransferData, H256};
 
 #[derive(Clone, Debug, Default)]
@@ -61,7 +62,7 @@ impl ChainApi for TestApi {
 
 	fn validate_transaction(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		_source: TransactionSource,
 		uxt: <Self::Block as BlockT>::Extrinsic,
 	) -> Self::ValidationFuture {
@@ -70,7 +71,7 @@ impl ChainApi for TestApi {
 		let nonce = transfer.nonce;
 		let from = transfer.from;
 
-		match self.block_id_to_number(at) {
+		match self.block_id_to_number(&BlockId::Hash(at)) {
 			Ok(Some(num)) if num > 5 => return ready(Ok(Err(InvalidTransaction::Stale.into()))),
 			_ => {},
 		}
@@ -94,6 +95,8 @@ impl ChainApi for TestApi {
 	) -> Result<Option<NumberFor<Self::Block>>, Self::Error> {
 		Ok(match at {
 			BlockId::Number(num) => Some(*num),
+			BlockId::Hash(hash) if *hash == H256::from_low_u64_be(hash.to_low_u64_be()) =>
+				Some(hash.to_low_u64_be()),
 			BlockId::Hash(_) => None,
 		})
 	}
@@ -104,7 +107,7 @@ impl ChainApi for TestApi {
 	) -> Result<Option<<Self::Block as BlockT>::Hash>, Self::Error> {
 		Ok(match at {
 			BlockId::Number(num) => Some(H256::from_low_u64_be(*num)).into(),
-			BlockId::Hash(_) => None,
+			BlockId::Hash(hash) => Some(*hash),
 		})
 	}
 
@@ -137,7 +140,7 @@ fn uxt(transfer: TransferData) -> Extrinsic {
 	ExtrinsicBuilder::new_bench_call(transfer).build()
 }
 
-fn bench_configured(pool: Pool<TestApi>, number: u64) {
+fn bench_configured(pool: Pool<TestApi>, number: u64, api: Arc<TestApi>) {
 	let source = TransactionSource::External;
 	let mut futures = Vec::new();
 	let mut tags = Vec::new();
@@ -151,7 +154,12 @@ fn bench_configured(pool: Pool<TestApi>, number: u64) {
 		});
 
 		tags.push(to_tag(nonce, AccountId::from_h256(H256::from_low_u64_be(1))));
-		futures.push(pool.submit_one(&BlockId::Number(1), source, xt));
+
+		futures.push(pool.submit_one(
+			api.block_id_to_hash(&BlockId::Number(1)).unwrap().unwrap(),
+			source,
+			xt,
+		));
 	}
 
 	let res = block_on(futures::future::join_all(futures.into_iter()));
@@ -162,7 +170,12 @@ fn bench_configured(pool: Pool<TestApi>, number: u64) {
 
 	// Prune all transactions.
 	let block_num = 6;
-	block_on(pool.prune_tags(&BlockId::Number(block_num), tags, vec![])).expect("Prune failed");
+	block_on(pool.prune_tags(
+		api.block_id_to_hash(&BlockId::Number(block_num)).unwrap().unwrap(),
+		tags,
+		vec![],
+	))
+	.expect("Prune failed");
 
 	// pool is empty
 	assert_eq!(pool.validated_pool().status().ready, 0);
@@ -172,19 +185,15 @@ fn bench_configured(pool: Pool<TestApi>, number: u64) {
 fn benchmark_main(c: &mut Criterion) {
 	c.bench_function("sequential 50 tx", |b| {
 		b.iter(|| {
-			bench_configured(
-				Pool::new(Default::default(), true.into(), TestApi::new_dependant().into()),
-				50,
-			);
+			let api = Arc::from(TestApi::new_dependant());
+			bench_configured(Pool::new(Default::default(), true.into(), api.clone()), 50, api);
 		});
 	});
 
 	c.bench_function("random 100 tx", |b| {
 		b.iter(|| {
-			bench_configured(
-				Pool::new(Default::default(), true.into(), TestApi::default().into()),
-				100,
-			);
+			let api = Arc::from(TestApi::default());
+			bench_configured(Pool::new(Default::default(), true.into(), api.clone()), 100, api);
 		});
 	});
 }
diff --git a/substrate/client/transaction-pool/src/api.rs b/substrate/client/transaction-pool/src/api.rs
index 871d8e9c817..cccaad7c899 100644
--- a/substrate/client/transaction-pool/src/api.rs
+++ b/substrate/client/transaction-pool/src/api.rs
@@ -133,13 +133,12 @@ where
 
 	fn validate_transaction(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		uxt: graph::ExtrinsicFor<Self>,
 	) -> Self::ValidationFuture {
 		let (tx, rx) = oneshot::channel();
 		let client = self.client.clone();
-		let at = *at;
 		let validation_pool = self.validation_pool.clone();
 		let metrics = self.metrics.clone();
 
@@ -151,7 +150,7 @@ where
 				.await
 				.send(
 					async move {
-						let res = validate_transaction_blocking(&*client, &at, source, uxt);
+						let res = validate_transaction_blocking(&*client, at, source, uxt);
 						let _ = tx.send(res);
 						metrics.report(|m| m.validations_finished.inc());
 					}
@@ -209,7 +208,7 @@ where
 /// This method will call into the runtime to perform the validation.
 fn validate_transaction_blocking<Client, Block>(
 	client: &Client,
-	at: &BlockId<Block>,
+	at: Block::Hash,
 	source: TransactionSource,
 	uxt: graph::ExtrinsicFor<FullChainApi<Client, Block>>,
 ) -> error::Result<TransactionValidity>
@@ -225,14 +224,10 @@ where
 {
 	sp_tracing::within_span!(sp_tracing::Level::TRACE, "validate_transaction";
 	{
-		let block_hash = client.to_hash(at)
-			.map_err(|e| Error::RuntimeApi(e.to_string()))?
-			.ok_or_else(|| Error::RuntimeApi(format!("Could not get hash for block `{:?}`.", at)))?;
-
 		let runtime_api = client.runtime_api();
 		let api_version = sp_tracing::within_span! { sp_tracing::Level::TRACE, "check_version";
 			runtime_api
-				.api_version::<dyn TaggedTransactionQueue<Block>>(block_hash)
+				.api_version::<dyn TaggedTransactionQueue<Block>>(at)
 				.map_err(|e| Error::RuntimeApi(e.to_string()))?
 				.ok_or_else(|| Error::RuntimeApi(
 					format!("Could not find `TaggedTransactionQueue` api for block `{:?}`.", at)
@@ -245,31 +240,31 @@ where
 			sp_tracing::Level::TRACE, "runtime::validate_transaction";
 		{
 			if api_version >= 3 {
-				runtime_api.validate_transaction(block_hash, source, uxt, block_hash)
+				runtime_api.validate_transaction(at, source, uxt, at)
 					.map_err(|e| Error::RuntimeApi(e.to_string()))
 			} else {
-				let block_number = client.to_number(at)
+				let block_number = client.to_number(&BlockId::Hash(at))
 					.map_err(|e| Error::RuntimeApi(e.to_string()))?
 					.ok_or_else(||
 						Error::RuntimeApi(format!("Could not get number for block `{:?}`.", at))
 					)?;
 
 				// The old versions require us to call `initialize_block` before.
-				runtime_api.initialize_block(block_hash, &sp_runtime::traits::Header::new(
+				runtime_api.initialize_block(at, &sp_runtime::traits::Header::new(
 					block_number + sp_runtime::traits::One::one(),
 					Default::default(),
 					Default::default(),
-					block_hash,
+					at,
 					Default::default()),
 				).map_err(|e| Error::RuntimeApi(e.to_string()))?;
 
 				if api_version == 2 {
 					#[allow(deprecated)] // old validate_transaction
-					runtime_api.validate_transaction_before_version_3(block_hash, source, uxt)
+					runtime_api.validate_transaction_before_version_3(at, source, uxt)
 						.map_err(|e| Error::RuntimeApi(e.to_string()))
 				} else {
 					#[allow(deprecated)] // old validate_transaction
-					runtime_api.validate_transaction_before_version_2(block_hash, uxt)
+					runtime_api.validate_transaction_before_version_2(at, uxt)
 						.map_err(|e| Error::RuntimeApi(e.to_string()))
 				}
 			}
@@ -294,7 +289,7 @@ where
 	/// the runtime locally.
 	pub fn validate_transaction_blocking(
 		&self,
-		at: &BlockId<Block>,
+		at: Block::Hash,
 		source: TransactionSource,
 		uxt: graph::ExtrinsicFor<Self>,
 	) -> error::Result<TransactionValidity> {
diff --git a/substrate/client/transaction-pool/src/graph/pool.rs b/substrate/client/transaction-pool/src/graph/pool.rs
index 4d34737a7ba..5305b5f1c12 100644
--- a/substrate/client/transaction-pool/src/graph/pool.rs
+++ b/substrate/client/transaction-pool/src/graph/pool.rs
@@ -71,7 +71,7 @@ pub trait ChainApi: Send + Sync {
 	/// Verify extrinsic at given block.
 	fn validate_transaction(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		uxt: ExtrinsicFor<Self>,
 	) -> Self::ValidationFuture;
@@ -154,7 +154,7 @@ impl<B: ChainApi> Pool<B> {
 	/// Imports a bunch of unverified extrinsics to the pool
 	pub async fn submit_at(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xts: impl IntoIterator<Item = ExtrinsicFor<B>>,
 	) -> Result<Vec<Result<ExtrinsicHash<B>, B::Error>>, B::Error> {
@@ -168,7 +168,7 @@ impl<B: ChainApi> Pool<B> {
 	/// This does not check if a transaction is banned, before we verify it again.
 	pub async fn resubmit_at(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xts: impl IntoIterator<Item = ExtrinsicFor<B>>,
 	) -> Result<Vec<Result<ExtrinsicHash<B>, B::Error>>, B::Error> {
@@ -180,7 +180,7 @@ impl<B: ChainApi> Pool<B> {
 	/// Imports one unverified extrinsic to the pool
 	pub async fn submit_one(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: ExtrinsicFor<B>,
 	) -> Result<ExtrinsicHash<B>, B::Error> {
@@ -191,11 +191,11 @@ impl<B: ChainApi> Pool<B> {
 	/// Import a single extrinsic and starts to watch its progress in the pool.
 	pub async fn submit_and_watch(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: ExtrinsicFor<B>,
 	) -> Result<Watcher<ExtrinsicHash<B>, ExtrinsicHash<B>>, B::Error> {
-		let block_number = self.resolve_block_number(at)?;
+		let block_number = self.resolve_block_number(&BlockId::Hash(at))?;
 		let (_, tx) = self
 			.verify_one(at, block_number, source, xt, CheckBannedBeforeVerify::Yes)
 			.await;
@@ -246,8 +246,8 @@ impl<B: ChainApi> Pool<B> {
 	/// their provided tags from there. Otherwise we query the runtime at the `parent` block.
 	pub async fn prune(
 		&self,
-		at: &BlockId<B::Block>,
-		parent: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
+		parent: <B::Block as BlockT>::Hash,
 		extrinsics: &[ExtrinsicFor<B>],
 	) -> Result<(), B::Error> {
 		log::debug!(
@@ -324,7 +324,7 @@ impl<B: ChainApi> Pool<B> {
 	/// prevent importing them in the (near) future.
 	pub async fn prune_tags(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		tags: impl IntoIterator<Item = Tag>,
 		known_imported_hashes: impl IntoIterator<Item = ExtrinsicHash<B>> + Clone,
 	) -> Result<(), B::Error> {
@@ -351,7 +351,7 @@ impl<B: ChainApi> Pool<B> {
 		// And finally - submit reverified transactions back to the pool
 
 		self.validated_pool.resubmit_pruned(
-			at,
+			&BlockId::Hash(at),
 			known_imported_hashes,
 			pruned_hashes,
 			reverified_transactions.into_values().collect(),
@@ -373,12 +373,12 @@ impl<B: ChainApi> Pool<B> {
 	/// Returns future that validates a bunch of transactions at given block.
 	async fn verify(
 		&self,
-		at: &BlockId<B::Block>,
+		at: <B::Block as BlockT>::Hash,
 		xts: impl IntoIterator<Item = (TransactionSource, ExtrinsicFor<B>)>,
 		check: CheckBannedBeforeVerify,
 	) -> Result<HashMap<ExtrinsicHash<B>, ValidatedTransactionFor<B>>, B::Error> {
 		// we need a block number to compute tx validity
-		let block_number = self.resolve_block_number(at)?;
+		let block_number = self.resolve_block_number(&BlockId::Hash(at))?;
 
 		let res = futures::future::join_all(
 			xts.into_iter()
@@ -394,7 +394,7 @@ impl<B: ChainApi> Pool<B> {
 	/// Returns future that validates single transaction at given block.
 	async fn verify_one(
 		&self,
-		block_id: &BlockId<B::Block>,
+		block_hash: <B::Block as BlockT>::Hash,
 		block_number: NumberFor<B>,
 		source: TransactionSource,
 		xt: ExtrinsicFor<B>,
@@ -410,7 +410,7 @@ impl<B: ChainApi> Pool<B> {
 		let validation_result = self
 			.validated_pool
 			.api()
-			.validate_transaction(block_id, source, xt.clone())
+			.validate_transaction(block_hash, source, xt.clone())
 			.await;
 
 		let status = match validation_result {
@@ -458,6 +458,7 @@ mod tests {
 	use super::{super::base_pool::Limit, *};
 	use crate::tests::{pool, uxt, TestApi, INVALID_NONCE};
 	use assert_matches::assert_matches;
+	use codec::Encode;
 	use futures::executor::block_on;
 	use parking_lot::Mutex;
 	use sc_transaction_pool_api::TransactionStatus;
@@ -471,11 +472,11 @@ mod tests {
 	#[test]
 	fn should_validate_and_import_transaction() {
 		// given
-		let pool = pool();
+		let (pool, api) = pool();
 
 		// when
 		let hash = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			api.expect_hash_from_number(0),
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -493,7 +494,7 @@ mod tests {
 	#[test]
 	fn should_reject_if_temporarily_banned() {
 		// given
-		let pool = pool();
+		let (pool, api) = pool();
 		let uxt = uxt(Transfer {
 			from: Alice.into(),
 			to: AccountId::from_h256(H256::from_low_u64_be(2)),
@@ -503,7 +504,7 @@ mod tests {
 
 		// when
 		pool.validated_pool.ban(&Instant::now(), vec![pool.hash_of(&uxt)]);
-		let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, uxt));
+		let res = block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt));
 		assert_eq!(pool.validated_pool().status().ready, 0);
 		assert_eq!(pool.validated_pool().status().future, 0);
 
@@ -514,18 +515,19 @@ mod tests {
 	#[test]
 	fn should_reject_unactionable_transactions() {
 		// given
+		let api = Arc::new(TestApi::default());
 		let pool = Pool::new(
 			Default::default(),
 			// the node does not author blocks
 			false.into(),
-			TestApi::default().into(),
+			api.clone(),
 		);
 
 		// after validation `IncludeData` will be set to non-propagable (validate_transaction mock)
 		let uxt = ExtrinsicBuilder::new_include_data(vec![42]).build();
 
 		// when
-		let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, uxt));
+		let res = block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt));
 
 		// then
 		assert_matches!(res.unwrap_err(), error::Error::Unactionable);
@@ -535,12 +537,13 @@ mod tests {
 	fn should_notify_about_pool_events() {
 		let (stream, hash0, hash1) = {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
+			let hash_of_block0 = api.expect_hash_from_number(0);
 			let stream = pool.validated_pool().import_notification_stream();
 
 			// when
 			let hash0 = block_on(pool.submit_one(
-				&BlockId::Number(0),
+				hash_of_block0,
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -551,7 +554,7 @@ mod tests {
 			))
 			.unwrap();
 			let hash1 = block_on(pool.submit_one(
-				&BlockId::Number(0),
+				hash_of_block0,
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -563,7 +566,7 @@ mod tests {
 			.unwrap();
 			// future doesn't count
 			let _hash = block_on(pool.submit_one(
-				&BlockId::Number(0),
+				hash_of_block0,
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -590,9 +593,10 @@ mod tests {
 	#[test]
 	fn should_clear_stale_transactions() {
 		// given
-		let pool = pool();
+		let (pool, api) = pool();
+		let hash_of_block0 = api.expect_hash_from_number(0);
 		let hash1 = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			hash_of_block0,
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -603,7 +607,7 @@ mod tests {
 		))
 		.unwrap();
 		let hash2 = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			hash_of_block0,
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -614,7 +618,7 @@ mod tests {
 		))
 		.unwrap();
 		let hash3 = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			hash_of_block0,
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -641,9 +645,9 @@ mod tests {
 	#[test]
 	fn should_ban_mined_transactions() {
 		// given
-		let pool = pool();
+		let (pool, api) = pool();
 		let hash1 = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			api.expect_hash_from_number(0),
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -655,12 +659,12 @@ mod tests {
 		.unwrap();
 
 		// when
-		block_on(pool.prune_tags(&BlockId::Number(1), vec![vec![0]], vec![hash1])).unwrap();
+		block_on(pool.prune_tags(api.expect_hash_from_number(1), vec![vec![0]], vec![hash1]))
+			.unwrap();
 
 		// then
 		assert!(pool.validated_pool.is_banned(&hash1));
 	}
-	use codec::Encode;
 
 	#[test]
 	fn should_limit_futures() {
@@ -678,14 +682,15 @@ mod tests {
 
 		let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
 
-		let pool = Pool::new(options, true.into(), TestApi::default().into());
+		let api = Arc::new(TestApi::default());
+		let pool = Pool::new(options, true.into(), api.clone());
 
-		let hash1 = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
+		let hash1 = block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt)).unwrap();
 		assert_eq!(pool.validated_pool().status().future, 1);
 
 		// when
 		let hash2 = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			api.expect_hash_from_number(0),
 			SOURCE,
 			uxt(Transfer {
 				from: Bob.into(),
@@ -709,11 +714,12 @@ mod tests {
 
 		let options = Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
 
-		let pool = Pool::new(options, true.into(), TestApi::default().into());
+		let api = Arc::new(TestApi::default());
+		let pool = Pool::new(options, true.into(), api.clone());
 
 		// when
 		block_on(pool.submit_one(
-			&BlockId::Number(0),
+			api.expect_hash_from_number(0),
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -732,11 +738,11 @@ mod tests {
 	#[test]
 	fn should_reject_transactions_with_no_provides() {
 		// given
-		let pool = pool();
+		let (pool, api) = pool();
 
 		// when
 		let err = block_on(pool.submit_one(
-			&BlockId::Number(0),
+			api.expect_hash_from_number(0),
 			SOURCE,
 			uxt(Transfer {
 				from: Alice.into(),
@@ -759,9 +765,9 @@ mod tests {
 		#[test]
 		fn should_trigger_ready_and_finalized() {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
 			let watcher = block_on(pool.submit_and_watch(
-				&BlockId::Number(0),
+				api.expect_hash_from_number(0),
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -774,26 +780,25 @@ mod tests {
 			assert_eq!(pool.validated_pool().status().ready, 1);
 			assert_eq!(pool.validated_pool().status().future, 0);
 
+			let hash_of_block2 = api.expect_hash_from_number(2);
+
 			// when
-			block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![])).unwrap();
+			block_on(pool.prune_tags(hash_of_block2, vec![vec![0u8]], vec![])).unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 0);
 			assert_eq!(pool.validated_pool().status().future, 0);
 
 			// then
 			let mut stream = futures::executor::block_on_stream(watcher.into_stream());
 			assert_eq!(stream.next(), Some(TransactionStatus::Ready));
-			assert_eq!(
-				stream.next(),
-				Some(TransactionStatus::InBlock((H256::from_low_u64_be(2).into(), 0))),
-			);
+			assert_eq!(stream.next(), Some(TransactionStatus::InBlock((hash_of_block2.into(), 0))),);
 		}
 
 		#[test]
 		fn should_trigger_ready_and_finalized_when_pruning_via_hash() {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
 			let watcher = block_on(pool.submit_and_watch(
-				&BlockId::Number(0),
+				api.expect_hash_from_number(0),
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -806,8 +811,10 @@ mod tests {
 			assert_eq!(pool.validated_pool().status().ready, 1);
 			assert_eq!(pool.validated_pool().status().future, 0);
 
+			let hash_of_block2 = api.expect_hash_from_number(2);
+
 			// when
-			block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![*watcher.hash()]))
+			block_on(pool.prune_tags(hash_of_block2, vec![vec![0u8]], vec![*watcher.hash()]))
 				.unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 0);
 			assert_eq!(pool.validated_pool().status().future, 0);
@@ -815,18 +822,17 @@ mod tests {
 			// then
 			let mut stream = futures::executor::block_on_stream(watcher.into_stream());
 			assert_eq!(stream.next(), Some(TransactionStatus::Ready));
-			assert_eq!(
-				stream.next(),
-				Some(TransactionStatus::InBlock((H256::from_low_u64_be(2).into(), 0))),
-			);
+			assert_eq!(stream.next(), Some(TransactionStatus::InBlock((hash_of_block2.into(), 0))),);
 		}
 
 		#[test]
 		fn should_trigger_future_and_ready_after_promoted() {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
+			let hash_of_block0 = api.expect_hash_from_number(0);
+
 			let watcher = block_on(pool.submit_and_watch(
-				&BlockId::Number(0),
+				hash_of_block0,
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -841,7 +847,7 @@ mod tests {
 
 			// when
 			block_on(pool.submit_one(
-				&BlockId::Number(0),
+				hash_of_block0,
 				SOURCE,
 				uxt(Transfer {
 					from: Alice.into(),
@@ -862,7 +868,7 @@ mod tests {
 		#[test]
 		fn should_trigger_invalid_and_ban() {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
 			let uxt = uxt(Transfer {
 				from: Alice.into(),
 				to: AccountId::from_h256(H256::from_low_u64_be(2)),
@@ -870,7 +876,8 @@ mod tests {
 				nonce: 0,
 			});
 			let watcher =
-				block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, uxt)).unwrap();
+				block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, uxt))
+					.unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 1);
 
 			// when
@@ -886,7 +893,7 @@ mod tests {
 		#[test]
 		fn should_trigger_broadcasted() {
 			// given
-			let pool = pool();
+			let (pool, api) = pool();
 			let uxt = uxt(Transfer {
 				from: Alice.into(),
 				to: AccountId::from_h256(H256::from_low_u64_be(2)),
@@ -894,7 +901,8 @@ mod tests {
 				nonce: 0,
 			});
 			let watcher =
-				block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, uxt)).unwrap();
+				block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, uxt))
+					.unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 1);
 
 			// when
@@ -916,7 +924,8 @@ mod tests {
 			let options =
 				Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
 
-			let pool = Pool::new(options, true.into(), TestApi::default().into());
+			let api = Arc::new(TestApi::default());
+			let pool = Pool::new(options, true.into(), api.clone());
 
 			let xt = uxt(Transfer {
 				from: Alice.into(),
@@ -924,7 +933,9 @@ mod tests {
 				amount: 5,
 				nonce: 0,
 			});
-			let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, xt)).unwrap();
+			let watcher =
+				block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt))
+					.unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 1);
 
 			// when
@@ -934,7 +945,7 @@ mod tests {
 				amount: 4,
 				nonce: 1,
 			});
-			block_on(pool.submit_one(&BlockId::Number(1), SOURCE, xt)).unwrap();
+			block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt)).unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 1);
 
 			// then
@@ -951,12 +962,13 @@ mod tests {
 				let options =
 					Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
 
-				let pool = Pool::new(options, true.into(), TestApi::default().into());
+				let api = Arc::new(TestApi::default());
+				let pool = Pool::new(options, true.into(), api.clone());
 
 				// after validation `IncludeData` will have priority set to 9001
 				// (validate_transaction mock)
 				let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
-				block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
+				block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt)).unwrap();
 				assert_eq!(pool.validated_pool().status().ready, 1);
 
 				// then
@@ -968,7 +980,7 @@ mod tests {
 					amount: 4,
 					nonce: 1,
 				});
-				let result = block_on(pool.submit_one(&BlockId::Number(1), SOURCE, xt));
+				let result = block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt));
 				assert!(matches!(
 					result,
 					Err(sc_transaction_pool_api::error::Error::ImmediatelyDropped)
@@ -980,12 +992,15 @@ mod tests {
 				let options =
 					Options { ready: limit.clone(), future: limit.clone(), ..Default::default() };
 
-				let pool = Pool::new(options, true.into(), TestApi::default().into());
+				let api = Arc::new(TestApi::default());
+				let pool = Pool::new(options, true.into(), api.clone());
+
+				let hash_of_block0 = api.expect_hash_from_number(0);
 
 				// after validation `IncludeData` will have priority set to 9001
 				// (validate_transaction mock)
 				let xt = ExtrinsicBuilder::new_include_data(Vec::new()).build();
-				block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, xt)).unwrap();
+				block_on(pool.submit_and_watch(hash_of_block0, SOURCE, xt)).unwrap();
 				assert_eq!(pool.validated_pool().status().ready, 1);
 
 				// after validation `Transfer` will have priority set to 4 (validate_transaction
@@ -996,15 +1011,14 @@ mod tests {
 					amount: 5,
 					nonce: 0,
 				});
-				let watcher =
-					block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, xt)).unwrap();
+				let watcher = block_on(pool.submit_and_watch(hash_of_block0, SOURCE, xt)).unwrap();
 				assert_eq!(pool.validated_pool().status().ready, 2);
 
 				// when
 				// after validation `Store` will have priority set to 9001 (validate_transaction
 				// mock)
 				let xt = ExtrinsicBuilder::new_indexed_call(Vec::new()).build();
-				block_on(pool.submit_one(&BlockId::Number(1), SOURCE, xt)).unwrap();
+				block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt)).unwrap();
 				assert_eq!(pool.validated_pool().status().ready, 2);
 
 				// then
@@ -1021,7 +1035,10 @@ mod tests {
 			let (tx, rx) = std::sync::mpsc::sync_channel(1);
 			let mut api = TestApi::default();
 			api.delay = Arc::new(Mutex::new(rx.into()));
-			let pool = Arc::new(Pool::new(Default::default(), true.into(), api.into()));
+			let api = Arc::new(api);
+			let pool = Arc::new(Pool::new(Default::default(), true.into(), api.clone()));
+
+			let hash_of_block0 = api.expect_hash_from_number(0);
 
 			// when
 			let xt = uxt(Transfer {
@@ -1034,7 +1051,7 @@ mod tests {
 			// This transaction should go to future, since we use `nonce: 1`
 			let pool2 = pool.clone();
 			std::thread::spawn(move || {
-				block_on(pool2.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
+				block_on(pool2.submit_one(hash_of_block0, SOURCE, xt)).unwrap();
 				ready.send(()).unwrap();
 			});
 
@@ -1048,12 +1065,13 @@ mod tests {
 			});
 			// The tag the above transaction provides (TestApi is using just nonce as u8)
 			let provides = vec![0_u8];
-			block_on(pool.submit_one(&BlockId::Number(0), SOURCE, xt)).unwrap();
+			block_on(pool.submit_one(hash_of_block0, SOURCE, xt)).unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 1);
 
 			// Now block import happens before the second transaction is able to finish
 			// verification.
-			block_on(pool.prune_tags(&BlockId::Number(1), vec![provides], vec![])).unwrap();
+			block_on(pool.prune_tags(api.expect_hash_from_number(1), vec![provides], vec![]))
+				.unwrap();
 			assert_eq!(pool.validated_pool().status().ready, 0);
 
 			// so when we release the verification of the previous one it will have
diff --git a/substrate/client/transaction-pool/src/lib.rs b/substrate/client/transaction-pool/src/lib.rs
index ffaab89d982..faa3f455a58 100644
--- a/substrate/client/transaction-pool/src/lib.rs
+++ b/substrate/client/transaction-pool/src/lib.rs
@@ -166,8 +166,11 @@ where
 		finalized_hash: Block::Hash,
 	) -> (Self, Pin<Box<dyn Future<Output = ()> + Send>>) {
 		let pool = Arc::new(graph::Pool::new(Default::default(), true.into(), pool_api.clone()));
-		let (revalidation_queue, background_task) =
-			revalidation::RevalidationQueue::new_background(pool_api.clone(), pool.clone());
+		let (revalidation_queue, background_task) = revalidation::RevalidationQueue::new_background(
+			pool_api.clone(),
+			pool.clone(),
+			finalized_hash,
+		);
 		(
 			Self {
 				api: pool_api,
@@ -203,8 +206,11 @@ where
 			RevalidationType::Light =>
 				(revalidation::RevalidationQueue::new(pool_api.clone(), pool.clone()), None),
 			RevalidationType::Full => {
-				let (queue, background) =
-					revalidation::RevalidationQueue::new_background(pool_api.clone(), pool.clone());
+				let (queue, background) = revalidation::RevalidationQueue::new_background(
+					pool_api.clone(),
+					pool.clone(),
+					finalized_hash,
+				);
 				(queue, Some(background))
 			},
 		};
@@ -254,46 +260,43 @@ where
 
 	fn submit_at(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xts: Vec<TransactionFor<Self>>,
 	) -> PoolFuture<Vec<Result<TxHash<Self>, Self::Error>>, Self::Error> {
 		let pool = self.pool.clone();
-		let at = *at;
 
 		self.metrics
 			.report(|metrics| metrics.submitted_transactions.inc_by(xts.len() as u64));
 
-		async move { pool.submit_at(&at, source, xts).await }.boxed()
+		async move { pool.submit_at(at, source, xts).await }.boxed()
 	}
 
 	fn submit_one(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: TransactionFor<Self>,
 	) -> PoolFuture<TxHash<Self>, Self::Error> {
 		let pool = self.pool.clone();
-		let at = *at;
 
 		self.metrics.report(|metrics| metrics.submitted_transactions.inc());
 
-		async move { pool.submit_one(&at, source, xt).await }.boxed()
+		async move { pool.submit_one(at, source, xt).await }.boxed()
 	}
 
 	fn submit_and_watch(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		source: TransactionSource,
 		xt: TransactionFor<Self>,
 	) -> PoolFuture<Pin<Box<TransactionStatusStreamFor<Self>>>, Self::Error> {
-		let at = *at;
 		let pool = self.pool.clone();
 
 		self.metrics.report(|metrics| metrics.submitted_transactions.inc());
 
 		async move {
-			let watcher = pool.submit_and_watch(&at, source, xt).await?;
+			let watcher = pool.submit_and_watch(at, source, xt).await?;
 
 			Ok(watcher.into_stream().boxed())
 		}
@@ -433,11 +436,7 @@ where
 
 		let validity = self
 			.api
-			.validate_transaction_blocking(
-				&BlockId::hash(at),
-				TransactionSource::Local,
-				xt.clone(),
-			)?
+			.validate_transaction_blocking(at, TransactionSource::Local, xt.clone())?
 			.map_err(|e| {
 				Self::Error::Pool(match e {
 					TransactionValidityError::Invalid(i) => TxPoolError::InvalidTransaction(i),
@@ -577,10 +576,7 @@ async fn prune_known_txs_for_block<Block: BlockT, Api: graph::ChainApi<Block = B
 		},
 	};
 
-	if let Err(e) = pool
-		.prune(&BlockId::Hash(block_hash), &BlockId::hash(*header.parent_hash()), &extrinsics)
-		.await
-	{
+	if let Err(e) = pool.prune(block_hash, *header.parent_hash(), &extrinsics).await {
 		log::error!("Cannot prune known in the pool: {}", e);
 	}
 
@@ -691,7 +687,7 @@ where
 
 			if let Err(e) = pool
 				.resubmit_at(
-					&BlockId::Hash(*hash),
+					*hash,
 					// These transactions are coming from retracted blocks, we should
 					// simply consider them external.
 					TransactionSource::External,
@@ -717,7 +713,7 @@ where
 
 		if next_action.revalidate {
 			let hashes = pool.validated_pool().ready().map(|tx| tx.hash).collect();
-			self.revalidation_queue.revalidate_later(*block_number, hashes).await;
+			self.revalidation_queue.revalidate_later(*hash, hashes).await;
 
 			self.revalidation_strategy.lock().clear();
 		}
diff --git a/substrate/client/transaction-pool/src/revalidation.rs b/substrate/client/transaction-pool/src/revalidation.rs
index b2c41be92ee..488ab19d8ea 100644
--- a/substrate/client/transaction-pool/src/revalidation.rs
+++ b/substrate/client/transaction-pool/src/revalidation.rs
@@ -25,14 +25,12 @@ use std::{
 };
 
 use crate::{
-	graph::{ChainApi, ExtrinsicHash, NumberFor, Pool, ValidatedTransaction},
+	graph::{BlockHash, ChainApi, ExtrinsicHash, Pool, ValidatedTransaction},
 	LOG_TARGET,
 };
 use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
 use sp_runtime::{
-	generic::BlockId,
-	traits::{SaturatedConversion, Zero},
-	transaction_validity::TransactionValidityError,
+	generic::BlockId, traits::SaturatedConversion, transaction_validity::TransactionValidityError,
 };
 
 use futures::prelude::*;
@@ -44,7 +42,7 @@ const MIN_BACKGROUND_REVALIDATION_BATCH_SIZE: usize = 20;
 
 /// Payload from queue to worker.
 struct WorkerPayload<Api: ChainApi> {
-	at: NumberFor<Api>,
+	at: BlockHash<Api>,
 	transactions: Vec<ExtrinsicHash<Api>>,
 }
 
@@ -54,9 +52,9 @@ struct WorkerPayload<Api: ChainApi> {
 struct RevalidationWorker<Api: ChainApi> {
 	api: Arc<Api>,
 	pool: Arc<Pool<Api>>,
-	best_block: NumberFor<Api>,
-	block_ordered: BTreeMap<NumberFor<Api>, HashSet<ExtrinsicHash<Api>>>,
-	members: HashMap<ExtrinsicHash<Api>, NumberFor<Api>>,
+	best_block: BlockHash<Api>,
+	block_ordered: BTreeMap<BlockHash<Api>, HashSet<ExtrinsicHash<Api>>>,
+	members: HashMap<ExtrinsicHash<Api>, BlockHash<Api>>,
 }
 
 impl<Api: ChainApi> Unpin for RevalidationWorker<Api> {}
@@ -68,15 +66,30 @@ impl<Api: ChainApi> Unpin for RevalidationWorker<Api> {}
 async fn batch_revalidate<Api: ChainApi>(
 	pool: Arc<Pool<Api>>,
 	api: Arc<Api>,
-	at: NumberFor<Api>,
+	at: BlockHash<Api>,
 	batch: impl IntoIterator<Item = ExtrinsicHash<Api>>,
 ) {
+	// This conversion should work. Otherwise, for unknown block the revalidation shall be skipped,
+	// all the transactions will be kept in the validated pool, and can be scheduled for
+	// revalidation with the next request.
+	let block_number = match api.block_id_to_number(&BlockId::Hash(at)) {
+		Ok(Some(n)) => n,
+		Ok(None) => {
+			log::debug!(target: LOG_TARGET, "revalidation skipped at block {at:?}, could not get block number.");
+			return
+		},
+		Err(e) => {
+			log::debug!(target: LOG_TARGET, "revalidation skipped at block {at:?}: {e:?}.");
+			return
+		},
+	};
+
 	let mut invalid_hashes = Vec::new();
 	let mut revalidated = HashMap::new();
 
 	let validation_results = futures::future::join_all(batch.into_iter().filter_map(|ext_hash| {
 		pool.validated_pool().ready_by_hash(&ext_hash).map(|ext| {
-			api.validate_transaction(&BlockId::Number(at), ext.source, ext.data.clone())
+			api.validate_transaction(at, ext.source, ext.data.clone())
 				.map(move |validation_result| (validation_result, ext_hash, ext))
 		})
 	}))
@@ -107,7 +120,7 @@ async fn batch_revalidate<Api: ChainApi>(
 				revalidated.insert(
 					ext_hash,
 					ValidatedTransaction::valid_at(
-						at.saturated_into::<u64>(),
+						block_number.saturated_into::<u64>(),
 						ext_hash,
 						ext.source,
 						ext.data.clone(),
@@ -135,13 +148,13 @@ async fn batch_revalidate<Api: ChainApi>(
 }
 
 impl<Api: ChainApi> RevalidationWorker<Api> {
-	fn new(api: Arc<Api>, pool: Arc<Pool<Api>>) -> Self {
+	fn new(api: Arc<Api>, pool: Arc<Pool<Api>>, best_block: BlockHash<Api>) -> Self {
 		Self {
 			api,
 			pool,
+			best_block,
 			block_ordered: Default::default(),
 			members: Default::default(),
-			best_block: Zero::zero(),
 		}
 	}
 
@@ -303,10 +316,11 @@ where
 		api: Arc<Api>,
 		pool: Arc<Pool<Api>>,
 		interval: Duration,
+		best_block: BlockHash<Api>,
 	) -> (Self, Pin<Box<dyn Future<Output = ()> + Send>>) {
 		let (to_worker, from_queue) = tracing_unbounded("mpsc_revalidation_queue", 100_000);
 
-		let worker = RevalidationWorker::new(api.clone(), pool.clone());
+		let worker = RevalidationWorker::new(api.clone(), pool.clone(), best_block);
 
 		let queue = Self { api, pool, background: Some(to_worker) };
 
@@ -317,8 +331,9 @@ where
 	pub fn new_background(
 		api: Arc<Api>,
 		pool: Arc<Pool<Api>>,
+		best_block: BlockHash<Api>,
 	) -> (Self, Pin<Box<dyn Future<Output = ()> + Send>>) {
-		Self::new_with_interval(api, pool, BACKGROUND_REVALIDATION_INTERVAL)
+		Self::new_with_interval(api, pool, BACKGROUND_REVALIDATION_INTERVAL, best_block)
 	}
 
 	/// Queue some transaction for later revalidation.
@@ -328,7 +343,7 @@ where
 	/// revalidation is actually done.
 	pub async fn revalidate_later(
 		&self,
-		at: NumberFor<Api>,
+		at: BlockHash<Api>,
 		transactions: Vec<ExtrinsicHash<Api>>,
 	) {
 		if transactions.len() > 0 {
@@ -360,9 +375,8 @@ mod tests {
 	};
 	use futures::executor::block_on;
 	use sc_transaction_pool_api::TransactionSource;
-	use sp_runtime::generic::BlockId;
 	use substrate_test_runtime::{AccountId, Transfer, H256};
-	use substrate_test_runtime_client::AccountKeyring::Alice;
+	use substrate_test_runtime_client::AccountKeyring::{Alice, Bob};
 
 	#[test]
 	fn revalidation_queue_works() {
@@ -376,18 +390,63 @@ mod tests {
 			amount: 5,
 			nonce: 0,
 		});
-		let uxt_hash = block_on(pool.submit_one(
-			&BlockId::number(0),
-			TransactionSource::External,
-			uxt.clone(),
-		))
-		.expect("Should be valid");
 
-		block_on(queue.revalidate_later(0, vec![uxt_hash]));
+		let hash_of_block0 = api.expect_hash_from_number(0);
+
+		let uxt_hash =
+			block_on(pool.submit_one(hash_of_block0, TransactionSource::External, uxt.clone()))
+				.expect("Should be valid");
+
+		block_on(queue.revalidate_later(hash_of_block0, vec![uxt_hash]));
 
 		// revalidated in sync offload 2nd time
 		assert_eq!(api.validation_requests().len(), 2);
 		// number of ready
 		assert_eq!(pool.validated_pool().status().ready, 1);
 	}
+
+	#[test]
+	fn revalidation_queue_skips_revalidation_for_unknown_block_hash() {
+		let api = Arc::new(TestApi::default());
+		let pool = Arc::new(Pool::new(Default::default(), true.into(), api.clone()));
+		let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone()));
+
+		let uxt0 = uxt(Transfer {
+			from: Alice.into(),
+			to: AccountId::from_h256(H256::from_low_u64_be(2)),
+			amount: 5,
+			nonce: 0,
+		});
+		let uxt1 = uxt(Transfer {
+			from: Bob.into(),
+			to: AccountId::from_h256(H256::from_low_u64_be(2)),
+			amount: 4,
+			nonce: 1,
+		});
+
+		let hash_of_block0 = api.expect_hash_from_number(0);
+		let unknown_block = H256::repeat_byte(0x13);
+
+		let uxt_hashes =
+			block_on(pool.submit_at(hash_of_block0, TransactionSource::External, vec![uxt0, uxt1]))
+				.expect("Should be valid")
+				.into_iter()
+				.map(|r| r.expect("Should be valid"))
+				.collect::<Vec<_>>();
+
+		assert_eq!(api.validation_requests().len(), 2);
+		assert_eq!(pool.validated_pool().status().ready, 2);
+
+		// revalidation works fine for block 0:
+		block_on(queue.revalidate_later(hash_of_block0, uxt_hashes.clone()));
+		assert_eq!(api.validation_requests().len(), 4);
+		assert_eq!(pool.validated_pool().status().ready, 2);
+
+		// revalidation shall be skipped for unknown block:
+		block_on(queue.revalidate_later(unknown_block, uxt_hashes));
+		// no revalidation shall be done
+		assert_eq!(api.validation_requests().len(), 4);
+		// number of ready shall not change
+		assert_eq!(pool.validated_pool().status().ready, 2);
+	}
 }
diff --git a/substrate/client/transaction-pool/src/tests.rs b/substrate/client/transaction-pool/src/tests.rs
index 62911d5cbb4..325add3fb1c 100644
--- a/substrate/client/transaction-pool/src/tests.rs
+++ b/substrate/client/transaction-pool/src/tests.rs
@@ -32,7 +32,7 @@ use sp_runtime::{
 };
 use std::{collections::HashSet, sync::Arc};
 use substrate_test_runtime::{
-	substrate_test_pallet::pallet::Call as PalletCall, BalancesCall, Block, Extrinsic,
+	substrate_test_pallet::pallet::Call as PalletCall, BalancesCall, Block, BlockNumber, Extrinsic,
 	ExtrinsicBuilder, Hashing, RuntimeCall, Transfer, TransferData, H256,
 };
 
@@ -53,6 +53,11 @@ impl TestApi {
 	pub fn validation_requests(&self) -> Vec<Extrinsic> {
 		self.validation_requests.lock().clone()
 	}
+
+	/// Helper function for mapping block number to hash. Use if mapping shall not fail.
+	pub fn expect_hash_from_number(&self, n: BlockNumber) -> H256 {
+		self.block_id_to_hash(&BlockId::Number(n)).unwrap().unwrap()
+	}
 }
 
 impl ChainApi for TestApi {
@@ -64,13 +69,13 @@ impl ChainApi for TestApi {
 	/// Verify extrinsic at given block.
 	fn validate_transaction(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		_source: TransactionSource,
 		uxt: ExtrinsicFor<Self>,
 	) -> Self::ValidationFuture {
 		self.validation_requests.lock().push(uxt.clone());
 		let hash = self.hash_and_length(&uxt).0;
-		let block_number = self.block_id_to_number(at).unwrap().unwrap();
+		let block_number = self.block_id_to_number(&BlockId::Hash(at)).unwrap().unwrap();
 
 		let res = match uxt {
 			Extrinsic {
@@ -153,6 +158,8 @@ impl ChainApi for TestApi {
 	) -> Result<Option<NumberFor<Self>>, Self::Error> {
 		Ok(match at {
 			BlockId::Number(num) => Some(*num),
+			BlockId::Hash(hash) if *hash == H256::from_low_u64_be(hash.to_low_u64_be()) =>
+				Some(hash.to_low_u64_be()),
 			BlockId::Hash(_) => None,
 		})
 	}
@@ -164,7 +171,7 @@ impl ChainApi for TestApi {
 	) -> Result<Option<<Self::Block as BlockT>::Hash>, Self::Error> {
 		Ok(match at {
 			BlockId::Number(num) => Some(H256::from_low_u64_be(*num)).into(),
-			BlockId::Hash(_) => None,
+			BlockId::Hash(hash) => Some(*hash),
 		})
 	}
 
@@ -199,6 +206,7 @@ pub(crate) fn uxt(transfer: Transfer) -> Extrinsic {
 	ExtrinsicBuilder::new_transfer(transfer).build()
 }
 
-pub(crate) fn pool() -> Pool<TestApi> {
-	Pool::new(Default::default(), true.into(), TestApi::default().into())
+pub(crate) fn pool() -> (Pool<TestApi>, Arc<TestApi>) {
+	let api = Arc::new(TestApi::default());
+	(Pool::new(Default::default(), true.into(), api.clone()), api)
 }
diff --git a/substrate/client/transaction-pool/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs
index 4adf811b425..e5ab01ffbf0 100644
--- a/substrate/client/transaction-pool/tests/pool.rs
+++ b/substrate/client/transaction-pool/tests/pool.rs
@@ -47,8 +47,9 @@ use substrate_test_runtime_transaction_pool::{uxt, TestApi};
 
 const LOG_TARGET: &str = "txpool";
 
-fn pool() -> Pool<TestApi> {
-	Pool::new(Default::default(), true.into(), TestApi::with_alice_nonce(209).into())
+fn pool() -> (Pool<TestApi>, Arc<TestApi>) {
+	let api = Arc::new(TestApi::with_alice_nonce(209));
+	(Pool::new(Default::default(), true.into(), api.clone()), api)
 }
 
 fn maintained_pool() -> (BasicPool<TestApi, Block>, Arc<TestApi>, futures::executor::ThreadPool) {
@@ -83,8 +84,8 @@ const SOURCE: TransactionSource = TransactionSource::External;
 
 #[test]
 fn submission_should_work() {
-	let pool = pool();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
+	let (pool, api) = pool();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 209))).unwrap();
 
 	let pending: Vec<_> = pool
 		.validated_pool()
@@ -96,9 +97,9 @@ fn submission_should_work() {
 
 #[test]
 fn multiple_submission_should_work() {
-	let pool = pool();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
+	let (pool, api) = pool();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 209))).unwrap();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 210))).unwrap();
 
 	let pending: Vec<_> = pool
 		.validated_pool()
@@ -111,8 +112,8 @@ fn multiple_submission_should_work() {
 #[test]
 fn early_nonce_should_be_culled() {
 	sp_tracing::try_init_simple();
-	let pool = pool();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 208))).unwrap();
+	let (pool, api) = pool();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 208))).unwrap();
 
 	let pending: Vec<_> = pool
 		.validated_pool()
@@ -124,9 +125,9 @@ fn early_nonce_should_be_culled() {
 
 #[test]
 fn late_nonce_should_be_queued() {
-	let pool = pool();
+	let (pool, api) = pool();
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 210))).unwrap();
 	let pending: Vec<_> = pool
 		.validated_pool()
 		.ready()
@@ -134,7 +135,7 @@ fn late_nonce_should_be_queued() {
 		.collect();
 	assert_eq!(pending, Vec::<Nonce>::new());
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 209))).unwrap();
 	let pending: Vec<_> = pool
 		.validated_pool()
 		.ready()
@@ -145,9 +146,10 @@ fn late_nonce_should_be_queued() {
 
 #[test]
 fn prune_tags_should_work() {
-	let pool = pool();
-	let hash209 = block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 209))).unwrap();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt(Alice, 210))).unwrap();
+	let (pool, api) = pool();
+	let hash209 =
+		block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 209))).unwrap();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt(Alice, 210))).unwrap();
 
 	let pending: Vec<_> = pool
 		.validated_pool()
@@ -157,7 +159,7 @@ fn prune_tags_should_work() {
 	assert_eq!(pending, vec![209, 210]);
 
 	pool.validated_pool().api().push_block(1, Vec::new(), true);
-	block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![hash209]))
+	block_on(pool.prune_tags(api.expect_hash_from_number(1), vec![vec![209]], vec![hash209]))
 		.expect("Prune tags");
 
 	let pending: Vec<_> = pool
@@ -170,11 +172,12 @@ fn prune_tags_should_work() {
 
 #[test]
 fn should_ban_invalid_transactions() {
-	let pool = pool();
+	let (pool, api) = pool();
 	let uxt = uxt(Alice, 209);
-	let hash = block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt.clone())).unwrap();
+	let hash =
+		block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt.clone())).unwrap();
 	pool.validated_pool().remove_invalid(&[hash]);
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt.clone())).unwrap_err();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt.clone())).unwrap_err();
 
 	// when
 	let pending: Vec<_> = pool
@@ -185,7 +188,7 @@ fn should_ban_invalid_transactions() {
 	assert_eq!(pending, Vec::<Nonce>::new());
 
 	// then
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, uxt.clone())).unwrap_err();
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, uxt.clone())).unwrap_err();
 }
 
 #[test]
@@ -193,9 +196,9 @@ fn only_prune_on_new_best() {
 	let (pool, api, _) = maintained_pool();
 	let uxt = uxt(Alice, 209);
 
-	let _ = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, uxt.clone()))
+	let _ = block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, uxt.clone()))
 		.expect("1. Imported");
-	pool.api().push_block(1, vec![uxt.clone()], true);
+	api.push_block(1, vec![uxt.clone()], true);
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(2, vec![uxt], true);
@@ -212,13 +215,15 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() {
 	}));
 	let pool = Pool::new(Default::default(), true.into(), api.clone());
 	let xt = uxt(Alice, 209);
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.validated_pool().status().ready, 1);
 
 	// remove the transaction that just got imported.
 	api.increment_nonce(Alice.into());
 	api.push_block(1, Vec::new(), true);
-	block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![])).expect("1. Pruned");
+	block_on(pool.prune_tags(api.expect_hash_from_number(1), vec![vec![209]], vec![]))
+		.expect("1. Pruned");
 	assert_eq!(pool.validated_pool().status().ready, 0);
 	// it's re-imported to future
 	assert_eq!(pool.validated_pool().status().future, 1);
@@ -227,7 +232,8 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() {
 	api.increment_nonce(Alice.into());
 	api.push_block(2, Vec::new(), true);
 	let xt = uxt(Alice, 211);
-	block_on(pool.submit_one(&BlockId::number(2), SOURCE, xt.clone())).expect("2. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(2), SOURCE, xt.clone()))
+		.expect("2. Imported");
 	assert_eq!(pool.validated_pool().status().ready, 1);
 	assert_eq!(pool.validated_pool().status().future, 1);
 	let pending: Vec<_> = pool
@@ -240,7 +246,8 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() {
 	// prune it and make sure the pool is empty
 	api.increment_nonce(Alice.into());
 	api.push_block(3, Vec::new(), true);
-	block_on(pool.prune_tags(&BlockId::number(3), vec![vec![155]], vec![])).expect("2. Pruned");
+	block_on(pool.prune_tags(api.expect_hash_from_number(3), vec![vec![155]], vec![]))
+		.expect("2. Pruned");
 	assert_eq!(pool.validated_pool().status().ready, 0);
 	assert_eq!(pool.validated_pool().status().future, 2);
 }
@@ -270,7 +277,8 @@ fn should_prune_old_during_maintenance() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![xt.clone()], true);
@@ -285,9 +293,11 @@ fn should_revalidate_during_maintenance() {
 	let xt2 = uxt(Alice, 210);
 
 	let (pool, api, _guard) = maintained_pool();
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, xt2.clone()))
-		.expect("2. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt1.clone()))
+		.expect("1. Imported");
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt2.clone()))
+			.expect("2. Imported");
 	assert_eq!(pool.status().ready, 2);
 	assert_eq!(api.validation_requests().len(), 2);
 
@@ -311,7 +321,8 @@ fn should_resubmit_from_retracted_during_maintenance() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![], true);
@@ -329,7 +340,8 @@ fn should_not_resubmit_from_retracted_during_maintenance_if_tx_is_also_in_enacte
 
 	let (pool, api, _guard) = maintained_pool();
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![xt.clone()], true);
@@ -347,8 +359,9 @@ fn should_not_retain_invalid_hashes_from_retracted() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, xt.clone()))
-		.expect("1. Imported");
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+			.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![], true);
@@ -374,15 +387,18 @@ fn should_revalidate_across_many_blocks() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	let watcher1 = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, xt1.clone()))
+	let watcher1 =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt1.clone()))
+			.expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt2.clone()))
 		.expect("1. Imported");
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt2.clone())).expect("1. Imported");
 	assert_eq!(pool.status().ready, 2);
 
 	let header = api.push_block(1, vec![], true);
 	block_on(pool.maintain(block_event(header)));
 
-	block_on(pool.submit_one(&BlockId::number(1), SOURCE, xt3.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt3.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.status().ready, 3);
 
 	let header = api.push_block(2, vec![xt1.clone()], true);
@@ -409,19 +425,24 @@ fn should_push_watchers_during_maintenance() {
 
 	let tx0 = alice_uxt(0);
 	let watcher0 =
-		block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, tx0.clone())).unwrap();
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, tx0.clone()))
+			.unwrap();
 	let tx1 = alice_uxt(1);
 	let watcher1 =
-		block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, tx1.clone())).unwrap();
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, tx1.clone()))
+			.unwrap();
 	let tx2 = alice_uxt(2);
 	let watcher2 =
-		block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, tx2.clone())).unwrap();
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, tx2.clone()))
+			.unwrap();
 	let tx3 = alice_uxt(3);
 	let watcher3 =
-		block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, tx3.clone())).unwrap();
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, tx3.clone()))
+			.unwrap();
 	let tx4 = alice_uxt(4);
 	let watcher4 =
-		block_on(pool.submit_and_watch(&BlockId::Number(0), SOURCE, tx4.clone())).unwrap();
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, tx4.clone()))
+			.unwrap();
 	assert_eq!(pool.status().ready, 5);
 
 	// when
@@ -489,11 +510,13 @@ fn finalization() {
 	let api = TestApi::with_alice_nonce(209);
 	api.push_block(1, vec![], true);
 	let pool = create_basic_pool(api);
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, xt.clone()))
-		.expect("1. Imported");
-	pool.api().push_block(2, vec![xt.clone()], true);
+	let api = pool.api();
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, xt.clone()))
+			.expect("1. Imported");
+	api.push_block(2, vec![xt.clone()], true);
 
-	let header = pool.api().chain().read().block_by_number.get(&2).unwrap()[0].0.header().clone();
+	let header = api.chain().read().block_by_number.get(&2).unwrap()[0].0.header().clone();
 	let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
 	block_on(pool.maintain(event));
 
@@ -515,16 +538,17 @@ fn fork_aware_finalization() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 	let mut canon_watchers = vec![];
 
 	let from_alice = uxt(Alice, 1);
 	let from_dave = uxt(Dave, 2);
 	let from_bob = uxt(Bob, 1);
 	let from_charlie = uxt(Charlie, 1);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Dave.into());
-	pool.api().increment_nonce(Charlie.into());
-	pool.api().increment_nonce(Bob.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Dave.into());
+	api.increment_nonce(Charlie.into());
+	api.increment_nonce(Bob.into());
 
 	let from_dave_watcher;
 	let from_bob_watcher;
@@ -538,10 +562,13 @@ fn fork_aware_finalization() {
 
 	// block B1
 	{
-		let watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header = pool.api().push_block(2, vec![from_alice.clone()], true);
+		let watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block(2, vec![from_alice.clone()], true);
 		canon_watchers.push((watcher, header.hash()));
 		assert_eq!(pool.status().ready, 1);
 
@@ -556,10 +583,13 @@ fn fork_aware_finalization() {
 
 	// block C2
 	{
-		let header = pool.api().push_block_with_parent(b1, vec![from_dave.clone()], true);
-		from_dave_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_dave.clone()))
-				.expect("1. Imported");
+		let header = api.push_block_with_parent(b1, vec![from_dave.clone()], true);
+		from_dave_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_dave.clone(),
+		))
+		.expect("1. Imported");
 		assert_eq!(pool.status().ready, 1);
 		log::trace!(target: LOG_TARGET, ">> C2: {:?} {:?}", header.hash(), header);
 		let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
@@ -570,11 +600,14 @@ fn fork_aware_finalization() {
 
 	// block D2
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
 		assert_eq!(pool.status().ready, 1);
-		let header = pool.api().push_block_with_parent(c2, vec![from_bob.clone()], true);
+		let header = api.push_block_with_parent(c2, vec![from_bob.clone()], true);
 
 		log::trace!(target: LOG_TARGET, ">> D2: {:?} {:?}", header.hash(), header);
 		let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
@@ -585,15 +618,18 @@ fn fork_aware_finalization() {
 
 	// block C1
 	{
-		let watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_charlie.clone()))
-				.expect("1.Imported");
+		let watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_charlie.clone(),
+		))
+		.expect("1.Imported");
 		assert_eq!(pool.status().ready, 1);
-		let header = pool.api().push_block_with_parent(b1, vec![from_charlie.clone()], true);
+		let header = api.push_block_with_parent(b1, vec![from_charlie.clone()], true);
 		log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header);
 		c1 = header.hash();
 		canon_watchers.push((watcher, header.hash()));
-		let event = block_event_with_retracted(header.clone(), d2, pool.api());
+		let event = block_event_with_retracted(header.clone(), d2, api);
 		block_on(pool.maintain(event));
 		assert_eq!(pool.status().ready, 2);
 
@@ -604,10 +640,10 @@ fn fork_aware_finalization() {
 	// block D1
 	{
 		let xt = uxt(Eve, 0);
-		let w = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, xt.clone()))
+		let w = block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, xt.clone()))
 			.expect("1. Imported");
 		assert_eq!(pool.status().ready, 3);
-		let header = pool.api().push_block_with_parent(c1, vec![xt.clone()], true);
+		let header = api.push_block_with_parent(c1, vec![xt.clone()], true);
 		log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header);
 		d1 = header.hash();
 		canon_watchers.push((w, header.hash()));
@@ -623,7 +659,7 @@ fn fork_aware_finalization() {
 
 	// block E1
 	{
-		let header = pool.api().push_block_with_parent(d1, vec![from_dave, from_bob], true);
+		let header = api.push_block_with_parent(d1, vec![from_dave, from_bob], true);
 		log::trace!(target: LOG_TARGET, ">> E1: {:?} {:?}", header.hash(), header);
 		e1 = header.hash();
 		let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
@@ -673,16 +709,18 @@ fn prune_and_retract_tx_at_same_time() {
 	api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
-	pool.api().increment_nonce(Alice.into());
+	api.increment_nonce(Alice.into());
 
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-		.expect("1. Imported");
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, from_alice.clone()))
+			.expect("1. Imported");
 
 	// Block B1
 	let b1 = {
-		let header = pool.api().push_block(2, vec![from_alice.clone()], true);
+		let header = api.push_block(2, vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
@@ -693,10 +731,10 @@ fn prune_and_retract_tx_at_same_time() {
 
 	// Block B2
 	let b2 = {
-		let header = pool.api().push_block(2, vec![from_alice.clone()], true);
+		let header = api.push_block(2, vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 0);
 
-		let event = block_event_with_retracted(header.clone(), b1, pool.api());
+		let event = block_event_with_retracted(header.clone(), b1, api);
 		block_on(pool.maintain(event));
 		assert_eq!(pool.status().ready, 0);
 
@@ -739,19 +777,21 @@ fn resubmit_tx_of_fork_that_is_not_part_of_retracted() {
 	api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let tx0 = uxt(Alice, 1);
 	let tx1 = uxt(Dave, 2);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Dave.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Dave.into());
 
 	let d0;
 
 	// Block D0
 	{
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx0.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block(2, vec![tx0.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx0.clone()))
+				.expect("1. Imported");
+		let header = api.push_block(2, vec![tx0.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None };
@@ -762,17 +802,18 @@ fn resubmit_tx_of_fork_that_is_not_part_of_retracted() {
 
 	// Block D1
 	{
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx1.clone()))
-			.expect("1. Imported");
-		pool.api().push_block(2, vec![tx1.clone()], false);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx1.clone()))
+				.expect("1. Imported");
+		api.push_block(2, vec![tx1.clone()], false);
 		assert_eq!(pool.status().ready, 1);
 	}
 
 	// Block D2
 	{
 		//push new best block
-		let header = pool.api().push_block(2, vec![], true);
-		let event = block_event_with_retracted(header, d0, pool.api());
+		let header = api.push_block(2, vec![], true);
+		let event = block_event_with_retracted(header, d0, api);
 		block_on(pool.maintain(event));
 		assert_eq!(pool.status().ready, 2);
 	}
@@ -786,6 +827,8 @@ fn resubmit_from_retracted_fork() {
 
 	let pool = create_basic_pool(api);
 
+	let api = pool.api();
+
 	let tx0 = uxt(Alice, 1);
 	let tx1 = uxt(Dave, 2);
 	let tx2 = uxt(Bob, 3);
@@ -795,18 +838,19 @@ fn resubmit_from_retracted_fork() {
 	let tx4 = uxt(Ferdie, 2);
 	let tx5 = uxt(One, 3);
 
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Dave.into());
-	pool.api().increment_nonce(Bob.into());
-	pool.api().increment_nonce(Eve.into());
-	pool.api().increment_nonce(Ferdie.into());
-	pool.api().increment_nonce(One.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Dave.into());
+	api.increment_nonce(Bob.into());
+	api.increment_nonce(Eve.into());
+	api.increment_nonce(Ferdie.into());
+	api.increment_nonce(One.into());
 
 	// Block D0
 	{
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx0.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block(2, vec![tx0.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx0.clone()))
+				.expect("1. Imported");
+		let header = api.push_block(2, vec![tx0.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		block_on(pool.maintain(block_event(header)));
@@ -815,18 +859,20 @@ fn resubmit_from_retracted_fork() {
 
 	// Block E0
 	{
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx1.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block(3, vec![tx1.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx1.clone()))
+				.expect("1. Imported");
+		let header = api.push_block(3, vec![tx1.clone()], true);
 		block_on(pool.maintain(block_event(header)));
 		assert_eq!(pool.status().ready, 0);
 	}
 
 	// Block F0
 	let f0 = {
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx2.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block(4, vec![tx2.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx2.clone()))
+				.expect("1. Imported");
+		let header = api.push_block(4, vec![tx2.clone()], true);
 		block_on(pool.maintain(block_event(header.clone())));
 		assert_eq!(pool.status().ready, 0);
 		header.hash()
@@ -834,27 +880,30 @@ fn resubmit_from_retracted_fork() {
 
 	// Block D1
 	let d1 = {
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx3.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block(2, vec![tx3.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx3.clone()))
+				.expect("1. Imported");
+		let header = api.push_block(2, vec![tx3.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 		header.hash()
 	};
 
 	// Block E1
 	let e1 = {
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx4.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block_with_parent(d1, vec![tx4.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx4.clone()))
+				.expect("1. Imported");
+		let header = api.push_block_with_parent(d1, vec![tx4.clone()], true);
 		assert_eq!(pool.status().ready, 2);
 		header.hash()
 	};
 
 	// Block F1
 	let f1_header = {
-		let _ = block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, tx5.clone()))
-			.expect("1. Imported");
-		let header = pool.api().push_block_with_parent(e1, vec![tx5.clone()], true);
+		let _ =
+			block_on(pool.submit_and_watch(api.expect_hash_from_number(1), SOURCE, tx5.clone()))
+				.expect("1. Imported");
+		let header = api.push_block_with_parent(e1, vec![tx5.clone()], true);
 		// Don't announce the block event to the pool directly, because we will
 		// re-org to this block.
 		assert_eq!(pool.status().ready, 3);
@@ -865,7 +914,7 @@ fn resubmit_from_retracted_fork() {
 	let expected_ready = vec![tx3, tx4, tx5].iter().map(Encode::encode).collect::<BTreeSet<_>>();
 	assert_eq!(expected_ready, ready);
 
-	let event = block_event_with_retracted(f1_header, f0, pool.api());
+	let event = block_event_with_retracted(f1_header, f0, api);
 	block_on(pool.maintain(event));
 
 	assert_eq!(pool.status().ready, 3);
@@ -876,9 +925,10 @@ fn resubmit_from_retracted_fork() {
 
 #[test]
 fn ready_set_should_not_resolve_before_block_update() {
-	let (pool, _api, _guard) = maintained_pool();
+	let (pool, api, _guard) = maintained_pool();
 	let xt1 = uxt(Alice, 209);
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt1.clone()))
+		.expect("1. Imported");
 
 	assert!(pool.ready_at(1).now_or_never().is_none());
 }
@@ -890,7 +940,8 @@ fn ready_set_should_resolve_after_block_update() {
 
 	let xt1 = uxt(Alice, 209);
 
-	block_on(pool.submit_one(&BlockId::number(1), SOURCE, xt1.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt1.clone()))
+		.expect("1. Imported");
 	block_on(pool.maintain(block_event(header)));
 
 	assert!(pool.ready_at(1).now_or_never().is_some());
@@ -903,7 +954,8 @@ fn ready_set_should_eventually_resolve_when_block_update_arrives() {
 
 	let xt1 = uxt(Alice, 209);
 
-	block_on(pool.submit_one(&BlockId::number(1), SOURCE, xt1.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(1), SOURCE, xt1.clone()))
+		.expect("1. Imported");
 
 	let noop_waker = futures::task::noop_waker();
 	let mut context = futures::task::Context::from_waker(&noop_waker);
@@ -948,7 +1000,12 @@ fn import_notification_to_pool_maintain_works() {
 
 	// Prepare the extrisic, push it to the pool and check that it was added.
 	let xt = uxt(Alice, 0);
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported");
+	block_on(pool.submit_one(
+		pool.api().block_id_to_hash(&BlockId::Number(0)).unwrap().unwrap(),
+		SOURCE,
+		xt.clone(),
+	))
+	.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let mut import_stream = block_on_stream(client.import_notification_stream());
@@ -973,7 +1030,8 @@ fn pruning_a_transaction_should_remove_it_from_best_transaction() {
 
 	let xt1 = ExtrinsicBuilder::new_include_data(Vec::new()).build();
 
-	block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt1.clone())).expect("1. Imported");
+	block_on(pool.submit_one(api.expect_hash_from_number(0), SOURCE, xt1.clone()))
+		.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 	let header = api.push_block(1, vec![xt1.clone()], true);
 
@@ -997,8 +1055,12 @@ fn stale_transactions_are_pruned() {
 	let (pool, api, _guard) = maintained_pool();
 
 	xts.into_iter().for_each(|xt| {
-		block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.into_unchecked_extrinsic()))
-			.expect("1. Imported");
+		block_on(pool.submit_one(
+			api.expect_hash_from_number(0),
+			SOURCE,
+			xt.into_unchecked_extrinsic(),
+		))
+		.expect("1. Imported");
 	});
 	assert_eq!(pool.status().ready, 0);
 	assert_eq!(pool.status().future, 3);
@@ -1038,8 +1100,9 @@ fn finalized_only_handled_correctly() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, xt.clone()))
-		.expect("1. Imported");
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+			.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![xt], true);
@@ -1066,8 +1129,9 @@ fn best_block_after_finalized_handled_correctly() {
 
 	let (pool, api, _guard) = maintained_pool();
 
-	let watcher = block_on(pool.submit_and_watch(&BlockId::number(0), SOURCE, xt.clone()))
-		.expect("1. Imported");
+	let watcher =
+		block_on(pool.submit_and_watch(api.expect_hash_from_number(0), SOURCE, xt.clone()))
+			.expect("1. Imported");
 	assert_eq!(pool.status().ready, 1);
 
 	let header = api.push_block(1, vec![xt], true);
@@ -1096,11 +1160,12 @@ fn switching_fork_with_finalized_works() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
 	let from_bob = uxt(Bob, 2);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Bob.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Bob.into());
 
 	let from_alice_watcher;
 	let from_bob_watcher;
@@ -1109,12 +1174,13 @@ fn switching_fork_with_finalized_works() {
 
 	// block B1
 	{
-		from_alice_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
+		from_alice_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 		log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header);
 		b1_header = header;
@@ -1122,10 +1188,13 @@ fn switching_fork_with_finalized_works() {
 
 	// block B2
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
-		let header = pool.api().push_block_with_parent(
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(
 			a_header.hash(),
 			vec![from_alice.clone(), from_bob.clone()],
 			true,
@@ -1174,11 +1243,12 @@ fn switching_fork_multiple_times_works() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
 	let from_bob = uxt(Bob, 2);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Bob.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Bob.into());
 
 	let from_alice_watcher;
 	let from_bob_watcher;
@@ -1187,12 +1257,13 @@ fn switching_fork_multiple_times_works() {
 
 	// block B1
 	{
-		from_alice_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
+		from_alice_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 		log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header);
 		b1_header = header;
@@ -1200,10 +1271,13 @@ fn switching_fork_multiple_times_works() {
 
 	// block B2
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
-		let header = pool.api().push_block_with_parent(
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(
 			a_header.hash(),
 			vec![from_alice.clone(), from_bob.clone()],
 			true,
@@ -1223,14 +1297,14 @@ fn switching_fork_multiple_times_works() {
 
 	{
 		// phase-1
-		let event = block_event_with_retracted(b2_header.clone(), b1_header.hash(), pool.api());
+		let event = block_event_with_retracted(b2_header.clone(), b1_header.hash(), api);
 		block_on(pool.maintain(event));
 		assert_eq!(pool.status().ready, 0);
 	}
 
 	{
 		// phase-2
-		let event = block_event_with_retracted(b1_header.clone(), b2_header.hash(), pool.api());
+		let event = block_event_with_retracted(b1_header.clone(), b2_header.hash(), api);
 		block_on(pool.maintain(event));
 		assert_eq!(pool.status().ready, 1);
 	}
@@ -1282,13 +1356,14 @@ fn two_blocks_delayed_finalization_works() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
 	let from_bob = uxt(Bob, 2);
 	let from_charlie = uxt(Charlie, 3);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Bob.into());
-	pool.api().increment_nonce(Charlie.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Bob.into());
+	api.increment_nonce(Charlie.into());
 
 	let from_alice_watcher;
 	let from_bob_watcher;
@@ -1299,12 +1374,13 @@ fn two_blocks_delayed_finalization_works() {
 
 	// block B1
 	{
-		from_alice_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
+		from_alice_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header);
@@ -1313,12 +1389,13 @@ fn two_blocks_delayed_finalization_works() {
 
 	// block C1
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
 		assert_eq!(pool.status().ready, 2);
 
 		log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header);
@@ -1327,12 +1404,13 @@ fn two_blocks_delayed_finalization_works() {
 
 	// block D1
 	{
-		from_charlie_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_charlie.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(c1_header.hash(), vec![from_charlie.clone()], true);
+		from_charlie_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_charlie.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(c1_header.hash(), vec![from_charlie.clone()], true);
 		assert_eq!(pool.status().ready, 3);
 
 		log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header);
@@ -1398,11 +1476,12 @@ fn delayed_finalization_does_not_retract() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
 	let from_bob = uxt(Bob, 2);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Bob.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Bob.into());
 
 	let from_alice_watcher;
 	let from_bob_watcher;
@@ -1411,12 +1490,13 @@ fn delayed_finalization_does_not_retract() {
 
 	// block B1
 	{
-		from_alice_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
+		from_alice_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header);
@@ -1425,12 +1505,13 @@ fn delayed_finalization_does_not_retract() {
 
 	// block C1
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
 		assert_eq!(pool.status().ready, 2);
 
 		log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header);
@@ -1493,11 +1574,12 @@ fn best_block_after_finalization_does_not_retract() {
 	let a_header = api.push_block(1, vec![], true);
 
 	let pool = create_basic_pool(api);
+	let api = pool.api();
 
 	let from_alice = uxt(Alice, 1);
 	let from_bob = uxt(Bob, 2);
-	pool.api().increment_nonce(Alice.into());
-	pool.api().increment_nonce(Bob.into());
+	api.increment_nonce(Alice.into());
+	api.increment_nonce(Bob.into());
 
 	let from_alice_watcher;
 	let from_bob_watcher;
@@ -1506,12 +1588,13 @@ fn best_block_after_finalization_does_not_retract() {
 
 	// block B1
 	{
-		from_alice_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
+		from_alice_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_alice.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true);
 		assert_eq!(pool.status().ready, 1);
 
 		log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header);
@@ -1520,12 +1603,13 @@ fn best_block_after_finalization_does_not_retract() {
 
 	// block C1
 	{
-		from_bob_watcher =
-			block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_bob.clone()))
-				.expect("1. Imported");
-		let header =
-			pool.api()
-				.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
+		from_bob_watcher = block_on(pool.submit_and_watch(
+			api.expect_hash_from_number(1),
+			SOURCE,
+			from_bob.clone(),
+		))
+		.expect("1. Imported");
+		let header = api.push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true);
 		assert_eq!(pool.status().ready, 2);
 
 		log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header);
diff --git a/substrate/test-utils/runtime/transaction-pool/src/lib.rs b/substrate/test-utils/runtime/transaction-pool/src/lib.rs
index 7b529200440..8c8345b06bd 100644
--- a/substrate/test-utils/runtime/transaction-pool/src/lib.rs
+++ b/substrate/test-utils/runtime/transaction-pool/src/lib.rs
@@ -22,6 +22,7 @@
 use codec::Encode;
 use futures::future::ready;
 use parking_lot::RwLock;
+use sc_transaction_pool::ChainApi;
 use sp_blockchain::{CachedHeaderMetadata, TreeRoute};
 use sp_runtime::{
 	generic::{self, BlockId},
@@ -237,9 +238,14 @@ impl TestApi {
 	) -> Result<sp_blockchain::TreeRoute<Block>, Error> {
 		sp_blockchain::tree_route(self, from, to)
 	}
+
+	/// Helper function for mapping block number to hash. Use if mapping shall not fail.
+	pub fn expect_hash_from_number(&self, n: BlockNumber) -> Hash {
+		self.block_id_to_hash(&BlockId::Number(n)).unwrap().unwrap()
+	}
 }
 
-impl sc_transaction_pool::ChainApi for TestApi {
+impl ChainApi for TestApi {
 	type Block = Block;
 	type Error = Error;
 	type ValidationFuture = futures::future::Ready<Result<TransactionValidity, Error>>;
@@ -247,13 +253,13 @@ impl sc_transaction_pool::ChainApi for TestApi {
 
 	fn validate_transaction(
 		&self,
-		at: &BlockId<Self::Block>,
+		at: <Self::Block as BlockT>::Hash,
 		_source: TransactionSource,
 		uxt: <Self::Block as BlockT>::Extrinsic,
 	) -> Self::ValidationFuture {
 		self.validation_requests.write().push(uxt.clone());
 
-		match self.block_id_to_number(at) {
+		match self.block_id_to_number(&BlockId::Hash(at)) {
 			Ok(Some(number)) => {
 				let found_best = self
 					.chain
diff --git a/substrate/utils/frame/rpc/system/src/lib.rs b/substrate/utils/frame/rpc/system/src/lib.rs
index 1eff71e3390..f467a879890 100644
--- a/substrate/utils/frame/rpc/system/src/lib.rs
+++ b/substrate/utils/frame/rpc/system/src/lib.rs
@@ -219,7 +219,6 @@ mod tests {
 	use jsonrpsee::{core::Error as JsonRpseeError, types::error::CallError};
 	use sc_transaction_pool::BasicPool;
 	use sp_runtime::{
-		generic::BlockId,
 		transaction_validity::{InvalidTransaction, TransactionValidityError},
 		ApplyExtrinsicResult,
 	};
@@ -245,11 +244,12 @@ mod tests {
 			};
 			t.into_unchecked_extrinsic()
 		};
+		let hash_of_block0 = client.info().genesis_hash;
 		// Populate the pool
 		let ext0 = new_transaction(0);
-		block_on(pool.submit_one(&BlockId::number(0), source, ext0)).unwrap();
+		block_on(pool.submit_one(hash_of_block0, source, ext0)).unwrap();
 		let ext1 = new_transaction(1);
-		block_on(pool.submit_one(&BlockId::number(0), source, ext1)).unwrap();
+		block_on(pool.submit_one(hash_of_block0, source, ext1)).unwrap();
 
 		let accounts = System::new(client, pool, DenyUnsafe::Yes);
 
-- 
GitLab