diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 9b7c07e28d76712cd3091e4888304c4613f2bfab..91c602167c5a313d7519b2305c14f1594d227a53 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -4698,6 +4698,7 @@ dependencies = [
  "sr-primitives 2.0.0",
  "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "substrate-client 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-network 2.0.0",
  "substrate-panic-handler 2.0.0",
@@ -4732,6 +4733,7 @@ dependencies = [
  "sr-version 2.0.0",
  "substrate-consensus-common 2.0.0",
  "substrate-executor 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-inherents 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-panic-handler 2.0.0",
@@ -4760,6 +4762,7 @@ dependencies = [
  "substrate-client 2.0.0",
  "substrate-consensus-common 2.0.0",
  "substrate-executor 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-primitives 2.0.0",
  "substrate-state-db 2.0.0",
@@ -4845,6 +4848,7 @@ dependencies = [
  "substrate-consensus-slots 2.0.0",
  "substrate-consensus-uncles 2.0.0",
  "substrate-executor 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-inherents 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-keystore 2.0.0",
@@ -5019,6 +5023,7 @@ dependencies = [
  "substrate-consensus-babe-primitives 2.0.0",
  "substrate-consensus-common 2.0.0",
  "substrate-finality-grandpa-primitives 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-inherents 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-keystore 2.0.0",
@@ -5044,6 +5049,15 @@ dependencies = [
  "substrate-client 2.0.0",
 ]
 
+[[package]]
+name = "substrate-header-metadata"
+version = "2.0.0"
+dependencies = [
+ "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sr-primitives 2.0.0",
+]
+
 [[package]]
 name = "substrate-inherents"
 version = "2.0.0"
@@ -5114,6 +5128,7 @@ dependencies = [
  "substrate-client 2.0.0",
  "substrate-consensus-babe-primitives 2.0.0",
  "substrate-consensus-common 2.0.0",
+ "substrate-header-metadata 2.0.0",
  "substrate-keyring 2.0.0",
  "substrate-peerset 2.0.0",
  "substrate-primitives 2.0.0",
diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml
index 75f86e7761776dfa4b892735b0e72c78a97a104f..c1bac20a7c4a13964ef8786a7a39b13d8e9c4984 100644
--- a/substrate/Cargo.toml
+++ b/substrate/Cargo.toml
@@ -26,6 +26,7 @@ members = [
 	"core/cli",
 	"core/client",
 	"core/client/db",
+	"core/client/header-metadata",
 	"core/consensus/aura",
 	"core/consensus/babe",
 	"core/consensus/common",
diff --git a/substrate/core/cli/Cargo.toml b/substrate/core/cli/Cargo.toml
index b33c48fbd1e61446e0a98779cc3d179f02e886d1..ba20d1169935ad2e82e934ac9cbd3537535b9a91 100644
--- a/substrate/core/cli/Cargo.toml
+++ b/substrate/core/cli/Cargo.toml
@@ -24,6 +24,7 @@ exit-future = "0.1"
 serde_json = "1.0"
 panic-handler = { package = "substrate-panic-handler", path = "../../core/panic-handler" }
 client = { package = "substrate-client", path = "../../core/client" }
+header-metadata = { package = "substrate-header-metadata", path = "../../core/client/header-metadata" }
 network = { package = "substrate-network", path = "../../core/network" }
 sr-primitives = { path = "../../core/sr-primitives" }
 primitives = { package = "substrate-primitives", path = "../../core/primitives" }
diff --git a/substrate/core/cli/src/informant.rs b/substrate/core/cli/src/informant.rs
index a5fe52c09af4046b398bfd841bb6fbf9362e9233..9d7f04b8f6d4f5162204107d5d4ee96f0e2971d8 100644
--- a/substrate/core/cli/src/informant.rs
+++ b/substrate/core/cli/src/informant.rs
@@ -20,7 +20,7 @@ use client::BlockchainEvents;
 use futures::{Future, Stream};
 use futures03::{StreamExt as _, TryStreamExt as _};
 use log::{info, warn};
-use sr_primitives::{generic::BlockId, traits::Header};
+use sr_primitives::traits::Header;
 use service::AbstractService;
 
 mod display;
@@ -47,19 +47,18 @@ pub fn build(service: &impl AbstractService) -> impl Future<Item = (), Error = (
 		// detect and log reorganizations.
 		if let Some((ref last_num, ref last_hash)) = last_best {
 			if n.header.parent_hash() != last_hash && n.is_new_best  {
-				let tree_route = ::client::blockchain::tree_route(
-					|id| client.header(&id)?.ok_or_else(
-						|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-					BlockId::Hash(last_hash.clone()),
-					BlockId::Hash(n.hash),
+				let maybe_ancestor = header_metadata::lowest_common_ancestor(
+					&*client,
+					last_hash.clone(),
+					n.hash,
 				);
 
-				match tree_route {
-					Ok(ref t) if !t.retracted().is_empty() => info!(
+				match maybe_ancestor {
+					Ok(ref ancestor) if ancestor.hash != *last_hash => info!(
 						"Reorg from #{},{} to #{},{}, common ancestor #{},{}",
 						last_num, last_hash,
 						n.header.number(), n.hash,
-						t.common_block().number, t.common_block().hash,
+						ancestor.number, ancestor.hash,
 					),
 					Ok(_) => {},
 					Err(e) => warn!("Error computing tree route: {}", e),
diff --git a/substrate/core/client/Cargo.toml b/substrate/core/client/Cargo.toml
index 7fcd291f505a167c843af53e6d4260aac01ee71d..58de8233bbf43a830236c56cda4e7a5522bb8c2b 100644
--- a/substrate/core/client/Cargo.toml
+++ b/substrate/core/client/Cargo.toml
@@ -27,6 +27,7 @@ runtime-version = { package = "sr-version", path = "../sr-version", default-feat
 rstd = { package = "sr-std", path = "../sr-std", default-features = false }
 inherents = { package = "substrate-inherents", path = "../inherents", default-features = false }
 sr-api-macros = { path = "../sr-api-macros" }
+header-metadata = { package = "substrate-header-metadata", path = "header-metadata", optional = true }
 
 [dev-dependencies]
 env_logger = "0.6"
@@ -45,6 +46,7 @@ std = [
 	"sr-primitives/std",
 	"runtime-version/std",
 	"hash-db/std",
+	"header-metadata",
 	"consensus",
 	"parking_lot",
 	"derive_more",
diff --git a/substrate/core/client/db/Cargo.toml b/substrate/core/client/db/Cargo.toml
index 89a1dcc0cbc8ae43da1ef07654410c77676f7790..594bda744c83c94d073c514ed0b417f76e7be897 100644
--- a/substrate/core/client/db/Cargo.toml
+++ b/substrate/core/client/db/Cargo.toml
@@ -22,6 +22,7 @@ executor = { package = "substrate-executor", path = "../../executor" }
 state_db = { package = "substrate-state-db", path = "../../state-db" }
 trie = { package = "substrate-trie", path = "../../trie" }
 consensus_common = { package = "substrate-consensus-common", path = "../../consensus/common" }
+header_metadata = { package = "substrate-header-metadata", path = "../header-metadata" }
 
 [dev-dependencies]
 substrate-keyring = { path = "../../keyring" }
diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs
index 728858e2d68e2b5fd59fe52a3d7695989789b268..126622364a76a25a2bcb4f9d1ddbab5d40a0e615 100644
--- a/substrate/core/client/db/src/lib.rs
+++ b/substrate/core/client/db/src/lib.rs
@@ -42,6 +42,7 @@ use client::backend::NewBlockState;
 use client::blockchain::{well_known_cache_keys, HeaderBackend};
 use client::{ForkBlocks, ExecutionStrategies};
 use client::backend::{StorageCollection, ChildStorageCollection};
+use client::error::Result as ClientResult;
 use codec::{Decode, Encode};
 use hash_db::{Hasher, Prefix};
 use kvdb::{KeyValueDB, DBTransaction};
@@ -61,10 +62,11 @@ use state_machine::{
 	DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, ChangesTrieBuildCache,
 	backend::Backend as StateBackend,
 };
-use crate::utils::{Meta, db_err, meta_keys, read_db, block_id_to_lookup_key, read_meta};
+use crate::utils::{Meta, db_err, meta_keys, read_db, read_meta};
 use client::leaves::{LeafSet, FinalizationDisplaced};
 use client::children;
 use state_db::StateDb;
+use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
 use crate::storage_cache::{CachingState, SharedCache, new_shared_cache};
 use log::{trace, debug, warn};
 pub use state_db::PruningMode;
@@ -271,16 +273,18 @@ pub struct BlockchainDb<Block: BlockT> {
 	db: Arc<dyn KeyValueDB>,
 	meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
 	leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
+	header_metadata_cache: HeaderMetadataCache<Block>,
 }
 
 impl<Block: BlockT> BlockchainDb<Block> {
-	fn new(db: Arc<dyn KeyValueDB>) -> Result<Self, client::error::Error> {
+	fn new(db: Arc<dyn KeyValueDB>) -> ClientResult<Self> {
 		let meta = read_meta::<Block>(&*db, columns::META, columns::HEADER)?;
 		let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?;
 		Ok(BlockchainDb {
 			db,
 			leaves: RwLock::new(leaves),
 			meta: Arc::new(RwLock::new(meta)),
+			header_metadata_cache: HeaderMetadataCache::default(),
 		})
 	}
 
@@ -310,7 +314,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
 }
 
 impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
-	fn header(&self, id: BlockId<Block>) -> Result<Option<Block::Header>, client::error::Error> {
+	fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
 		utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)
 	}
 
@@ -325,7 +329,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
 		}
 	}
 
-	fn status(&self, id: BlockId<Block>) -> Result<client::blockchain::BlockStatus, client::error::Error> {
+	fn status(&self, id: BlockId<Block>) -> ClientResult<client::blockchain::BlockStatus> {
 		let exists = match id {
 			BlockId::Hash(_) => read_db(
 				&*self.db,
@@ -341,16 +345,11 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
 		}
 	}
 
-	fn number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, client::error::Error> {
-		if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::KEY_LOOKUP, BlockId::Hash(hash))? {
-			let number = utils::lookup_key_to_number(&lookup_key)?;
-			Ok(Some(number))
-		} else {
-			Ok(None)
-		}
+	fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
+		Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number))
 	}
 
-	fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>, client::error::Error> {
+	fn hash(&self, number: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
 		self.header(BlockId::Number(number)).and_then(|maybe_header| match maybe_header {
 			Some(header) => Ok(Some(header.hash().clone())),
 			None => Ok(None),
@@ -359,7 +358,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
 }
 
 impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
-	fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, client::error::Error> {
+	fn body(&self, id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
 		match read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, id)? {
 			Some(body) => match Decode::decode(&mut &body[..]) {
 				Ok(body) => Ok(Some(body)),
@@ -371,7 +370,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
 		}
 	}
 
-	fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>, client::error::Error> {
+	fn justification(&self, id: BlockId<Block>) -> ClientResult<Option<Justification>> {
 		match read_db(&*self.db, columns::KEY_LOOKUP, columns::JUSTIFICATION, id)? {
 			Some(justification) => match Decode::decode(&mut &justification[..]) {
 				Ok(justification) => Ok(Some(justification)),
@@ -383,7 +382,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
 		}
 	}
 
-	fn last_finalized(&self) -> Result<Block::Hash, client::error::Error> {
+	fn last_finalized(&self) -> ClientResult<Block::Hash> {
 		Ok(self.meta.read().finalized_hash.clone())
 	}
 
@@ -391,11 +390,11 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
 		None
 	}
 
-	fn leaves(&self) -> Result<Vec<Block::Hash>, client::error::Error> {
+	fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
 		Ok(self.leaves.read().hashes())
 	}
 
-	fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>, client::error::Error> {
+	fn children(&self, parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
 		children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)
 	}
 }
@@ -406,6 +405,31 @@ impl<Block: BlockT> client::blockchain::ProvideCache<Block> for BlockchainDb<Blo
 	}
 }
 
+impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
+	type Error = client::error::Error;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.header_metadata_cache.header_metadata(hash).or_else(|_| {
+			self.header(BlockId::hash(hash))?.map(|header| {
+				let header_metadata = CachedHeaderMetadata::from(&header);
+				self.header_metadata_cache.insert_header_metadata(
+					header_metadata.hash,
+					header_metadata.clone(),
+				);
+				header_metadata
+			}).ok_or(client::error::Error::UnknownBlock("header not found in db".to_owned()))
+		})
+	}
+
+	fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
+		self.header_metadata_cache.insert_header_metadata(hash, metadata)
+	}
+
+	fn remove_header_metadata(&self, hash: Block::Hash) {
+		self.header_metadata_cache.remove_header_metadata(hash);
+	}
+}
+
 /// Database transaction
 pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
 	old_state: CachingState<Blake2Hasher, RefTrackingState<Block>, Block>,
@@ -437,7 +461,7 @@ where Block: BlockT<Hash=H256>,
 {
 	type State = CachingState<Blake2Hasher, RefTrackingState<Block>, Block>;
 
-	fn state(&self) -> Result<Option<&Self::State>, client::error::Error> {
+	fn state(&self) -> ClientResult<Option<&Self::State>> {
 		Ok(Some(&self.old_state))
 	}
 
@@ -447,7 +471,7 @@ where Block: BlockT<Hash=H256>,
 		body: Option<Vec<Block::Extrinsic>>,
 		justification: Option<Justification>,
 		leaf_state: NewBlockState,
-	) -> Result<(), client::error::Error> {
+	) -> ClientResult<()> {
 		assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
 		self.pending_block = Some(PendingBlock {
 			header,
@@ -462,7 +486,7 @@ where Block: BlockT<Hash=H256>,
 		// Currently cache isn't implemented on full nodes.
 	}
 
-	fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> Result<(), client::error::Error> {
+	fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> ClientResult<()> {
 		self.db_updates = update;
 		Ok(())
 	}
@@ -471,7 +495,7 @@ where Block: BlockT<Hash=H256>,
 		&mut self,
 		top: StorageOverlay,
 		children: ChildrenStorageOverlay
-	) -> Result<H256, client::error::Error> {
+	) -> ClientResult<H256> {
 
 		if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
 			return Err(client::error::Error::GenesisInvalid.into());
@@ -499,13 +523,13 @@ where Block: BlockT<Hash=H256>,
 	fn update_changes_trie(
 		&mut self,
 		update: ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>,
-	) -> Result<(), client::error::Error> {
+	) -> ClientResult<()> {
 		self.changes_trie_updates = update.0;
 		self.changes_trie_cache_update = Some(update.1);
 		Ok(())
 	}
 
-	fn insert_aux<I>(&mut self, ops: I) -> Result<(), client::error::Error>
+	fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
 		where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
 	{
 		self.aux_ops.append(&mut ops.into_iter().collect());
@@ -516,18 +540,18 @@ where Block: BlockT<Hash=H256>,
 		&mut self,
 		update: StorageCollection,
 		child_update: ChildStorageCollection,
-	) -> Result<(), client::error::Error> {
+	) -> ClientResult<()> {
 		self.storage_updates = update;
 		self.child_storage_updates = child_update;
 		Ok(())
 	}
 
-	fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> Result<(), client::error::Error> {
+	fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> ClientResult<()> {
 		self.finalized_blocks.push((block, justification));
 		Ok(())
 	}
 
-	fn mark_head(&mut self, block: BlockId<Block>) -> Result<(), client::error::Error> {
+	fn mark_head(&mut self, block: BlockId<Block>) -> ClientResult<()> {
 		assert!(self.set_head.is_none(), "Only one set head per operation is allowed");
 		self.set_head = Some(block);
 		Ok(())
@@ -751,11 +775,11 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 	/// Create a new instance of database backend.
 	///
 	/// The pruning window is how old a block must be before the state is pruned.
-	pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> client::error::Result<Self> {
+	pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> {
 		Self::new_inner(config, canonicalization_delay)
 	}
 
-	fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> Result<Self, client::error::Error> {
+	fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> {
 		#[cfg(feature = "kvdb-rocksdb")]
 		let db = crate::utils::open_database(&config, columns::META, "full")?;
 		#[cfg(not(feature = "kvdb-rocksdb"))]
@@ -795,7 +819,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 		db: Arc<dyn KeyValueDB>,
 		canonicalization_delay: u64,
 		config: &DatabaseSettings
-	) -> Result<Self, client::error::Error> {
+	) -> ClientResult<Self> {
 		let is_archive_pruning = config.pruning.is_archive();
 		let blockchain = BlockchainDb::new(db.clone())?;
 		let meta = blockchain.meta.clone();
@@ -881,7 +905,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 	///
 	/// Currently changes tries configuration is set up once (at genesis) and could not
 	/// be changed. Thus, we'll actually read value once and then just use cached value.
-	fn changes_trie_config(&self, block: Block::Hash) -> Result<Option<ChangesTrieConfiguration>, client::error::Error> {
+	fn changes_trie_config(&self, block: Block::Hash) -> ClientResult<Option<ChangesTrieConfiguration>> {
 		let mut cached_changes_trie_config = self.changes_trie_config.lock();
 		match cached_changes_trie_config.clone() {
 			Some(cached_changes_trie_config) => Ok(cached_changes_trie_config),
@@ -904,7 +928,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 	/// In the case where the new best block is a block to be imported, `route_to`
 	/// should be the parent of `best_to`. In the case where we set an existing block
 	/// to be best, `route_to` should equal to `best_to`.
-	fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(Vec<Block::Hash>, Vec<Block::Hash>), client::error::Error> {
+	fn set_head_with_transaction(
+		&self,
+		transaction: &mut DBTransaction,
+		route_to: Block::Hash,
+		best_to: (NumberFor<Block>, Block::Hash),
+	) -> ClientResult<(Vec<Block::Hash>, Vec<Block::Hash>)> {
 		let mut enacted = Vec::default();
 		let mut retracted = Vec::default();
 
@@ -912,12 +941,10 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 
 		// cannot find tree route with empty DB.
 		if meta.best_hash != Default::default() {
-			let tree_route = ::client::blockchain::tree_route(
-				|id| self.blockchain.header(id)?.ok_or_else(
-					|| client::error::Error::UnknownBlock(format!("{:?}", id))
-				),
-				BlockId::Hash(meta.best_hash),
-				BlockId::Hash(route_to),
+			let tree_route = header_metadata::tree_route(
+				&self.blockchain,
+				meta.best_hash,
+				route_to,
 			)?;
 
 			// uncanonicalize: check safety violations and ensure the numbers no longer
@@ -968,7 +995,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 		&self,
 		header: &Block::Header,
 		last_finalized: Option<Block::Hash>,
-	) -> Result<(), client::error::Error> {
+	) -> ClientResult<()> {
 		let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash);
 		if *header.parent_hash() != last_finalized {
 			return Err(::client::error::Error::NonSequentialFinalization(
@@ -986,7 +1013,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 		last_finalized: Option<Block::Hash>,
 		justification: Option<Justification>,
 		finalization_displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
-	) -> Result<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool), client::error::Error> {
+	) -> ClientResult<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool)> {
 		// TODO: ensure best chain contains this block.
 		let number = *header.number();
 		self.ensure_sequential_finalization(header, last_finalized)?;
@@ -1014,7 +1041,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 		hash: Block::Hash,
 		number: NumberFor<Block>,
 	)
-		-> Result<(), client::error::Error>
+		-> ClientResult<()>
 	{
 		let number_u64 = number.saturated_into::<u64>();
 		if number_u64 > self.canonicalization_delay {
@@ -1042,7 +1069,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 	}
 
 	fn try_commit_operation(&self, mut operation: BlockImportOperation<Block, Blake2Hasher>)
-		-> Result<(), client::error::Error>
+		-> ClientResult<()>
 	{
 		let mut transaction = DBTransaction::new();
 		let mut finalization_displaced_leaves = None;
@@ -1088,6 +1115,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 				hash,
 			)?;
 
+			let header_metadata = CachedHeaderMetadata::from(&pending_block.header);
+			self.blockchain.insert_header_metadata(
+				header_metadata.hash,
+				header_metadata,
+			);
+
 			transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode());
 			if let Some(body) = pending_block.body {
 				transaction.put(columns::BODY, &lookup_key, &body.encode());
@@ -1231,7 +1264,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
 		f_header: &Block::Header,
 		f_hash: Block::Hash,
 		displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>
-	) -> Result<(), client::error::Error> where
+	) -> ClientResult<()> where
 		Block: BlockT<Hash=H256>,
 	{
 		let f_num = f_header.number().clone();
@@ -1284,7 +1317,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has
 		'c: 'a,
 		I: IntoIterator<Item=&'a(&'c [u8], &'c [u8])>,
 		D: IntoIterator<Item=&'a &'b [u8]>,
-	>(&self, insert: I, delete: D) -> client::error::Result<()> {
+	>(&self, insert: I, delete: D) -> ClientResult<()> {
 		let mut transaction = DBTransaction::new();
 		for (k, v) in insert {
 			transaction.put(columns::AUX, k, v);
@@ -1296,7 +1329,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has
 		Ok(())
 	}
 
-	fn get_aux(&self, key: &[u8]) -> Result<Option<Vec<u8>>, client::error::Error> {
+	fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
 		Ok(self.storage.db.get(columns::AUX, key).map(|r| r.map(|v| v.to_vec())).map_err(db_err)?)
 	}
 }
@@ -1308,7 +1341,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 	type ChangesTrieStorage = DbChangesTrieStorage<Block>;
 	type OffchainStorage = offchain::LocalStorage;
 
-	fn begin_operation(&self) -> Result<Self::BlockImportOperation, client::error::Error> {
+	fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
 		let old_state = self.state_at(BlockId::Hash(Default::default()))?;
 		Ok(BlockImportOperation {
 			pending_block: None,
@@ -1324,13 +1357,17 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 		})
 	}
 
-	fn begin_state_operation(&self, operation: &mut Self::BlockImportOperation, block: BlockId<Block>) -> Result<(), client::error::Error> {
+	fn begin_state_operation(
+		&self,
+		operation: &mut Self::BlockImportOperation,
+		block: BlockId<Block>,
+	) -> ClientResult<()> {
 		operation.old_state = self.state_at(block)?;
 		Ok(())
 	}
 
 	fn commit_operation(&self, operation: Self::BlockImportOperation)
-		-> Result<(), client::error::Error>
+		-> ClientResult<()>
 	{
 		match self.try_commit_operation(operation) {
 			Ok(_) => {
@@ -1345,7 +1382,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 	}
 
 	fn finalize_block(&self, block: BlockId<Block>, justification: Option<Justification>)
-		-> Result<(), client::error::Error>
+		-> ClientResult<()>
 	{
 		let mut transaction = DBTransaction::new();
 		let hash = self.blockchain.expect_block_hash_from_id(&block)?;
@@ -1385,7 +1422,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 		Some(self.offchain_storage.clone())
 	}
 
-	fn revert(&self, n: NumberFor<Block>) -> Result<NumberFor<Block>, client::error::Error> {
+	fn revert(&self, n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
 		let mut best = self.blockchain.info().best_number;
 		let finalized = self.blockchain.info().finalized_number;
 		let revertible = best - finalized;
@@ -1430,7 +1467,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 		Some(used)
 	}
 
-	fn state_at(&self, block: BlockId<Block>) -> Result<Self::State, client::error::Error> {
+	fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
 		use client::blockchain::HeaderBackend as BcHeaderBackend;
 
 		// special case for genesis initialization
@@ -1466,7 +1503,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 		!self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>())
 	}
 
-	fn destroy_state(&self, state: Self::State) -> Result<(), client::error::Error> {
+	fn destroy_state(&self, state: Self::State) -> ClientResult<()> {
 		if let Some(hash) = state.cache.parent_hash.clone() {
 			let is_best = || self.blockchain.meta.read().best_hash == hash;
 			state.release().sync_cache(&[], &[], vec![], vec![], None, None, is_best);
@@ -1493,6 +1530,8 @@ mod tests {
 	use sr_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
 	use sr_primitives::traits::{Hash, BlakeTwo256};
 	use state_machine::{TrieMut, TrieDBMut, ChangesTrieRootsStorage, ChangesTrieStorage};
+	use header_metadata::{lowest_common_ancestor, tree_route};
+
 	use test_client;
 
 	type Block = RawBlock<ExtrinsicWrapper<u64>>;
@@ -1521,7 +1560,6 @@ mod tests {
 		extrinsics_root: H256,
 	) -> H256 {
 		use sr_primitives::testing::Digest;
-
 		let (changes_root, changes_trie_update) = prepare_changes(changes);
 		let digest = Digest {
 			logs: vec![
@@ -2077,11 +2115,7 @@ mod tests {
 		let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default());
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a3),
-				BlockId::Hash(b2)
-			).unwrap();
+			let tree_route = tree_route(blockchain, a3, b2).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, block0);
 			assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
@@ -2089,11 +2123,7 @@ mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a1),
-				BlockId::Hash(a3),
-			).unwrap();
+			let tree_route = tree_route(blockchain, a1, a3).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a1);
 			assert!(tree_route.retracted().is_empty());
@@ -2101,11 +2131,7 @@ mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a3),
-				BlockId::Hash(a1),
-			).unwrap();
+			let tree_route = tree_route(blockchain, a3, a1).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a1);
 			assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
@@ -2113,11 +2139,7 @@ mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a2),
-				BlockId::Hash(a2),
-			).unwrap();
+			let tree_route = tree_route(blockchain, a2, a2).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a2);
 			assert!(tree_route.retracted().is_empty());
@@ -2134,11 +2156,7 @@ mod tests {
 		let block1 = insert_header(&backend, 1, block0, Vec::new(), Default::default());
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(block0),
-				BlockId::Hash(block1),
-			).unwrap();
+			let tree_route = tree_route(blockchain, block0, block1).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, block0);
 			assert!(tree_route.retracted().is_empty());
@@ -2146,6 +2164,64 @@ mod tests {
 		}
 	}
 
+	#[test]
+	fn lowest_common_ancestor_works() {
+		let backend = Backend::<Block>::new_test(1000, 100);
+		let blockchain = backend.blockchain();
+		let block0 = insert_header(&backend, 0, Default::default(), Vec::new(), Default::default());
+
+		// fork from genesis: 3 prong.
+		let a1 = insert_header(&backend, 1, block0, Vec::new(), Default::default());
+		let a2 = insert_header(&backend, 2, a1, Vec::new(), Default::default());
+		let a3 = insert_header(&backend, 3, a2, Vec::new(), Default::default());
+
+		// fork from genesis: 2 prong.
+		let b1 = insert_header(&backend, 1, block0, Vec::new(), H256::from([1; 32]));
+		let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default());
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a3, b2).unwrap();
+
+			assert_eq!(lca.hash, block0);
+			assert_eq!(lca.number, 0);
+		}
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a1, a3).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a3, a1).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a2, a3).unwrap();
+
+			assert_eq!(lca.hash, a2);
+			assert_eq!(lca.number, 2);
+		}
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a2, a1).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(blockchain, a2, a2).unwrap();
+
+			assert_eq!(lca.hash, a2);
+			assert_eq!(lca.number, 2);
+		}
+	}
+
 	#[test]
 	fn test_leaves_with_complex_block_tree() {
 		let backend: Arc<Backend<test_client::runtime::Block>> = Arc::new(Backend::new_test(20, 20));
diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs
index 07f805495cc1ee55d8e9c65f024d8ae215725ab5..111b8e0deb43b59a6f06485897e5da8363b40e51 100644
--- a/substrate/core/client/db/src/light.rs
+++ b/substrate/core/client/db/src/light.rs
@@ -35,6 +35,7 @@ use codec::{Decode, Encode};
 use primitives::Blake2Hasher;
 use sr_primitives::generic::{DigestItem, BlockId};
 use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, NumberFor};
+use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
 use crate::cache::{DbCacheSync, DbCache, ComplexBlockId, EntryType as CacheEntryType};
 use crate::utils::{self, meta_keys, Meta, db_err, read_db, block_id_to_lookup_key, read_meta};
 use crate::DatabaseSettings;
@@ -60,6 +61,7 @@ pub struct LightStorage<Block: BlockT> {
 	db: Arc<dyn KeyValueDB>,
 	meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
 	cache: Arc<DbCacheSync<Block>>,
+	header_metadata_cache: HeaderMetadataCache<Block>,
 }
 
 impl<Block> LightStorage<Block>
@@ -109,6 +111,7 @@ impl<Block> LightStorage<Block>
 			db,
 			meta: RwLock::new(meta),
 			cache: Arc::new(DbCacheSync(RwLock::new(cache))),
+			header_metadata_cache: HeaderMetadataCache::default(),
 		})
 	}
 
@@ -192,6 +195,31 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
 	}
 }
 
+impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> {
+	type Error = ClientError;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.header_metadata_cache.header_metadata(hash).or_else(|_| {
+			self.header(BlockId::hash(hash))?.map(|header| {
+				let header_metadata = CachedHeaderMetadata::from(&header);
+				self.header_metadata_cache.insert_header_metadata(
+					header_metadata.hash,
+					header_metadata.clone(),
+				);
+				header_metadata
+			}).ok_or(ClientError::UnknownBlock("header not found in db".to_owned()))
+		})
+	}
+
+	fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
+		self.header_metadata_cache.insert_header_metadata(hash, metadata)
+	}
+
+	fn remove_header_metadata(&self, hash: Block::Hash) {
+		self.header_metadata_cache.remove_header_metadata(hash);
+	}
+}
+
 impl<Block: BlockT> LightStorage<Block> {
 	// Get block changes trie root, if available.
 	fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
@@ -208,17 +236,18 @@ impl<Block: BlockT> LightStorage<Block> {
 	/// In the case where the new best block is a block to be imported, `route_to`
 	/// should be the parent of `best_to`. In the case where we set an existing block
 	/// to be best, `route_to` should equal to `best_to`.
-	fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(), client::error::Error> {
+	fn set_head_with_transaction(
+		&self,
+		transaction: &mut DBTransaction,
+		route_to: Block::Hash,
+		best_to: (NumberFor<Block>, Block::Hash),
+	) -> ClientResult<()> {
 		let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?;
 
 		// handle reorg.
 		let meta = self.meta.read();
 		if meta.best_hash != Default::default() {
-			let tree_route = ::client::blockchain::tree_route(
-				|id| self.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(meta.best_hash),
-				BlockId::Hash(route_to),
-			)?;
+			let tree_route = header_metadata::tree_route(self, meta.best_hash, route_to)?;
 
 			// update block number to hash lookup entries.
 			for retracted in tree_route.retracted() {
@@ -418,6 +447,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
 		)?;
 		transaction.put(columns::HEADER, &lookup_key, &header.encode());
 
+		let header_metadata = CachedHeaderMetadata::from(&header);
+		self.header_metadata_cache.insert_header_metadata(
+			header.hash().clone(),
+			header_metadata,
+		);
+
 		let is_genesis = number.is_zero();
 		if is_genesis {
 			self.cache.0.write().set_genesis_hash(hash);
@@ -537,6 +572,7 @@ pub(crate) mod tests {
 	use client::cht;
 	use sr_primitives::generic::DigestItem;
 	use sr_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper};
+	use header_metadata::{lowest_common_ancestor, tree_route};
 	use super::*;
 
 	type Block = RawBlock<ExtrinsicWrapper<u32>>;
@@ -781,11 +817,7 @@ pub(crate) mod tests {
 		let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a3),
-				BlockId::Hash(b2)
-			).unwrap();
+			let tree_route = tree_route(&db, a3, b2).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, block0);
 			assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
@@ -793,11 +825,7 @@ pub(crate) mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a1),
-				BlockId::Hash(a3),
-			).unwrap();
+			let tree_route = tree_route(&db, a1, a3).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a1);
 			assert!(tree_route.retracted().is_empty());
@@ -805,11 +833,7 @@ pub(crate) mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a3),
-				BlockId::Hash(a1),
-			).unwrap();
+			let tree_route = tree_route(&db, a3, a1).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a1);
 			assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
@@ -817,11 +841,7 @@ pub(crate) mod tests {
 		}
 
 		{
-			let tree_route = ::client::blockchain::tree_route(
-				|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(a2),
-				BlockId::Hash(a2),
-			).unwrap();
+			let tree_route = tree_route(&db, a2, a2).unwrap();
 
 			assert_eq!(tree_route.common_block().hash, a2);
 			assert!(tree_route.retracted().is_empty());
@@ -829,6 +849,63 @@ pub(crate) mod tests {
 		}
 	}
 
+	#[test]
+	fn lowest_common_ancestor_works() {
+		let db = LightStorage::new_test();
+		let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
+
+		// fork from genesis: 3 prong.
+		let a1 = insert_block(&db, HashMap::new(), || default_header(&block0, 1));
+		let a2 = insert_block(&db, HashMap::new(), || default_header(&a1, 2));
+		let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3));
+
+		// fork from genesis: 2 prong.
+		let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32])));
+		let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
+
+		{
+			let lca = lowest_common_ancestor(&db, a3, b2).unwrap();
+
+			assert_eq!(lca.hash, block0);
+			assert_eq!(lca.number, 0);
+		}
+
+		{
+			let lca = lowest_common_ancestor(&db, a1, a3).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(&db, a3, a1).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(&db, a2, a3).unwrap();
+
+			assert_eq!(lca.hash, a2);
+			assert_eq!(lca.number, 2);
+		}
+
+		{
+			let lca = lowest_common_ancestor(&db, a2, a1).unwrap();
+
+			assert_eq!(lca.hash, a1);
+			assert_eq!(lca.number, 1);
+		}
+
+		{
+			let lca = lowest_common_ancestor(&db, a2, a2).unwrap();
+
+			assert_eq!(lca.hash, a2);
+			assert_eq!(lca.number, 2);
+		}
+	}
+
 	#[test]
 	fn authorities_are_cached() {
 		let db = LightStorage::new_test();
diff --git a/substrate/core/client/header-metadata/Cargo.toml b/substrate/core/client/header-metadata/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..43fcebb25bf96647aae8faa8e949c973cea89784
--- /dev/null
+++ b/substrate/core/client/header-metadata/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "substrate-header-metadata"
+version = "2.0.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+
+[dependencies]
+lru-cache = { version = "0.1.2" }
+parking_lot = { version = "0.9.0" }
+sr-primitives = { path = "../../sr-primitives" }
diff --git a/substrate/core/client/header-metadata/src/lib.rs b/substrate/core/client/header-metadata/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cce45f264e8ea08bb543323f2bfcafd6b5c3165a
--- /dev/null
+++ b/substrate/core/client/header-metadata/src/lib.rs
@@ -0,0 +1,281 @@
+// Copyright 2019 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Implements tree backend, cached header metadata and algorithms
+//! to compute routes efficiently over the tree of headers.
+
+use sr_primitives::traits::{Block as BlockT, NumberFor, Header};
+use parking_lot::RwLock;
+use lru_cache::LruCache;
+
+/// Set to the expected max difference between `best` and `finalized` blocks at sync.
+const LRU_CACHE_SIZE: usize = 5_000;
+
+/// Get lowest common ancestor between two blocks in the tree.
+///
+/// This implementation is efficient because our trees have very few and
+/// small branches, and because of our current query pattern:
+/// lca(best, final), lca(best + 1, final), lca(best + 2, final), etc.
+/// The first call is O(h) but the others are O(1).
+pub fn lowest_common_ancestor<Block: BlockT, T: HeaderMetadata<Block>>(
+	backend: &T,
+	id_one: Block::Hash,
+	id_two: Block::Hash,
+) -> Result<HashAndNumber<Block>, T::Error> {
+	let mut header_one = backend.header_metadata(id_one)?;
+	let mut header_two = backend.header_metadata(id_two)?;
+
+	let mut orig_header_one = header_one.clone();
+	let mut orig_header_two = header_two.clone();
+
+	// We move through ancestor links as much as possible, since ancestor >= parent.
+
+	while header_one.number > header_two.number {
+		let ancestor_one = backend.header_metadata(header_one.ancestor)?;
+
+		if ancestor_one.number >= header_two.number {
+			header_one = ancestor_one;
+		} else {
+			break
+		}
+	}
+
+	while header_one.number < header_two.number {
+		let ancestor_two = backend.header_metadata(header_two.ancestor)?;
+
+		if ancestor_two.number >= header_one.number {
+			header_two = ancestor_two;
+		} else {
+			break
+		}
+	}
+
+	// Then we move the remaining path using parent links.
+
+	while header_one.hash != header_two.hash {
+		if header_one.number > header_two.number {
+			header_one = backend.header_metadata(header_one.parent)?;
+		} else {
+			header_two = backend.header_metadata(header_two.parent)?;
+		}
+	}
+
+	// Update cached ancestor links.
+
+	if orig_header_one.number > header_one.number {
+		orig_header_one.ancestor = header_one.hash;
+		backend.insert_header_metadata(orig_header_one.hash, orig_header_one);
+	}
+
+	if orig_header_two.number > header_one.number {
+		orig_header_two.ancestor = header_one.hash;
+		backend.insert_header_metadata(orig_header_two.hash, orig_header_two);
+	}
+
+	Ok(HashAndNumber {
+		hash: header_one.hash,
+		number: header_one.number,
+	})
+}
+
+/// Compute a tree-route between two blocks. See tree-route docs for more details.
+pub fn tree_route<Block: BlockT, T: HeaderMetadata<Block>>(
+	backend: &T,
+	from: Block::Hash,
+	to: Block::Hash,
+) -> Result<TreeRoute<Block>, T::Error> {
+	let mut from = backend.header_metadata(from)?;
+	let mut to = backend.header_metadata(to)?;
+
+	let mut from_branch = Vec::new();
+	let mut to_branch = Vec::new();
+
+	while to.number > from.number {
+		to_branch.push(HashAndNumber {
+			number: to.number,
+			hash: to.hash,
+		});
+
+		to = backend.header_metadata(to.parent)?;
+	}
+
+	while from.number > to.number {
+		from_branch.push(HashAndNumber {
+			number: from.number,
+			hash: from.hash,
+		});
+		from = backend.header_metadata(from.parent)?;
+	}
+
+	// numbers are equal now. walk backwards until the block is the same
+
+	while to != from {
+		to_branch.push(HashAndNumber {
+			number: to.number,
+			hash: to.hash,
+		});
+		to = backend.header_metadata(to.parent)?;
+
+		from_branch.push(HashAndNumber {
+			number: from.number,
+			hash: from.hash,
+		});
+		from = backend.header_metadata(from.parent)?;
+	}
+
+	// add the pivot block. and append the reversed to-branch (note that it's reverse order originals)
+	let pivot = from_branch.len();
+	from_branch.push(HashAndNumber {
+		number: to.number,
+		hash: to.hash,
+	});
+	from_branch.extend(to_branch.into_iter().rev());
+
+	Ok(TreeRoute {
+		route: from_branch,
+		pivot,
+	})
+}
+
+/// Hash and number of a block.
+#[derive(Debug)]
+pub struct HashAndNumber<Block: BlockT> {
+	/// The number of the block.
+	pub number: NumberFor<Block>,
+	/// The hash of the block.
+	pub hash: Block::Hash,
+}
+
+/// A tree-route from one block to another in the chain.
+///
+/// All blocks prior to the pivot in the deque is the reverse-order unique ancestry
+/// of the first block, the block at the pivot index is the common ancestor,
+/// and all blocks after the pivot is the ancestry of the second block, in
+/// order.
+///
+/// The ancestry sets will include the given blocks, and thus the tree-route is
+/// never empty.
+///
+/// ```text
+/// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2]
+///   <- R3 <- R2 <- R1
+///  /
+/// C
+///  \-> E1 -> E2
+/// ```
+///
+/// ```text
+/// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2]
+/// C -> E1 -> E2
+/// ```
+#[derive(Debug)]
+pub struct TreeRoute<Block: BlockT> {
+	route: Vec<HashAndNumber<Block>>,
+	pivot: usize,
+}
+
+impl<Block: BlockT> TreeRoute<Block> {
+	/// Get a slice of all retracted blocks in reverse order (towards common ancestor)
+	pub fn retracted(&self) -> &[HashAndNumber<Block>] {
+		&self.route[..self.pivot]
+	}
+
+	/// Get the common ancestor block. This might be one of the two blocks of the
+	/// route.
+	pub fn common_block(&self) -> &HashAndNumber<Block> {
+		self.route.get(self.pivot).expect("tree-routes are computed between blocks; \
+			which are included in the route; \
+			thus it is never empty; qed")
+	}
+
+	/// Get a slice of enacted blocks (descendents of the common ancestor)
+	pub fn enacted(&self) -> &[HashAndNumber<Block>] {
+		&self.route[self.pivot + 1 ..]
+	}
+}
+
+/// Handles header metadata: hash, number, parent hash, etc.
+pub trait HeaderMetadata<Block: BlockT> {
+	/// Error used in case the header metadata is not found.
+	type Error;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error>;
+	fn insert_header_metadata(&self, hash: Block::Hash, header_metadata: CachedHeaderMetadata<Block>);
+	fn remove_header_metadata(&self, hash: Block::Hash);
+}
+
+/// Caches header metadata in an in-memory LRU cache.
+pub struct HeaderMetadataCache<Block: BlockT> {
+	cache: RwLock<LruCache<Block::Hash, CachedHeaderMetadata<Block>>>,
+}
+
+impl<Block: BlockT> HeaderMetadataCache<Block> {
+	/// Creates a new LRU header metadata cache with `capacity`.
+	pub fn new(capacity: usize) -> Self {
+		HeaderMetadataCache {
+			cache: RwLock::new(LruCache::new(capacity)),
+		}
+	}
+}
+
+impl<Block: BlockT> Default for HeaderMetadataCache<Block> {
+	fn default() -> Self {
+		HeaderMetadataCache {
+			cache: RwLock::new(LruCache::new(LRU_CACHE_SIZE)),
+		}
+	}
+}
+
+impl<Block: BlockT> HeaderMetadata<Block> for HeaderMetadataCache<Block> {
+	type Error = String;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.cache.write().get_mut(&hash).cloned()
+			.ok_or("header metadata not found in cache".to_owned())
+	}
+
+	fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
+		self.cache.write().insert(hash, metadata);
+	}
+
+	fn remove_header_metadata(&self, hash: Block::Hash) {
+		self.cache.write().remove(&hash);
+	}
+}
+
+/// Cached header metadata. Used to efficiently traverse the tree.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct CachedHeaderMetadata<Block: BlockT> {
+	/// Hash of the header.
+	pub hash: Block::Hash,
+	/// Block number.
+	pub number: NumberFor<Block>,
+	/// Hash of parent header.
+	pub parent: Block::Hash,
+	/// Hash of an ancestor header. Used to jump through the tree.
+	ancestor: Block::Hash,
+}
+
+impl<Block: BlockT> From<&Block::Header> for CachedHeaderMetadata<Block> {
+	fn from(header: &Block::Header) -> Self {
+		CachedHeaderMetadata {
+			hash: header.hash().clone(),
+			number: header.number().clone(),
+			parent: header.parent_hash().clone(),
+			ancestor: header.parent_hash().clone(),
+		}
+	}
+}
diff --git a/substrate/core/client/src/blockchain.rs b/substrate/core/client/src/blockchain.rs
index 7cf510faf91be0d82f53a70a3a1cce70e414490e..73b7c138d020e62238750b72fe8bf202e42ec109 100644
--- a/substrate/core/client/src/blockchain.rs
+++ b/substrate/core/client/src/blockchain.rs
@@ -24,6 +24,8 @@ use sr_primitives::Justification;
 use log::warn;
 use parking_lot::Mutex;
 
+use header_metadata::HeaderMetadata;
+
 use crate::error::{Error, Result};
 
 /// Blockchain database header backend. Does not perform any validation.
@@ -74,7 +76,7 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync {
 }
 
 /// Blockchain database backend. Does not perform any validation.
-pub trait Backend<Block: BlockT>: HeaderBackend<Block> {
+pub trait Backend<Block: BlockT>: HeaderBackend<Block> + HeaderMetadata<Block, Error=Error> {
 	/// Get block body. Returns `None` if block is not found.
 	fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
 	/// Get block justification. Returns `None` if justification does not exist.
@@ -257,122 +259,6 @@ pub enum BlockStatus {
 	Unknown,
 }
 
-/// An entry in a tree route.
-#[derive(Debug)]
-pub struct RouteEntry<Block: BlockT> {
-	/// The number of the block.
-	pub number: <Block::Header as HeaderT>::Number,
-	/// The hash of the block.
-	pub hash: Block::Hash,
-}
-
-/// A tree-route from one block to another in the chain.
-///
-/// All blocks prior to the pivot in the deque is the reverse-order unique ancestry
-/// of the first block, the block at the pivot index is the common ancestor,
-/// and all blocks after the pivot is the ancestry of the second block, in
-/// order.
-///
-/// The ancestry sets will include the given blocks, and thus the tree-route is
-/// never empty.
-///
-/// ```text
-/// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2]
-///   <- R3 <- R2 <- R1
-///  /
-/// C
-///  \-> E1 -> E2
-/// ```
-///
-/// ```text
-/// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2]
-/// C -> E1 -> E2
-/// ```
-#[derive(Debug)]
-pub struct TreeRoute<Block: BlockT> {
-	route: Vec<RouteEntry<Block>>,
-	pivot: usize,
-}
-
-impl<Block: BlockT> TreeRoute<Block> {
-	/// Get a slice of all retracted blocks in reverse order (towards common ancestor)
-	pub fn retracted(&self) -> &[RouteEntry<Block>] {
-		&self.route[..self.pivot]
-	}
-
-	/// Get the common ancestor block. This might be one of the two blocks of the
-	/// route.
-	pub fn common_block(&self) -> &RouteEntry<Block> {
-		self.route.get(self.pivot).expect("tree-routes are computed between blocks; \
-			which are included in the route; \
-			thus it is never empty; qed")
-	}
-
-	/// Get a slice of enacted blocks (descendents of the common ancestor)
-	pub fn enacted(&self) -> &[RouteEntry<Block>] {
-		&self.route[self.pivot + 1 ..]
-	}
-}
-
-/// Compute a tree-route between two blocks. See tree-route docs for more details.
-pub fn tree_route<Block: BlockT, F: Fn(BlockId<Block>) -> Result<<Block as BlockT>::Header>>(
-	load_header: F,
-	from: BlockId<Block>,
-	to: BlockId<Block>,
-) -> Result<TreeRoute<Block>> {
-	let mut from = load_header(from)?;
-	let mut to = load_header(to)?;
-
-	let mut from_branch = Vec::new();
-	let mut to_branch = Vec::new();
-
-	while to.number() > from.number() {
-		to_branch.push(RouteEntry {
-			number: to.number().clone(),
-			hash: to.hash(),
-		});
-
-		to = load_header(BlockId::Hash(*to.parent_hash()))?;
-	}
-
-	while from.number() > to.number() {
-		from_branch.push(RouteEntry {
-			number: from.number().clone(),
-			hash: from.hash(),
-		});
-		from = load_header(BlockId::Hash(*from.parent_hash()))?;
-	}
-
-	// numbers are equal now. walk backwards until the block is the same
-
-	while to != from {
-		to_branch.push(RouteEntry {
-			number: to.number().clone(),
-			hash: to.hash(),
-		});
-		to = load_header(BlockId::Hash(*to.parent_hash()))?;
-
-		from_branch.push(RouteEntry {
-			number: from.number().clone(),
-			hash: from.hash(),
-		});
-		from = load_header(BlockId::Hash(*from.parent_hash()))?;
-	}
-
-	// add the pivot block. and append the reversed to-branch (note that it's reverse order originalls)
-	let pivot = from_branch.len();
-	from_branch.push(RouteEntry {
-		number: to.number().clone(),
-		hash: to.hash(),
-	});
-	from_branch.extend(to_branch.into_iter().rev());
-
-	Ok(TreeRoute {
-		route: from_branch,
-		pivot,
-	})
-}
-
 /// A list of all well known keys in the blockchain cache.
 pub mod well_known_cache_keys {
 	/// The type representing cache keys.
diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs
index 595aa0e9c7e7e2c7453c2e6fdc7ca27a2c119078..aff099233a41e05da7441bd55517bb1ab31bedc6 100644
--- a/substrate/core/client/src/client.rs
+++ b/substrate/core/client/src/client.rs
@@ -51,6 +51,7 @@ use consensus::{
 	ImportResult, BlockOrigin, ForkChoiceStrategy,
 	SelectChain, self,
 };
+use header_metadata::{HeaderMetadata, CachedHeaderMetadata};
 
 use crate::{
 	runtime_api::{
@@ -966,10 +967,10 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 		};
 
 		let retracted = if is_new_best {
-			let route_from_best = crate::blockchain::tree_route(
-				|id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(info.best_hash),
-				BlockId::Hash(parent_hash),
+			let route_from_best = header_metadata::tree_route(
+				self.backend.blockchain(),
+				info.best_hash,
+				parent_hash,
 			)?;
 			route_from_best.retracted().iter().rev().map(|e| e.hash.clone()).collect()
 		} else {
@@ -1100,11 +1101,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 			return Ok(());
 		}
 
-		let route_from_finalized = crate::blockchain::tree_route(
-			|id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))),
-			BlockId::Hash(last_finalized),
-			BlockId::Hash(block),
-		)?;
+		let route_from_finalized = header_metadata::tree_route(self.backend.blockchain(), last_finalized, block)?;
 
 		if let Some(retracted) = route_from_finalized.retracted().get(0) {
 			warn!("Safety violation: attempted to revert finalized block {:?} which is not in the \
@@ -1113,11 +1110,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 			return Err(error::Error::NotInFinalizedChain);
 		}
 
-		let route_from_best = crate::blockchain::tree_route(
-			|id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))),
-			BlockId::Hash(best_block),
-			BlockId::Hash(block),
-		)?;
+		let route_from_best = header_metadata::tree_route(self.backend.blockchain(), best_block, block)?;
 
 		// if the block is not a direct ancestor of the current best chain,
 		// then some other block is the common ancestor.
@@ -1321,6 +1314,26 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 	}
 }
 
+impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA> where
+	B: backend::Backend<Block, Blake2Hasher>,
+	E: CallExecutor<Block, Blake2Hasher>,
+	Block: BlockT<Hash=H256>,
+{
+	type Error = error::Error;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.backend.blockchain().header_metadata(hash)
+	}
+
+	fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
+		self.backend.blockchain().insert_header_metadata(hash, metadata)
+	}
+
+	fn remove_header_metadata(&self, hash: Block::Hash) {
+		self.backend.blockchain().remove_header_metadata(hash)
+	}
+}
+
 impl<B, E, Block, RA> ProvideUncles<Block> for Client<B, E, Block, RA> where
 	B: backend::Backend<Block, Blake2Hasher>,
 	E: CallExecutor<Block, Blake2Hasher>,
@@ -1798,7 +1811,7 @@ where
 /// Utility methods for the client.
 pub mod utils {
 	use super::*;
-	use crate::{blockchain, error};
+	use crate::error;
 	use primitives::H256;
 	use std::borrow::Borrow;
 
@@ -1812,7 +1825,7 @@ pub mod utils {
 		client: &'a T,
 		current: Option<(H, H)>,
 	) -> impl Fn(&H256, &H256) -> Result<bool, error::Error> + 'a
-		where T: ChainHeaderBackend<Block>,
+		where T: ChainHeaderBackend<Block> + HeaderMetadata<Block, Error=error::Error>,
 	{
 		move |base, hash| {
 			if base == hash { return Ok(false); }
@@ -1831,13 +1844,9 @@ pub mod utils {
 				}
 			}
 
-			let tree_route = blockchain::tree_route(
-				|id| client.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))),
-				BlockId::Hash(*hash),
-				BlockId::Hash(*base),
-			)?;
+			let ancestor = header_metadata::lowest_common_ancestor(client, *hash, *base)?;
 
-			Ok(tree_route.common_block().hash == *base)
+			Ok(ancestor.hash == *base)
 		}
 	}
 }
diff --git a/substrate/core/client/src/in_mem.rs b/substrate/core/client/src/in_mem.rs
index debfbbd6c75c01e9b5578adc71623f2d0ec234cb..99dc1b62634ba13d8caa80fe00ca7673c1e0c3ab 100644
--- a/substrate/core/client/src/in_mem.rs
+++ b/substrate/core/client/src/in_mem.rs
@@ -27,12 +27,15 @@ use state_machine::backend::{Backend as StateBackend, InMemory};
 use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId, ChangesTrieTransaction};
 use hash_db::{Hasher, Prefix};
 use trie::MemoryDB;
+use header_metadata::{CachedHeaderMetadata, HeaderMetadata};
 
 use crate::error;
 use crate::backend::{self, NewBlockState, StorageCollection, ChildStorageCollection};
 use crate::light;
 use crate::leaves::LeafSet;
-use crate::blockchain::{self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId};
+use crate::blockchain::{
+	self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId
+};
 
 struct PendingBlock<B: BlockT> {
 	block: StoredBlock<B>,
@@ -221,11 +224,7 @@ impl<Block: BlockT> Blockchain<Block> {
 			if &best_hash == header.parent_hash() {
 				None
 			} else {
-				let route = crate::blockchain::tree_route(
-					|id| self.header(id)?.ok_or_else(|| error::Error::UnknownBlock(format!("{:?}", id))),
-					BlockId::Hash(best_hash),
-					BlockId::Hash(*header.parent_hash()),
-				)?;
+				let route = header_metadata::tree_route(self, best_hash, *header.parent_hash())?;
 				Some(route)
 			}
 		};
@@ -320,6 +319,21 @@ impl<Block: BlockT> HeaderBackend<Block> for Blockchain<Block> {
 	}
 }
 
+impl<Block: BlockT> HeaderMetadata<Block> for Blockchain<Block> {
+	type Error = error::Error;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.header(BlockId::hash(hash))?.map(|header| CachedHeaderMetadata::from(&header))
+			.ok_or(error::Error::UnknownBlock("header not found".to_owned()))
+	}
+
+	fn insert_header_metadata(&self, _hash: Block::Hash, _metadata: CachedHeaderMetadata<Block>) {
+		// No need to implement.
+	}
+	fn remove_header_metadata(&self, _hash: Block::Hash) {
+		// No need to implement.
+	}
+}
 
 impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
 	fn body(&self, id: BlockId<Block>) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
diff --git a/substrate/core/client/src/light/blockchain.rs b/substrate/core/client/src/light/blockchain.rs
index 8fef351b7caf7ab80fa3ec388cc9e3492fe1a4a6..202f94cd74fed91b731781ef2d5311cb0bc24f28 100644
--- a/substrate/core/client/src/light/blockchain.rs
+++ b/substrate/core/client/src/light/blockchain.rs
@@ -23,18 +23,20 @@ use std::{sync::Arc, collections::HashMap};
 use sr_primitives::{Justification, generic::BlockId};
 use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
 
+use header_metadata::{HeaderMetadata, CachedHeaderMetadata};
+
 use crate::backend::{AuxStore, NewBlockState};
 use crate::blockchain::{
 	Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
 	HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache,
-	well_known_cache_keys,
+	well_known_cache_keys
 };
 use crate::cht;
 use crate::error::{Error as ClientError, Result as ClientResult};
 use crate::light::fetcher::{Fetcher, RemoteHeaderRequest};
 
 /// Light client blockchain storage.
-pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> {
+pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError> {
 	/// Store new header. Should refuse to revert any finalized blocks.
 	///
 	/// Takes new authorities, the leaf state of the new block, and
@@ -140,6 +142,22 @@ impl<S, Block> BlockchainHeaderBackend<Block> for Blockchain<S> where Block: Blo
 	}
 }
 
+impl<S, Block> HeaderMetadata<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
+	type Error = ClientError;
+
+	fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+		self.storage.header_metadata(hash)
+	}
+
+	fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
+		self.storage.insert_header_metadata(hash, metadata)
+	}
+
+	fn remove_header_metadata(&self, hash: Block::Hash) {
+		self.storage.remove_header_metadata(hash)
+	}
+}
+
 impl<S, Block> BlockchainBackend<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
 	fn body(&self, _id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
 		Err(ClientError::NotAvailableOnLightClient)
@@ -281,6 +299,17 @@ pub mod tests {
 		}
 	}
 
+	impl HeaderMetadata<Block> for DummyStorage {
+		type Error = ClientError;
+
+		fn header_metadata(&self, hash: Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
+			self.header(BlockId::hash(hash))?.map(|header| CachedHeaderMetadata::from(&header))
+				.ok_or(ClientError::UnknownBlock("header not found".to_owned()))
+		}
+		fn insert_header_metadata(&self, _hash: Hash, _metadata: CachedHeaderMetadata<Block>) {}
+		fn remove_header_metadata(&self, _hash: Hash) {}
+	}
+
 	impl AuxStore for DummyStorage {
 		fn insert_aux<
 			'a,
diff --git a/substrate/core/consensus/babe/Cargo.toml b/substrate/core/consensus/babe/Cargo.toml
index a6d1833b292cc455b61dcafdb0140d7d3d1b120c..b268a6f41b6d41b679eec3567287fe16622c9b87 100644
--- a/substrate/core/consensus/babe/Cargo.toml
+++ b/substrate/core/consensus/babe/Cargo.toml
@@ -21,6 +21,7 @@ substrate-telemetry = { path = "../../telemetry" }
 keystore = { package = "substrate-keystore", path = "../../keystore" }
 srml-babe = { path = "../../../srml/babe" }
 client = { package = "substrate-client", path = "../../client" }
+header-metadata = { package = "substrate-header-metadata", path = "../../client/header-metadata" }
 consensus-common = { package = "substrate-consensus-common", path = "../common" }
 uncles = { package = "substrate-consensus-uncles", path = "../uncles" }
 slots = { package = "substrate-consensus-slots", path = "../slots"  }
diff --git a/substrate/core/consensus/babe/src/epoch_changes.rs b/substrate/core/consensus/babe/src/epoch_changes.rs
index 0dbad245e644ee881c7cef4ed6f14b532591d618..311271ae15cc7126e2cfe24b66c4b32248b24371 100644
--- a/substrate/core/consensus/babe/src/epoch_changes.rs
+++ b/substrate/core/consensus/babe/src/epoch_changes.rs
@@ -28,6 +28,7 @@ use codec::{Encode, Decode};
 use client::error::Error as ClientError;
 use client::utils as client_utils;
 use client::blockchain::HeaderBackend;
+use header_metadata::HeaderMetadata;
 use primitives::H256;
 use std::ops::Add;
 
@@ -62,7 +63,7 @@ pub(crate) struct HeaderBackendDescendentBuilder<H, Block>(H, std::marker::Phant
 // https://github.com/paritytech/substrate/issues/3624
 impl<'a, H, Block> IsDescendentOfBuilder<H256>
 	for HeaderBackendDescendentBuilder<&'a H, Block> where
-	H: HeaderBackend<Block>,
+	H: HeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError>,
 	Block: BlockT<Hash = H256>,
 {
 	type Error = ClientError;
diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs
index bd2830d4e74838b84607d2cfb90074b9cd033a67..d2a16bedb844fbdc3cce76372fd41336da5f0347 100644
--- a/substrate/core/consensus/babe/src/lib.rs
+++ b/substrate/core/consensus/babe/src/lib.rs
@@ -93,7 +93,7 @@ use consensus_common::import_queue::{Verifier, BasicQueue, CacheKeyId};
 use client::{
 	block_builder::api::BlockBuilder as BlockBuilderApi,
 	blockchain::{self, HeaderBackend, ProvideCache}, BlockchainEvents, CallExecutor, Client,
-	error::Result as ClientResult, backend::{AuxStore, Backend},
+	error::Result as ClientResult, error::Error as ClientError, backend::{AuxStore, Backend},
 	ProvideUncles,
 };
 use slots::{CheckedHeader, check_equivocation};
@@ -101,6 +101,8 @@ use futures::prelude::*;
 use log::{warn, debug, info, trace};
 use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible};
 use epoch_changes::descendent_query;
+use header_metadata::HeaderMetadata;
+
 mod aux_schema;
 mod verification;
 mod epoch_changes;
@@ -223,7 +225,7 @@ pub fn start_babe<B, C, SC, E, I, SO, Error>(BabeParams {
 > where
 	B: BlockT<Hash=H256>,
 	C: ProvideRuntimeApi + ProvideCache<B> + ProvideUncles<B> + BlockchainEvents<B>
-		+ HeaderBackend<B> + Send + Sync + 'static,
+		+ HeaderBackend<B> + HeaderMetadata<B, Error=ClientError> + Send + Sync + 'static,
 	C::Api: BabeApi<B>,
 	SC: SelectChain<B> + 'static,
 	E: Environment<B, Error=Error> + Send + Sync,
@@ -296,7 +298,7 @@ struct BabeWorker<B: BlockT, C, E, I, SO> {
 
 impl<B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<B, C, E, I, SO> where
 	B: BlockT<Hash=H256>,
-	C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B>,
+	C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError>,
 	C::Api: BabeApi<B>,
 	E: Environment<B, Error=Error>,
 	E::Proposer: Proposer<B, Error=Error>,
@@ -408,7 +410,7 @@ impl<B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<B, C, E, I
 
 impl<B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<B, C, E, I, SO> where
 	B: BlockT<Hash=H256>,
-	C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + Send + Sync,
+	C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError> + Send + Sync,
 	C::Api: BabeApi<B>,
 	E: Environment<B, Error=Error> + Send + Sync,
 	E::Proposer: Proposer<B, Error=Error>,
@@ -1071,7 +1073,7 @@ pub mod test_helpers {
 		link: &BabeLink<B>,
 	) -> Option<BabePreDigest> where
 		B: BlockT<Hash=H256>,
-		C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B>,
+		C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError>,
 		C::Api: BabeApi<B>,
 	{
 		let epoch = link.epoch_changes.lock().epoch_for_child_of(
diff --git a/substrate/core/finality-grandpa/Cargo.toml b/substrate/core/finality-grandpa/Cargo.toml
index f2e1dd490bb370176a6380abcbaff108d5dfa47d..4e835e14f8dfe6aece78f5a65ae26c48d2aca1b2 100644
--- a/substrate/core/finality-grandpa/Cargo.toml
+++ b/substrate/core/finality-grandpa/Cargo.toml
@@ -21,6 +21,7 @@ substrate-telemetry = { path = "../telemetry" }
 keystore = { package = "substrate-keystore", path = "../keystore" }
 serde_json = "1.0"
 client = { package = "substrate-client", path = "../client" }
+header-metadata = { package = "substrate-header-metadata", path = "../client/header-metadata" }
 inherents = { package = "substrate-inherents", path = "../../core/inherents" }
 network = { package = "substrate-network", path = "../network" }
 srml-finality-tracker = { path = "../../srml/finality-tracker" }
diff --git a/substrate/core/finality-grandpa/src/environment.rs b/substrate/core/finality-grandpa/src/environment.rs
index 5e3abb3ac7b265415337d7d79067e021da5132fa..70e45848be1dd10192cc477466cb81d5aa973cb7 100644
--- a/substrate/core/finality-grandpa/src/environment.rs
+++ b/substrate/core/finality-grandpa/src/environment.rs
@@ -498,11 +498,7 @@ pub(crate) fn ancestry<B, Block: BlockT<Hash=H256>, E, RA>(
 {
 	if base == block { return Err(GrandpaError::NotDescendent) }
 
-	let tree_route_res = ::client::blockchain::tree_route(
-		|id| client.header(&id)?.ok_or(client::error::Error::UnknownBlock(format!("{:?}", id))),
-		BlockId::Hash(block),
-		BlockId::Hash(base),
-	);
+	let tree_route_res = header_metadata::tree_route(client, block, base);
 
 	let tree_route = match tree_route_res {
 		Ok(tree_route) => tree_route,
diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml
index 9a0a4fc678be5565e4ae572546ca962916117134..e8d84e1c6f33013127eaea4c67ad62af55952ff3 100644
--- a/substrate/core/network/Cargo.toml
+++ b/substrate/core/network/Cargo.toml
@@ -19,13 +19,14 @@ futures03 = { package = "futures-preview", version = "0.3.0-alpha.18", features
 futures-timer = "0.3"
 linked-hash-map = "0.5"
 linked_hash_set = "0.1.3"
-lru-cache = "0.1.1"
+lru-cache = "0.1.2"
 rustc-hex = "2.0"
 rand = "0.6"
 libp2p = { version = "0.12.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] }
 fork-tree = { path = "../../core/utils/fork-tree" }
 consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" }
 client = { package = "substrate-client", path = "../../core/client" }
+header_metadata = { package = "substrate-header-metadata", path = "../../core/client/header-metadata" }
 sr-primitives = { path = "../../core/sr-primitives" }
 primitives = { package = "substrate-primitives", path = "../../core/primitives" }
 codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] }
diff --git a/substrate/core/network/src/chain.rs b/substrate/core/network/src/chain.rs
index d23068179ff5568a972b84bba1446a67b71e2cf7..1a1f649cae2dd62946678486a5131c357b89b0a9 100644
--- a/substrate/core/network/src/chain.rs
+++ b/substrate/core/network/src/chain.rs
@@ -153,14 +153,8 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
 			return Ok(false);
 		}
 
-		let tree_route = ::client::blockchain::tree_route(
-			|id| self.header(&id)?.ok_or_else(||
-				client::error::Error::UnknownBlock(format!("{:?}", id))
-			),
-			BlockId::Hash(*block),
-			BlockId::Hash(*base),
-		)?;
-
-		Ok(tree_route.common_block().hash == *base)
+		let ancestor = header_metadata::lowest_common_ancestor(self, *block, *base)?;
+
+		Ok(ancestor.hash == *base)
 	}
 }
diff --git a/substrate/core/test-runtime/client/src/lib.rs b/substrate/core/test-runtime/client/src/lib.rs
index d2cf704fdfb01481049cd5c3c160ad377e2017fd..722aa0b2b472601b6003bc3bf805ef1324acfecd 100644
--- a/substrate/core/test-runtime/client/src/lib.rs
+++ b/substrate/core/test-runtime/client/src/lib.rs
@@ -78,7 +78,7 @@ pub type LightExecutor = client::light::call_executor::GenesisCallExecutor<
 	client::LocalCallExecutor<
 		client::light::backend::Backend<
 			client_db::light::LightStorage<runtime::Block>,
-			Blake2Hasher
+			Blake2Hasher,
 		>,
 		NativeExecutor<LocalExecutor>
 	>