diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs
index 6b2f2f5c0af4c83c35e805721c225f1188031137..336e4cba705714d1e29e6560fec0d09733736972 100644
--- a/core/client/src/light/backend.rs
+++ b/core/client/src/light/backend.rs
@@ -18,7 +18,7 @@
 //! Everything else is requested from full nodes on demand.
 
 use std::collections::HashMap;
-use std::sync::{Arc, Weak};
+use std::sync::Arc;
 use parking_lot::{RwLock, Mutex};
 
 use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay};
@@ -32,7 +32,6 @@ use crate::backend::{
 use crate::blockchain::HeaderBackend as BlockchainHeaderBackend;
 use crate::error::{Error as ClientError, Result as ClientResult};
 use crate::light::blockchain::{Blockchain, Storage as BlockchainStorage};
-use crate::light::fetcher::{Fetcher, RemoteReadRequest};
 use hash_db::Hasher;
 use trie::MemoryDB;
 use consensus::well_known_cache_keys;
@@ -40,14 +39,14 @@ use consensus::well_known_cache_keys;
 const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always succeeds; qed";
 
 /// Light client backend.
-pub struct Backend<S, F, H: Hasher> {
-	blockchain: Arc<Blockchain<S, F>>,
+pub struct Backend<S, H: Hasher> {
+	blockchain: Arc<Blockchain<S>>,
 	genesis_state: RwLock<Option<InMemoryState<H>>>,
 	import_lock: Mutex<()>,
 }
 
 /// Light block (header and justification) import operation.
-pub struct ImportOperation<Block: BlockT, S, F, H: Hasher> {
+pub struct ImportOperation<Block: BlockT, S, H: Hasher> {
 	header: Option<Block::Header>,
 	cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
 	leaf_state: NewBlockState,
@@ -55,28 +54,21 @@ pub struct ImportOperation<Block: BlockT, S, F, H: Hasher> {
 	finalized_blocks: Vec<BlockId<Block>>,
 	set_head: Option<BlockId<Block>>,
 	storage_update: Option<InMemoryState<H>>,
-	_phantom: ::std::marker::PhantomData<(S, F)>,
+	_phantom: ::std::marker::PhantomData<(S)>,
 }
 
-/// On-demand state.
-pub struct OnDemandState<Block: BlockT, S, F> {
-	fetcher: Weak<F>,
-	blockchain: Weak<Blockchain<S, F>>,
-	block: Block::Hash,
-	cached_header: RwLock<Option<Block::Header>>,
-}
-
-/// On-demand or in-memory genesis state.
-pub enum OnDemandOrGenesisState<Block: BlockT, S, F, H: Hasher> {
-	/// On-demand state - storage values are fetched from remote nodes.
-	OnDemand(OnDemandState<Block, S, F>),
+/// Either in-memory genesis state, or locally-unavailable state.
+pub enum GenesisOrUnavailableState<H: Hasher> {
 	/// Genesis state - storage values are stored in-memory.
 	Genesis(InMemoryState<H>),
+	/// We know that state exists, but all calls will fail with error, because it
+	/// isn't locally available.
+	Unavailable,
 }
 
-impl<S, F, H: Hasher> Backend<S, F, H> {
+impl<S, H: Hasher> Backend<S, H> {
 	/// Create new light backend.
-	pub fn new(blockchain: Arc<Blockchain<S, F>>) -> Self {
+	pub fn new(blockchain: Arc<Blockchain<S>>) -> Self {
 		Self {
 			blockchain,
 			genesis_state: RwLock::new(None),
@@ -85,12 +77,12 @@ impl<S, F, H: Hasher> Backend<S, F, H> {
 	}
 
 	/// Get shared blockchain reference.
-	pub fn blockchain(&self) -> &Arc<Blockchain<S, F>> {
+	pub fn blockchain(&self) -> &Arc<Blockchain<S>> {
 		&self.blockchain
 	}
 }
 
-impl<S: AuxStore, F, H: Hasher> AuxStore for Backend<S, F, H> {
+impl<S: AuxStore, H: Hasher> AuxStore for Backend<S, H> {
 	fn insert_aux<
 		'a,
 		'b: 'a,
@@ -106,16 +98,15 @@ impl<S: AuxStore, F, H: Hasher> AuxStore for Backend<S, F, H> {
 	}
 }
 
-impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
+impl<S, Block, H> ClientBackend<Block, H> for Backend<S, H> where
 	Block: BlockT,
 	S: BlockchainStorage<Block>,
-	F: Fetcher<Block>,
 	H: Hasher<Out=Block::Hash>,
 	H::Out: Ord,
 {
-	type BlockImportOperation = ImportOperation<Block, S, F, H>;
-	type Blockchain = Blockchain<S, F>;
-	type State = OnDemandOrGenesisState<Block, S, F, H>;
+	type BlockImportOperation = ImportOperation<Block, S, H>;
+	type Blockchain = Blockchain<S>;
+	type State = GenesisOrUnavailableState<H>;
 	type ChangesTrieStorage = in_mem::ChangesTrieStorage<Block, H>;
 	type OffchainStorage = in_mem::OffchainStorage;
 
@@ -183,7 +174,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
 		self.blockchain.storage().finalize_header(block)
 	}
 
-	fn blockchain(&self) -> &Blockchain<S, F> {
+	fn blockchain(&self) -> &Blockchain<S> {
 		&self.blockchain
 	}
 
@@ -205,22 +196,17 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
 		// special case for genesis block
 		if block_number.is_zero() {
 			if let Some(genesis_state) = self.genesis_state.read().clone() {
-				return Ok(OnDemandOrGenesisState::Genesis(genesis_state));
+				return Ok(GenesisOrUnavailableState::Genesis(genesis_state));
 			}
 		}
 
-		// else create on-demand state
-		let block_hash = self.blockchain.expect_block_hash_from_id(&block)?;
-		Ok(OnDemandOrGenesisState::OnDemand(OnDemandState {
-			fetcher: self.blockchain.fetcher(),
-			blockchain: Arc::downgrade(&self.blockchain),
-			block: block_hash,
-			cached_header: RwLock::new(None),
-		}))
+		// else return unavailable state. We do not return error here, because error
+		// would mean that we do not know this state at all. But we know that it exists
+		Ok(GenesisOrUnavailableState::Unavailable)
 	}
 
 	fn revert(&self, _n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
-		Err(ClientError::NotAvailableOnLightClient.into())
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 
 	fn get_import_lock(&self) -> &Mutex<()> {
@@ -228,11 +214,10 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
 	}
 }
 
-impl<S, F, Block, H> RemoteBackend<Block, H> for Backend<S, F, H>
+impl<S, Block, H> RemoteBackend<Block, H> for Backend<S, H>
 where
 	Block: BlockT,
 	S: BlockchainStorage<Block> + 'static,
-	F: Fetcher<Block> + 'static,
 	H: Hasher<Out=Block::Hash>,
 	H::Out: Ord,
 {
@@ -248,15 +233,14 @@ where
 	}
 }
 
-impl<S, F, Block, H> BlockImportOperation<Block, H> for ImportOperation<Block, S, F, H>
+impl<S, Block, H> BlockImportOperation<Block, H> for ImportOperation<Block, S, H>
 where
 	Block: BlockT,
-	F: Fetcher<Block>,
 	S: BlockchainStorage<Block>,
 	H: Hasher<Out=Block::Hash>,
 	H::Out: Ord,
 {
-	type State = OnDemandOrGenesisState<Block, S, F, H>;
+	type State = GenesisOrUnavailableState<H>;
 
 	fn state(&self) -> ClientResult<Option<&Self::State>> {
 		// None means 'locally-stateless' backend
@@ -341,99 +325,9 @@ where
 	}
 }
 
-impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
-where
-	Block: BlockT,
-	S: BlockchainStorage<Block>,
-	F: Fetcher<Block>,
-	H: Hasher<Out=Block::Hash>,
-{
-	type Error = ClientError;
-	type Transaction = ();
-	type TrieBackendStorage = MemoryDB<H>;
-
-	fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
-		let mut header = self.cached_header.read().clone();
-		if header.is_none() {
-			let cached_header = self.blockchain.upgrade()
-				.ok_or_else(|| ClientError::UnknownBlock(format!("{}", self.block)))
-				.and_then(|blockchain| blockchain.expect_header(BlockId::Hash(self.block)))?;
-			header = Some(cached_header.clone());
-			*self.cached_header.write() = Some(cached_header);
-		}
-
-		futures03::executor::block_on(
-			self.fetcher.upgrade().ok_or(ClientError::NotAvailableOnLightClient)?
-				.remote_read(RemoteReadRequest {
-					block: self.block,
-					header: header.expect("if block above guarantees that header is_some(); qed"),
-					key: key.to_vec(),
-					retry_count: None,
-				})
-		)
-	}
-
-	fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
-		Err(ClientError::NotAvailableOnLightClient.into())
-	}
-
-	fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, _prefix: &[u8], _action: A) {
-		// whole state is not available on light node
-	}
-
-	fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, _prefix: &[u8], _action: A) {
-		// whole state is not available on light node
-	}
-
-	fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, _storage_key: &[u8], _action: A) {
-		// whole state is not available on light node
-	}
-
-	fn for_child_keys_with_prefix<A: FnMut(&[u8])>(
-		&self,
-		_storage_key: &[u8],
-		_prefix: &[u8],
-		_action: A,
-	) {
-		// whole state is not available on light node
-	}
-
-	fn storage_root<I>(&self, _delta: I) -> (H::Out, Self::Transaction)
-	where
-		I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
-	{
-		(H::Out::default(), ())
-	}
-
-	fn child_storage_root<I>(&self, _key: &[u8], _delta: I) -> (Vec<u8>, bool, Self::Transaction)
+impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
 	where
-		I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
-	{
-		(H::Out::default().as_ref().to_vec(), true, ())
-	}
-
-	fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
-		// whole state is not available on light node
-		Vec::new()
-	}
-
-	fn keys(&self, _prefix: &[u8]) -> Vec<Vec<u8>> {
-		// whole state is not available on light node
-		Vec::new()
-	}
-
-	fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
-		None
-	}
-}
-
-impl<Block, S, F, H> StateBackend<H> for OnDemandOrGenesisState<Block, S, F, H>
-where
-	Block: BlockT,
-	F: Fetcher<Block>,
-	S: BlockchainStorage<Block>,
-	H: Hasher<Out=Block::Hash>,
-	H::Out: Ord,
+		H::Out: Ord,
 {
 	type Error = ClientError;
 	type Transaction = ();
@@ -441,44 +335,39 @@ where
 
 	fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::storage(state, key),
-			OnDemandOrGenesisState::Genesis(ref state) =>
+			GenesisOrUnavailableState::Genesis(ref state) =>
 				Ok(state.storage(key).expect(IN_MEMORY_EXPECT_PROOF)),
+			GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
 		}
 	}
 
 	fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::child_storage(state, storage_key, key),
-			OnDemandOrGenesisState::Genesis(ref state) =>
+			GenesisOrUnavailableState::Genesis(ref state) =>
 				Ok(state.child_storage(storage_key, key).expect(IN_MEMORY_EXPECT_PROOF)),
+			GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
 		}
 	}
 
 	fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, prefix: &[u8], action: A) {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::for_keys_with_prefix(state, prefix, action),
-			OnDemandOrGenesisState::Genesis(ref state) => state.for_keys_with_prefix(prefix, action),
+			GenesisOrUnavailableState::Genesis(ref state) => state.for_keys_with_prefix(prefix, action),
+			GenesisOrUnavailableState::Unavailable => (),
 		}
 	}
 
 	fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], action: A) {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::for_key_values_with_prefix(state, prefix, action),
-			OnDemandOrGenesisState::Genesis(ref state) => state.for_key_values_with_prefix(prefix, action),
+			GenesisOrUnavailableState::Genesis(ref state) => state.for_key_values_with_prefix(prefix, action),
+			GenesisOrUnavailableState::Unavailable => (),
 		}
 	}
 
 
 	fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, storage_key: &[u8], action: A) {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::for_keys_in_child_storage(state, storage_key, action),
-			OnDemandOrGenesisState::Genesis(ref state) => state.for_keys_in_child_storage(storage_key, action),
+			GenesisOrUnavailableState::Genesis(ref state) => state.for_keys_in_child_storage(storage_key, action),
+			GenesisOrUnavailableState::Unavailable => (),
 		}
 	}
 
@@ -489,10 +378,9 @@ where
 		action: A,
 	) {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::for_child_keys_with_prefix(state, storage_key, prefix, action),
-			OnDemandOrGenesisState::Genesis(ref state) =>
+			GenesisOrUnavailableState::Genesis(ref state) =>
 				state.for_child_keys_with_prefix(storage_key, prefix, action),
+			GenesisOrUnavailableState::Unavailable => (),
 		}
 	}
 
@@ -501,12 +389,9 @@ where
 		I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
 	{
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::storage_root(state, delta),
-			OnDemandOrGenesisState::Genesis(ref state) => {
-				let (root, _) = state.storage_root(delta);
-				(root, ())
-			},
+			GenesisOrUnavailableState::Genesis(ref state) =>
+				(state.storage_root(delta).0, ()),
+			GenesisOrUnavailableState::Unavailable => (H::Out::default(), ()),
 		}
 	}
 
@@ -515,35 +400,32 @@ where
 		I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
 	{
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::child_storage_root(state, key, delta),
-			OnDemandOrGenesisState::Genesis(ref state) => {
+			GenesisOrUnavailableState::Genesis(ref state) => {
 				let (root, is_equal, _) = state.child_storage_root(key, delta);
 				(root, is_equal, ())
 			},
+			GenesisOrUnavailableState::Unavailable => (H::Out::default().as_ref().to_vec(), true, ()),
 		}
 	}
 
 	fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::pairs(state),
-			OnDemandOrGenesisState::Genesis(ref state) => state.pairs(),
+			GenesisOrUnavailableState::Genesis(ref state) => state.pairs(),
+			GenesisOrUnavailableState::Unavailable => Vec::new(),
 		}
 	}
 
 	fn keys(&self, prefix: &[u8]) -> Vec<Vec<u8>> {
 		match *self {
-			OnDemandOrGenesisState::OnDemand(ref state) =>
-				StateBackend::<H>::keys(state, prefix),
-			OnDemandOrGenesisState::Genesis(ref state) => state.keys(prefix),
+			GenesisOrUnavailableState::Genesis(ref state) => state.keys(prefix),
+			GenesisOrUnavailableState::Unavailable => Vec::new(),
 		}
 	}
 
 	fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
 		match self {
-			OnDemandOrGenesisState::OnDemand(ref mut state) => state.as_trie_backend(),
-			OnDemandOrGenesisState::Genesis(ref mut state) => state.as_trie_backend(),
+			GenesisOrUnavailableState::Genesis(ref mut state) => state.as_trie_backend(),
+			GenesisOrUnavailableState::Unavailable => None,
 		}
 	}
 }
@@ -561,24 +443,24 @@ mod tests {
 		let def = Default::default();
 		let header0 = test_client::runtime::Header::new(0, def, def, def, Default::default());
 
-		let backend: Backend<_, _, Blake2Hasher> = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
+		let backend: Backend<_, Blake2Hasher> = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
 		let mut op = backend.begin_operation().unwrap();
 		op.set_block_data(header0, None, None, NewBlockState::Final).unwrap();
 		op.reset_storage(Default::default(), Default::default()).unwrap();
 		backend.commit_operation(op).unwrap();
 
 		match backend.state_at(BlockId::Number(0)).unwrap() {
-			OnDemandOrGenesisState::Genesis(_) => (),
+			GenesisOrUnavailableState::Genesis(_) => (),
 			_ => panic!("unexpected state"),
 		}
 	}
 
 	#[test]
-	fn remote_state_is_created_when_genesis_state_is_inavailable() {
-		let backend: Backend<_, _, Blake2Hasher> = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
+	fn unavailable_state_is_created_when_genesis_state_is_unavailable() {
+		let backend: Backend<_, Blake2Hasher> = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
 
 		match backend.state_at(BlockId::Number(0)).unwrap() {
-			OnDemandOrGenesisState::OnDemand(_) => (),
+			GenesisOrUnavailableState::Unavailable => (),
 			_ => panic!("unexpected state"),
 		}
 	}
diff --git a/core/client/src/light/blockchain.rs b/core/client/src/light/blockchain.rs
index 726d2abdc6f2f7adbf8beee5a211927d32cca791..1e1a7669a02c8c50b071f1ae7e0003b03ecf5a32 100644
--- a/core/client/src/light/blockchain.rs
+++ b/core/client/src/light/blockchain.rs
@@ -18,8 +18,7 @@
 //! blocks. CHT roots are stored for headers of ancient blocks.
 
 use std::future::Future;
-use std::{sync::{Weak, Arc}, collections::HashMap};
-use parking_lot::Mutex;
+use std::{sync::Arc, collections::HashMap};
 
 use sr_primitives::{Justification, generic::BlockId};
 use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
@@ -30,7 +29,7 @@ use crate::blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as Bloc
 	HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache};
 use crate::cht;
 use crate::error::{Error as ClientError, Result as ClientResult};
-use crate::light::fetcher::{Fetcher, RemoteBodyRequest, RemoteHeaderRequest};
+use crate::light::fetcher::{Fetcher, RemoteHeaderRequest};
 
 /// Light client blockchain storage.
 pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> {
@@ -95,37 +94,25 @@ pub trait RemoteBlockchain<Block: BlockT>: Send + Sync {
 }
 
 /// Light client blockchain.
-pub struct Blockchain<S, F> {
-	fetcher: Mutex<Weak<F>>,
+pub struct Blockchain<S> {
 	storage: S,
 }
 
-impl<S, F> Blockchain<S, F> {
+impl<S> Blockchain<S> {
 	/// Create new light blockchain backed with given storage.
 	pub fn new(storage: S) -> Self {
 		Self {
-			fetcher: Mutex::new(Default::default()),
 			storage,
 		}
 	}
 
-	/// Sets fetcher reference.
-	pub fn set_fetcher(&self, fetcher: Weak<F>) {
-		*self.fetcher.lock() = fetcher;
-	}
-
-	/// Get fetcher weak reference.
-	pub fn fetcher(&self) -> Weak<F> {
-		self.fetcher.lock().clone()
-	}
-
 	/// Get storage reference.
 	pub fn storage(&self) -> &S {
 		&self.storage
 	}
 }
 
-impl<S, F, Block> BlockchainHeaderBackend<Block> for Blockchain<S, F> where Block: BlockT, S: Storage<Block>, F: Fetcher<Block> {
+impl<S, Block> BlockchainHeaderBackend<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
 	fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
 		match RemoteBlockchain::header(self, id)? {
 			LocalOrRemote::Local(header) => Ok(Some(header)),
@@ -151,24 +138,13 @@ impl<S, F, Block> BlockchainHeaderBackend<Block> for Blockchain<S, F> where Bloc
 	}
 }
 
-impl<S, F, Block> BlockchainBackend<Block> for Blockchain<S, F> where Block: BlockT, S: Storage<Block>, F: Fetcher<Block> {
-	fn body(&self, id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
-		let header = match BlockchainHeaderBackend::header(self, id)? {
-			Some(header) => header,
-			None => return Ok(None),
-		};
-
-		futures03::executor::block_on(
-			self.fetcher().upgrade().ok_or(ClientError::NotAvailableOnLightClient)?
-				.remote_body(RemoteBodyRequest {
-					header,
-					retry_count: None,
-				})
-		).map(Some)
+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)
 	}
 
 	fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification>> {
-		Ok(None)
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 
 	fn last_finalized(&self) -> ClientResult<Block::Hash> {
@@ -180,24 +156,23 @@ impl<S, F, Block> BlockchainBackend<Block> for Blockchain<S, F> where Block: Blo
 	}
 
 	fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
-		unimplemented!()
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 
 	fn children(&self, _parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
-		unimplemented!()
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 }
 
-impl<S: Storage<Block>, F, Block: BlockT> ProvideCache<Block> for Blockchain<S, F> {
+impl<S: Storage<Block>, Block: BlockT> ProvideCache<Block> for Blockchain<S> {
 	fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>> {
 		self.storage.cache()
 	}
 }
 
-impl<S, F, Block: BlockT> RemoteBlockchain<Block> for Blockchain<S, F>
+impl<S, Block: BlockT> RemoteBlockchain<Block> for Blockchain<S>
 	where
 		S: Storage<Block>,
-		F: Fetcher<Block> + Send + Sync,
 {
 	fn header(&self, id: BlockId<Block>) -> ClientResult<LocalOrRemote<
 		Block::Header,
@@ -253,12 +228,12 @@ pub fn future_header<Block: BlockT, F: Fetcher<Block>>(
 #[cfg(test)]
 pub mod tests {
 	use std::collections::HashMap;
+	use parking_lot::Mutex;
 	use test_client::runtime::{Hash, Block, Header};
 	use crate::blockchain::Info;
-	use crate::light::fetcher::tests::OkCallFetcher;
 	use super::*;
 
-	pub type DummyBlockchain = Blockchain<DummyStorage, OkCallFetcher>;
+	pub type DummyBlockchain = Blockchain<DummyStorage>;
 
 	pub struct DummyStorage {
 		pub changes_tries_cht_roots: HashMap<u64, Hash>,
diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs
index ab7b5b0a108b01352478033202bbbffee057badb..a06b48a6411a0b2d748a8745b1b2d4f62c381dff 100644
--- a/core/client/src/light/call_executor.rs
+++ b/core/client/src/light/call_executor.rs
@@ -14,17 +14,16 @@
 // You should have received a copy of the GNU General Public License
 // along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Light client call executor. Executes methods on remote full nodes, fetching
-//! execution proof and checking it locally.
+//! Methods that light client could use to execute runtime calls.
 
 use std::{
 	collections::HashSet, sync::Arc, panic::UnwindSafe, result,
-	marker::PhantomData, cell::RefCell, rc::Rc,
+	cell::RefCell, rc::Rc,
 };
 
 use codec::{Encode, Decode};
 use primitives::{
-	offchain::{self, NeverOffchainExt}, H256, Blake2Hasher, convert_hash, NativeOrEncoded,
+	offchain, H256, Blake2Hasher, convert_hash, NativeOrEncoded,
 	traits::CodeExecutor,
 };
 use sr_primitives::generic::BlockId;
@@ -37,211 +36,40 @@ use hash_db::Hasher;
 
 use crate::runtime_api::{ProofRecorder, InitializeBlock};
 use crate::backend::RemoteBackend;
-use crate::blockchain::Backend as ChainBackend;
 use crate::call_executor::CallExecutor;
 use crate::error::{Error as ClientError, Result as ClientResult};
-use crate::light::fetcher::{Fetcher, RemoteCallRequest};
+use crate::light::fetcher::RemoteCallRequest;
 use executor::{RuntimeVersion, NativeVersion};
 
-/// Call executor that executes methods on remote node, querying execution proof
-/// and checking proof by re-executing locally.
-pub struct RemoteCallExecutor<B, F> {
-	blockchain: Arc<B>,
-	fetcher: Arc<F>,
-}
-
-/// Remote or local call executor.
+/// Call executor that is able to execute calls only on genesis state.
 ///
-/// Calls are executed locally if state is available locally. Otherwise, calls
-/// are redirected to remote call executor.
-pub struct RemoteOrLocalCallExecutor<Block: BlockT<Hash=H256>, B, R, L> {
+/// Trying to execute call on non-genesis state leads to error.
+pub struct GenesisCallExecutor<B, L> {
 	backend: Arc<B>,
-	remote: R,
 	local: L,
-	_block: PhantomData<Block>,
 }
 
-impl<B, F> Clone for RemoteCallExecutor<B, F> {
-	fn clone(&self) -> Self {
-		RemoteCallExecutor {
-			blockchain: self.blockchain.clone(),
-			fetcher: self.fetcher.clone(),
-		}
-	}
-}
-
-impl<B, F> RemoteCallExecutor<B, F> {
-	/// Creates new instance of remote call executor.
-	pub fn new(blockchain: Arc<B>, fetcher: Arc<F>) -> Self {
-		RemoteCallExecutor { blockchain, fetcher }
-	}
-}
-
-impl<B, F, Block> CallExecutor<Block, Blake2Hasher> for RemoteCallExecutor<B, F>
-where
-	Block: BlockT<Hash=H256>,
-	B: ChainBackend<Block>,
-	F: Fetcher<Block>,
-	Block::Hash: Ord,
-{
-	type Error = ClientError;
-
-	fn call<
-		O: offchain::Externalities,
-	>(
-		&self,
-		id: &BlockId<Block>,
-		method: &str,
-		call_data: &[u8],
-		_strategy: ExecutionStrategy,
-		_side_effects_handler: Option<&mut O>,
-	) -> ClientResult<Vec<u8>>
-	{
-		let block_hash = self.blockchain.expect_block_hash_from_id(id)?;
-		let block_header = self.blockchain.expect_header(id.clone())?;
-
-		futures03::executor::block_on(self.fetcher.remote_call(RemoteCallRequest {
-			block: block_hash,
-			header: block_header,
-			method: method.into(),
-			call_data: call_data.to_vec(),
-			retry_count: None,
-		}))
-	}
-
-	fn contextual_call<
-		'a,
-		O: offchain::Externalities,
-		IB: Fn() -> ClientResult<()>,
-		EM: Fn(
-			Result<NativeOrEncoded<R>, Self::Error>,
-			Result<NativeOrEncoded<R>, Self::Error>
-		) -> Result<NativeOrEncoded<R>, Self::Error>,
-		R: Encode + Decode + PartialEq,
-		NC,
-	>(
-		&self,
-		_initialize_block_fn: IB,
-		at: &BlockId<Block>,
-		method: &str,
-		call_data: &[u8],
-		changes: &RefCell<OverlayedChanges>,
-		initialize_block: InitializeBlock<'a, Block>,
-		execution_manager: ExecutionManager<EM>,
-		_native_call: Option<NC>,
-		side_effects_handler: Option<&mut O>,
-		_recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
-		_enable_keystore: bool,
-	) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
-		let block_initialized = match initialize_block {
-			InitializeBlock::Do(ref init_block) => {
-				init_block.borrow().is_some()
-			},
-			InitializeBlock::Skip => false,
-		};
-
-		// it is only possible to execute contextual call if changes are empty
-		if !changes.borrow().is_empty() || block_initialized {
-			return Err(ClientError::NotAvailableOnLightClient.into());
-		}
-
-		self.call(
-			at,
-			method,
-			call_data,
-			(&execution_manager).into(),
-			side_effects_handler,
-		).map(NativeOrEncoded::Encoded)
-	}
-
-	fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
-		let call_result = self.call(
-			id,
-			"Core_version",
-			&[],
-			ExecutionStrategy::NativeElseWasm,
-			NeverOffchainExt::new()
-		)?;
-		RuntimeVersion::decode(&mut call_result.as_slice())
-			.map_err(|_| ClientError::VersionInvalid.into())
-	}
-
-	fn call_at_state<
-		O: offchain::Externalities,
-		S: StateBackend<Blake2Hasher>,
-		FF: FnOnce(
-			Result<NativeOrEncoded<R>, Self::Error>,
-			Result<NativeOrEncoded<R>, Self::Error>
-		) -> Result<NativeOrEncoded<R>, Self::Error>,
-		R: Encode + Decode + PartialEq,
-		NC: FnOnce() -> result::Result<R, &'static str>,
-	>(&self,
-		_state: &S,
-		_changes: &mut OverlayedChanges,
-		_method: &str,
-		_call_data: &[u8],
-		_m: ExecutionManager<FF>,
-		_native_call: Option<NC>,
-		_side_effects_handler: Option<&mut O>,
-	) -> ClientResult<(
-		NativeOrEncoded<R>,
-		(S::Transaction, <Blake2Hasher as Hasher>::Out),
-		Option<ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>>,
-	)> {
-		Err(ClientError::NotAvailableOnLightClient.into())
-	}
-
-	fn prove_at_trie_state<S: state_machine::TrieBackendStorage<Blake2Hasher>>(
-		&self,
-		_state: &state_machine::TrieBackend<S, Blake2Hasher>,
-		_changes: &mut OverlayedChanges,
-		_method: &str,
-		_call_data: &[u8]
-	) -> ClientResult<(Vec<u8>, Vec<Vec<u8>>)> {
-		Err(ClientError::NotAvailableOnLightClient.into())
-	}
-
-	fn native_runtime_version(&self) -> Option<&NativeVersion> {
-		None
+impl<B, L> GenesisCallExecutor<B, L> {
+	/// Create new genesis call executor.
+	pub fn new(backend: Arc<B>, local: L) -> Self {
+		Self { backend, local }
 	}
 }
 
-impl<Block, B, R, L> Clone for RemoteOrLocalCallExecutor<Block, B, R, L>
-	where
-		Block: BlockT<Hash=H256>,
-		B: RemoteBackend<Block, Blake2Hasher>,
-		R: CallExecutor<Block, Blake2Hasher> + Clone,
-		L: CallExecutor<Block, Blake2Hasher> + Clone,
-{
+impl<B, L: Clone> Clone for GenesisCallExecutor<B, L> {
 	fn clone(&self) -> Self {
-		RemoteOrLocalCallExecutor {
+		GenesisCallExecutor {
 			backend: self.backend.clone(),
-			remote: self.remote.clone(),
 			local: self.local.clone(),
-			_block: Default::default(),
 		}
 	}
 }
 
-impl<Block, B, Remote, Local> RemoteOrLocalCallExecutor<Block, B, Remote, Local>
+impl<Block, B, Local> CallExecutor<Block, Blake2Hasher> for
+	GenesisCallExecutor<B, Local>
 	where
 		Block: BlockT<Hash=H256>,
 		B: RemoteBackend<Block, Blake2Hasher>,
-		Remote: CallExecutor<Block, Blake2Hasher>,
-		Local: CallExecutor<Block, Blake2Hasher>,
-{
-	/// Creates new instance of remote/local call executor.
-	pub fn new(backend: Arc<B>, remote: Remote, local: Local) -> Self {
-		RemoteOrLocalCallExecutor { backend, remote, local, _block: Default::default(), }
-	}
-}
-
-impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
-	RemoteOrLocalCallExecutor<Block, B, Remote, Local>
-	where
-		Block: BlockT<Hash=H256>,
-		B: RemoteBackend<Block, Blake2Hasher>,
-		Remote: CallExecutor<Block, Blake2Hasher>,
 		Local: CallExecutor<Block, Blake2Hasher>,
 {
 	type Error = ClientError;
@@ -258,7 +86,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
 	) -> ClientResult<Vec<u8>> {
 		match self.backend.is_local_state_available(id) {
 			true => self.local.call(id, method, call_data, strategy, side_effects_handler),
-			false => self.remote.call(id, method, call_data, strategy, side_effects_handler),
+			false => Err(ClientError::NotAvailableOnLightClient),
 		}
 	}
 
@@ -313,36 +141,14 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
 				recorder,
 				enable_keystore,
 			).map_err(|e| ClientError::Execution(Box::new(e.to_string()))),
-			false => CallExecutor::contextual_call::<
-				_,
-				_,
-				fn(
-					Result<NativeOrEncoded<R>, Remote::Error>,
-					Result<NativeOrEncoded<R>, Remote::Error>,
-				) -> Result<NativeOrEncoded<R>, Remote::Error>,
-				_,
-				NC
-			>(
-				&self.remote,
-				initialize_block_fn,
-				at,
-				method,
-				call_data,
-				changes,
-				initialize_block,
-				ExecutionManager::NativeWhenPossible,
-				native_call,
-				side_effects_handler,
-				recorder,
-				enable_keystore,
-			).map_err(|e| ClientError::Execution(Box::new(e.to_string()))),
+			false => Err(ClientError::NotAvailableOnLightClient),
 		}
 	}
 
 	fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
 		match self.backend.is_local_state_available(id) {
 			true => self.local.runtime_version(id),
-			false => self.remote.runtime_version(id),
+			false => Err(ClientError::NotAvailableOnLightClient),
 		}
 	}
 
@@ -356,50 +162,29 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
 		R: Encode + Decode + PartialEq,
 		NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
 	>(&self,
-		state: &S,
-		changes: &mut OverlayedChanges,
-		method: &str,
-		call_data: &[u8],
+		_state: &S,
+		_changes: &mut OverlayedChanges,
+		_method: &str,
+		_call_data: &[u8],
 		_manager: ExecutionManager<FF>,
-		native_call: Option<NC>,
-		side_effects_handler: Option<&mut O>,
+		_native_call: Option<NC>,
+		_side_effects_handler: Option<&mut O>,
 	) -> ClientResult<(
 		NativeOrEncoded<R>,
 		(S::Transaction, <Blake2Hasher as Hasher>::Out),
 		Option<ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>>,
 	)> {
-		// there's no actual way/need to specify native/wasm execution strategy on light node
-		// => we can safely ignore passed values
-
-		CallExecutor::call_at_state::<
-				_,
-				_,
-				fn(
-					Result<NativeOrEncoded<R>, Remote::Error>,
-					Result<NativeOrEncoded<R>, Remote::Error>,
-				) -> Result<NativeOrEncoded<R>, Remote::Error>,
-				_,
-				NC
-			>(
-				&self.remote,
-				state,
-				changes,
-				method,
-				call_data,
-				ExecutionManager::NativeWhenPossible,
-				native_call,
-				side_effects_handler,
-			).map_err(|e| ClientError::Execution(Box::new(e.to_string())))
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 
 	fn prove_at_trie_state<S: state_machine::TrieBackendStorage<Blake2Hasher>>(
 		&self,
-		state: &state_machine::TrieBackend<S, Blake2Hasher>,
-		changes: &mut OverlayedChanges,
-		method: &str,
-		call_data: &[u8]
+		_state: &state_machine::TrieBackend<S, Blake2Hasher>,
+		_changes: &mut OverlayedChanges,
+		_method: &str,
+		_call_data: &[u8]
 	) -> ClientResult<(Vec<u8>, Vec<Vec<u8>>)> {
-		self.remote.prove_at_trie_state(state, changes, method, call_data)
+		Err(ClientError::NotAvailableOnLightClient)
 	}
 
 	fn native_runtime_version(&self) -> Option<&NativeVersion> {
@@ -517,13 +302,103 @@ fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Head
 #[cfg(test)]
 mod tests {
 	use consensus::BlockOrigin;
-	use test_client::{self, runtime::{Header, Digest}, ClientExt, TestClient};
+	use primitives::offchain::NeverOffchainExt;
+	use test_client::{self, runtime::{Header, Digest, Block}, ClientExt, TestClient};
 	use executor::NativeExecutor;
 	use crate::backend::{Backend, NewBlockState};
 	use crate::in_mem::Backend as InMemBackend;
-	use crate::light::fetcher::tests::OkCallFetcher;
 	use super::*;
 
+	struct DummyCallExecutor;
+
+	impl CallExecutor<Block, Blake2Hasher> for DummyCallExecutor {
+		type Error = ClientError;
+
+		fn call<O: offchain::Externalities>(
+			&self,
+			_id: &BlockId<Block>,
+			_method: &str,
+			_call_data: &[u8],
+			_strategy: ExecutionStrategy,
+			_side_effects_handler: Option<&mut O>,
+		) -> Result<Vec<u8>, ClientError> {
+			Ok(vec![42])
+		}
+
+		fn contextual_call<
+			'a,
+			O: offchain::Externalities,
+			IB: Fn() -> ClientResult<()>,
+			EM: Fn(
+				Result<NativeOrEncoded<R>, Self::Error>,
+				Result<NativeOrEncoded<R>, Self::Error>
+			) -> Result<NativeOrEncoded<R>, Self::Error>,
+			R: Encode + Decode + PartialEq,
+			NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
+		>(
+			&self,
+			_initialize_block_fn: IB,
+			_at: &BlockId<Block>,
+			_method: &str,
+			_call_data: &[u8],
+			_changes: &RefCell<OverlayedChanges>,
+			_initialize_block: InitializeBlock<'a, Block>,
+			_execution_manager: ExecutionManager<EM>,
+			_native_call: Option<NC>,
+			_side_effects_handler: Option<&mut O>,
+			_proof_recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
+			_enable_keystore: bool,
+		) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
+			unreachable!()
+		}
+
+		fn runtime_version(&self, _id: &BlockId<Block>) -> Result<RuntimeVersion, ClientError> {
+			unreachable!()
+		}
+
+		fn call_at_state<
+			O: offchain::Externalities,
+			S: state_machine::Backend<Blake2Hasher>,
+			F: FnOnce(
+				Result<NativeOrEncoded<R>, Self::Error>,
+				Result<NativeOrEncoded<R>, Self::Error>
+			) -> Result<NativeOrEncoded<R>, Self::Error>,
+			R: Encode + Decode + PartialEq,
+			NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
+		>(&self,
+			_state: &S,
+			_overlay: &mut OverlayedChanges,
+			_method: &str,
+			_call_data: &[u8],
+			_manager: ExecutionManager<F>,
+			_native_call: Option<NC>,
+			_side_effects_handler: Option<&mut O>,
+		) -> Result<
+			(
+				NativeOrEncoded<R>,
+				(S::Transaction, H256),
+				Option<ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>>,
+			),
+			ClientError,
+		> {
+			unreachable!()
+		}
+
+		fn prove_at_trie_state<S: state_machine::TrieBackendStorage<Blake2Hasher>>(
+			&self,
+			_trie_state: &state_machine::TrieBackend<S, Blake2Hasher>,
+			_overlay: &mut OverlayedChanges,
+			_method: &str,
+			_call_data: &[u8]
+		) -> Result<(Vec<u8>, Vec<Vec<u8>>), ClientError> {
+			unreachable!()
+		}
+
+		fn native_runtime_version(&self) -> Option<&NativeVersion> {
+			unreachable!()
+		}
+	}
+
 	#[test]
 	fn execution_proof_is_generated_and_checked() {
 		fn execute(remote_client: &TestClient, at: u64, method: &'static str) -> (Vec<u8>, Vec<u8>) {
@@ -624,8 +499,8 @@ mod tests {
 	}
 
 	#[test]
-	fn code_is_executed_locally_or_remotely() {
-		let backend = Arc::new(InMemBackend::new());
+	fn code_is_executed_at_genesis_only() {
+		let backend = Arc::new(InMemBackend::<Block, Blake2Hasher>::new());
 		let def = H256::default();
 		let header0 = test_client::runtime::Header::new(0, def, def, def, Default::default());
 		let hash0 = header0.hash();
@@ -634,34 +509,29 @@ mod tests {
 		backend.blockchain().insert(hash0, header0, None, None, NewBlockState::Final).unwrap();
 		backend.blockchain().insert(hash1, header1, None, None, NewBlockState::Final).unwrap();
 
-		let local_executor = RemoteCallExecutor::new(
-			Arc::new(backend.blockchain().clone()),
-			Arc::new(OkCallFetcher::new(vec![1])),
-		);
-		let remote_executor = RemoteCallExecutor::new(
-			Arc::new(backend.blockchain().clone()),
-			Arc::new(OkCallFetcher::new(vec![2])),
-		);
-		let remote_or_local = RemoteOrLocalCallExecutor::new(backend, remote_executor, local_executor);
+		let genesis_executor = GenesisCallExecutor::new(backend, DummyCallExecutor);
 		assert_eq!(
-			remote_or_local.call(
+			genesis_executor.call(
 				&BlockId::Number(0),
 				"test_method",
 				&[],
 				ExecutionStrategy::NativeElseWasm,
 				NeverOffchainExt::new(),
 			).unwrap(),
-			vec![1],
+			vec![42],
 		);
-		assert_eq!(
-			remote_or_local.call(
-				&BlockId::Number(1),
-				"test_method",
-				&[],
-				ExecutionStrategy::NativeElseWasm,
-				NeverOffchainExt::new(),
-			).unwrap(),
-			vec![2],
+
+		let call_on_unavailable = genesis_executor.call(
+			&BlockId::Number(1),
+			"test_method",
+			&[],
+			ExecutionStrategy::NativeElseWasm,
+			NeverOffchainExt::new(),
 		);
+
+		match call_on_unavailable {
+			Err(ClientError::NotAvailableOnLightClient) => (),
+			_ => unreachable!("unexpected result: {:?}", call_on_unavailable),
+		}
 	}
 }
diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs
index 6ac637f3fa078c9e04fbceefe46c0655d430f9cd..c25092c32c1f17528958ab535e0e6eabd8eeb323 100644
--- a/core/client/src/light/fetcher.rs
+++ b/core/client/src/light/fetcher.rs
@@ -221,15 +221,15 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {
 }
 
 /// Remote data checker.
-pub struct LightDataChecker<E, H, B: BlockT, S: BlockchainStorage<B>, F> {
-	blockchain: Arc<Blockchain<S, F>>,
+pub struct LightDataChecker<E, H, B: BlockT, S: BlockchainStorage<B>> {
+	blockchain: Arc<Blockchain<S>>,
 	executor: E,
 	_hasher: PhantomData<(B, H)>,
 }
 
-impl<E, H, B: BlockT, S: BlockchainStorage<B>, F> LightDataChecker<E, H, B, S, F> {
+impl<E, H, B: BlockT, S: BlockchainStorage<B>> LightDataChecker<E, H, B, S> {
 	/// Create new light data checker.
-	pub fn new(blockchain: Arc<Blockchain<S, F>>, executor: E) -> Self {
+	pub fn new(blockchain: Arc<Blockchain<S>>, executor: E) -> Self {
 		Self {
 			blockchain, executor, _hasher: PhantomData
 		}
@@ -367,14 +367,13 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>, F> LightDataChecker<E, H, B, S, F
 	}
 }
 
-impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S, F>
+impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
 	where
 		Block: BlockT,
 		E: CodeExecutor<H>,
 		H: Hasher,
 		H::Out: Ord + 'static,
 		S: BlockchainStorage<Block>,
-		F: Send + Sync,
 {
 	fn check_header_proof(
 		&self,
@@ -563,7 +562,6 @@ pub mod tests {
 		Blake2Hasher,
 		Block,
 		DummyStorage,
-		OkCallFetcher,
 	>;
 
 	fn prepare_for_read_proof_check() -> (TestChecker, Header, Vec<Vec<u8>>, u32) {
diff --git a/core/client/src/light/mod.rs b/core/client/src/light/mod.rs
index 08e14ad8f323bb75015fcc03e7139a07f78e94d1..03b7dcff8564c45d7d9102b1a41a68531d1484fa 100644
--- a/core/client/src/light/mod.rs
+++ b/core/client/src/light/mod.rs
@@ -33,55 +33,48 @@ use crate::client::Client;
 use crate::error::Result as ClientResult;
 use crate::light::backend::Backend;
 use crate::light::blockchain::{Blockchain, Storage as BlockchainStorage};
-use crate::light::call_executor::{RemoteCallExecutor, RemoteOrLocalCallExecutor};
-use crate::light::fetcher::{Fetcher, LightDataChecker};
+use crate::light::call_executor::GenesisCallExecutor;
+use crate::light::fetcher::LightDataChecker;
 
 /// Create an instance of light client blockchain backend.
-pub fn new_light_blockchain<B: BlockT, S: BlockchainStorage<B>, F>(storage: S) -> Arc<Blockchain<S, F>> {
+pub fn new_light_blockchain<B: BlockT, S: BlockchainStorage<B>>(storage: S) -> Arc<Blockchain<S>> {
 	Arc::new(Blockchain::new(storage))
 }
 
 /// Create an instance of light client backend.
-pub fn new_light_backend<B, S, F>(blockchain: Arc<Blockchain<S, F>>, fetcher: Arc<F>) -> Arc<Backend<S, F, Blake2Hasher>>
+pub fn new_light_backend<B, S>(blockchain: Arc<Blockchain<S>>) -> Arc<Backend<S, Blake2Hasher>>
 	where
 		B: BlockT,
 		S: BlockchainStorage<B>,
-		F: Fetcher<B>,
 {
-	blockchain.set_fetcher(Arc::downgrade(&fetcher));
 	Arc::new(Backend::new(blockchain))
 }
 
 /// Create an instance of light client.
-pub fn new_light<B, S, F, GS, RA, E>(
-	backend: Arc<Backend<S, F, Blake2Hasher>>,
-	fetcher: Arc<F>,
+pub fn new_light<B, S, GS, RA, E>(
+	backend: Arc<Backend<S, Blake2Hasher>>,
 	genesis_storage: GS,
 	code_executor: E,
-) -> ClientResult<Client<Backend<S, F, Blake2Hasher>, RemoteOrLocalCallExecutor<
-	B,
-	Backend<S, F, Blake2Hasher>,
-	RemoteCallExecutor<Blockchain<S, F>, F>,
-	LocalCallExecutor<Backend<S, F, Blake2Hasher>, E>
+) -> ClientResult<Client<Backend<S, Blake2Hasher>, GenesisCallExecutor<
+	Backend<S, Blake2Hasher>,
+	LocalCallExecutor<Backend<S, Blake2Hasher>, E>
 >, B, RA>>
 	where
 		B: BlockT<Hash=H256>,
 		S: BlockchainStorage<B> + 'static,
-		F: Fetcher<B> + 'static,
 		GS: BuildStorage,
 		E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
 {
-	let remote_executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher);
 	let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, None);
-	let executor = RemoteOrLocalCallExecutor::new(backend.clone(), remote_executor, local_executor);
+	let executor = GenesisCallExecutor::new(backend.clone(), local_executor);
 	Client::new(backend, executor, genesis_storage, Default::default())
 }
 
 /// Create an instance of fetch data checker.
-pub fn new_fetch_checker<E, B: BlockT, S: BlockchainStorage<B>, F>(
-	blockchain: Arc<Blockchain<S, F>>,
+pub fn new_fetch_checker<E, B: BlockT, S: BlockchainStorage<B>>(
+	blockchain: Arc<Blockchain<S>>,
 	executor: E,
-) -> LightDataChecker<E, Blake2Hasher, B, S, F>
+) -> LightDataChecker<E, Blake2Hasher, B, S>
 	where
 		E: CodeExecutor<Blake2Hasher>,
 {
diff --git a/core/service/src/builder.rs b/core/service/src/builder.rs
index 1540eeac9cdb6d83508725499410bb218dc80a15..458c72a74ff6dc0cfe379eec37bfd618adfbe6ae 100644
--- a/core/service/src/builder.rs
+++ b/core/service/src/builder.rs
@@ -108,29 +108,18 @@ type TLightClient<TBl, TRtApi, TExecDisp> = Client<
 /// Light client backend type.
 type TLightBackend<TBl> = client::light::backend::Backend<
 	client_db::light::LightStorage<TBl>,
-	network::OnDemand<TBl>,
 	Blake2Hasher,
 >;
 
 /// Light call executor type.
-type TLightCallExecutor<TBl, TExecDisp> = client::light::call_executor::RemoteOrLocalCallExecutor<
-	TBl,
+type TLightCallExecutor<TBl, TExecDisp> = client::light::call_executor::GenesisCallExecutor<
 	client::light::backend::Backend<
 		client_db::light::LightStorage<TBl>,
-		network::OnDemand<TBl>,
 		Blake2Hasher
 	>,
-	client::light::call_executor::RemoteCallExecutor<
-		client::light::blockchain::Blockchain<
-			client_db::light::LightStorage<TBl>,
-			network::OnDemand<TBl>
-		>,
-		network::OnDemand<TBl>,
-	>,
 	client::LocalCallExecutor<
 		client::light::backend::Backend<
 			client_db::light::LightStorage<TBl>,
-			network::OnDemand<TBl>,
 			Blake2Hasher
 		>,
 		NativeExecutor<TExecDisp>
@@ -240,11 +229,10 @@ where TGen: Serialize + DeserializeOwned + BuildStorage {
 		let light_blockchain = client::light::new_light_blockchain(db_storage);
 		let fetch_checker = Arc::new(client::light::new_fetch_checker(light_blockchain.clone(), executor.clone()));
 		let fetcher = Arc::new(network::OnDemand::new(fetch_checker));
-		let backend = client::light::new_light_backend(light_blockchain, fetcher.clone());
+		let backend = client::light::new_light_backend(light_blockchain);
 		let remote_blockchain = backend.remote_blockchain();
 		let client = Arc::new(client::light::new_light(
 			backend.clone(),
-			fetcher.clone(),
 			&config.chain_spec,
 			executor,
 		)?);
@@ -459,15 +447,22 @@ impl<TBl, TRtApi, TCfg, TGen, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPo
 	/// Defines which import queue to use.
 	pub fn with_import_queue_and_opt_fprb<UImpQu, UFprb>(
 		self,
-		builder: impl FnOnce(&Configuration<TCfg, TGen>, Arc<TCl>, Arc<Backend>, Option<TSc>, Arc<TExPool>)
-			-> Result<(UImpQu, Option<UFprb>), Error>
+		builder: impl FnOnce(
+			&Configuration<TCfg, TGen>,
+			Arc<TCl>,
+			Arc<Backend>,
+			Option<TFchr>,
+			Option<TSc>,
+			Arc<TExPool>,
+		) -> Result<(UImpQu, Option<UFprb>), Error>
 	) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
 		TNetP, TExPool, TRpc, TRpcB, Backend>, Error>
-	where TSc: Clone {
+	where TSc: Clone, TFchr: Clone {
 		let (import_queue, fprb) = builder(
 			&self.config,
 			self.client.clone(),
 			self.backend.clone(),
+			self.fetcher.clone(),
 			self.select_chain.clone(),
 			self.transaction_pool.clone()
 		)?;
@@ -494,12 +489,21 @@ impl<TBl, TRtApi, TCfg, TGen, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPo
 	/// Defines which import queue to use.
 	pub fn with_import_queue_and_fprb<UImpQu, UFprb>(
 		self,
-		builder: impl FnOnce(&Configuration<TCfg, TGen>, Arc<TCl>, Arc<Backend>, Option<TSc>, Arc<TExPool>)
-			-> Result<(UImpQu, UFprb), Error>
+		builder: impl FnOnce(
+			&Configuration<TCfg, TGen>,
+			Arc<TCl>,
+			Arc<Backend>,
+			Option<TFchr>,
+			Option<TSc>,
+			Arc<TExPool>,
+		) -> Result<(UImpQu, UFprb), Error>
 	) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
 			TNetP, TExPool, TRpc, TRpcB, Backend>, Error>
-	where TSc: Clone {
-		self.with_import_queue_and_opt_fprb(|cfg, cl, b, sc, tx| builder(cfg, cl, b, sc, tx).map(|(q, f)| (q, Some(f))))
+	where TSc: Clone, TFchr: Clone {
+		self.with_import_queue_and_opt_fprb(|cfg, cl, b, f, sc, tx|
+			builder(cfg, cl, b, f, sc, tx)
+				.map(|(q, f)| (q, Some(f)))
+		)
 	}
 
 	/// Defines which transaction pool to use.
diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs
index 17e26708468aab7afca1e648c17c5c81ca83289d..3ae999f1f16fafe20eb72dc657256d8b165c0c50 100644
--- a/core/test-client/src/lib.rs
+++ b/core/test-client/src/lib.rs
@@ -47,7 +47,6 @@ use client::LocalCallExecutor;
 /// Test client light database backend.
 pub type LightBackend<Block> = client::light::backend::Backend<
 	client_db::light::LightStorage<Block>,
-	LightFetcher,
 	Blake2Hasher,
 >;
 
diff --git a/core/test-runtime/client/src/lib.rs b/core/test-runtime/client/src/lib.rs
index aeed1e7ad447e069a420650c97e46983d806b0dd..229fcbdaf9072279ad7f3d848b896b8e65117824 100644
--- a/core/test-runtime/client/src/lib.rs
+++ b/core/test-runtime/client/src/lib.rs
@@ -73,20 +73,11 @@ pub type Executor = client::LocalCallExecutor<
 pub type LightBackend = generic_test_client::LightBackend<runtime::Block>;
 
 /// Test client light executor.
-pub type LightExecutor = client::light::call_executor::RemoteOrLocalCallExecutor<
-	runtime::Block,
+pub type LightExecutor = client::light::call_executor::GenesisCallExecutor<
 	LightBackend,
-	client::light::call_executor::RemoteCallExecutor<
-		client::light::blockchain::Blockchain<
-			client_db::light::LightStorage<runtime::Block>,
-			LightFetcher
-		>,
-		LightFetcher
-	>,
 	client::LocalCallExecutor<
 		client::light::backend::Backend<
 			client_db::light::LightStorage<runtime::Block>,
-			LightFetcher,
 			Blake2Hasher
 		>,
 		NativeExecutor<LocalExecutor>
@@ -271,22 +262,16 @@ pub fn new_light() -> (
 	let blockchain = Arc::new(client::light::blockchain::Blockchain::new(storage));
 	let backend = Arc::new(LightBackend::new(blockchain.clone()));
 	let executor = NativeExecutor::new(None);
-	let fetcher = Arc::new(LightFetcher);
-	let remote_call_executor = client::light::call_executor::RemoteCallExecutor::new(
-		blockchain.clone(),
-		fetcher,
-	);
 	let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor, None);
 	let call_executor = LightExecutor::new(
 		backend.clone(),
-		remote_call_executor,
 		local_call_executor,
 	);
 
-	(TestClientBuilder::with_backend(backend.clone())
-		.build_with_executor(call_executor)
-		.0,
-	backend,
+	(
+		TestClientBuilder::with_backend(backend.clone())
+			.build_with_executor(call_executor)
+			.0,
+		backend,
 	)
-
 }
diff --git a/node-template/src/service.rs b/node-template/src/service.rs
index 310b8f44a7cfc897d993864391d7485c658e7884..f4ab3f40000e2b71c8f88c17aeb062d4d77c38d9 100644
--- a/node-template/src/service.rs
+++ b/node-template/src/service.rs
@@ -197,9 +197,8 @@ pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisCo
 		.with_transaction_pool(|config, client|
 			Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
 		)?
-		.with_import_queue_and_fprb(|_config, client, backend, _select_chain, transaction_pool| {
-			let fetch_checker = backend.blockchain().fetcher()
-				.upgrade()
+		.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, transaction_pool| {
+			let fetch_checker = fetcher
 				.map(|fetcher| fetcher.checker().clone())
 				.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
 			let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, _>(
diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs
index ca6b249484e07f85ef1b049252ff7e0339249aca..f3ce9baa2e353875bc8be460ab6081bd2e616e7f 100644
--- a/node/cli/src/service.rs
+++ b/node/cli/src/service.rs
@@ -237,9 +237,8 @@ pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisCo
 		.with_transaction_pool(|config, client|
 			Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
 		)?
-		.with_import_queue_and_fprb(|_config, client, backend, _select_chain, transaction_pool| {
-			let fetch_checker = backend.blockchain().fetcher()
-				.upgrade()
+		.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, transaction_pool| {
+			let fetch_checker = fetcher
 				.map(|fetcher| fetcher.checker().clone())
 				.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
 			let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, _>(