diff --git a/core/client/src/backend.rs b/core/client/src/backend.rs
index 60a0069db490264432481beabdbfd5b73d3b17fd..108b30df03b199a59c5fd411ebbfc2062a410932 100644
--- a/core/client/src/backend.rs
+++ b/core/client/src/backend.rs
@@ -177,4 +177,7 @@ pub trait RemoteBackend<Block, H>: Backend<Block, H>
 where
 	Block: BlockT,
 	H: Hasher<Out=Block::Hash>,
-{}
+{
+	/// Returns true if the state for given block is available locally.
+	fn is_local_state_available(&self, block: &BlockId<Block>) -> bool;
+}
diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs
index 4c2ff946b56b6b21c424f24d266ba7a66a5c5259..4c274fc43633d9107ee371c2a4d0bb8cfdc26614 100644
--- a/core/client/src/call_executor.rs
+++ b/core/client/src/call_executor.rs
@@ -154,7 +154,7 @@ impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
 
 impl<B, E, Block> CallExecutor<Block, Blake2Hasher> for LocalCallExecutor<B, E>
 where
-	B: backend::LocalBackend<Block, Blake2Hasher>,
+	B: backend::Backend<Block, Blake2Hasher>,
 	E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
 	Block: BlockT<Hash=H256>,
 {
diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs
index 7102426b8f49390b87c1c452057990062391ac06..6f53ad7d2a348d085d5be7b625b974b949c22e80 100644
--- a/core/client/src/in_mem.rs
+++ b/core/client/src/in_mem.rs
@@ -465,17 +465,11 @@ where
 	}
 
 	fn reset_storage(&mut self, mut top: StorageMap, children: ChildrenStorageMap) -> error::Result<H::Out> {
-		if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
-			return Err(error::ErrorKind::GenesisInvalid.into());
-		}
+		check_genesis_storage(&top, &children)?;
 
 		let mut transaction: Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)> = Default::default();
 
 		for (child_key, child_map) in children {
-			if !well_known_keys::is_child_storage_key(&child_key) {
-				return Err(error::ErrorKind::GenesisInvalid.into());
-			}
-
 			let (root, is_default, update) = self.old_state.child_storage_root(&child_key, child_map.into_iter().map(|(k, v)| (k, Some(v))));
 			transaction.consolidate(update);
 
@@ -662,6 +656,19 @@ where
 	H::Out: HeapSizeOf + Ord,
 {}
 
+impl<Block, H> backend::RemoteBackend<Block, H> for Backend<Block, H>
+where
+	Block: BlockT,
+	H: Hasher<Out=Block::Hash>,
+	H::Out: HeapSizeOf + Ord,
+{
+	fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
+		self.blockchain.expect_block_number_from_id(block)
+			.map(|num| num.is_zero())
+			.unwrap_or(false)
+	}
+}
+
 impl<Block: BlockT> Cache<Block> {
 	fn insert(&self, at: Block::Hash, authorities: Option<Vec<AuthorityIdFor<Block>>>) {
 		self.authorities_at.write().insert(at, authorities);
@@ -708,6 +715,19 @@ pub fn cache_authorities_at<Block: BlockT>(
 	blockchain.cache.insert(at, authorities);
 }
 
+/// Check that genesis storage is valid.
+pub fn check_genesis_storage(top: &StorageMap, children: &ChildrenStorageMap) -> error::Result<()> {
+	if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
+		return Err(error::ErrorKind::GenesisInvalid.into());
+	}
+
+	if children.keys().any(|child_key| !well_known_keys::is_child_storage_key(&child_key)) {
+		return Err(error::ErrorKind::GenesisInvalid.into());
+	}
+
+	Ok(())
+}
+
 #[cfg(test)]
 mod tests {
 	use std::sync::Arc;
diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs
index ca5c84cae2bc62a435e9ad9dd90d829a3a525db3..ff9e6e113efa339bc5128c6dfd27ed33f092ffd5 100644
--- a/core/client/src/light/backend.rs
+++ b/core/client/src/light/backend.rs
@@ -17,14 +17,15 @@
 //! Light client backend. Only stores headers and justifications of blocks.
 //! Everything else is requested from full nodes on demand.
 
+use std::collections::HashMap;
 use std::sync::{Arc, Weak};
 use futures::{Future, IntoFuture};
 use parking_lot::RwLock;
 
 use runtime_primitives::{generic::BlockId, Justification, StorageMap, ChildrenStorageMap};
-use state_machine::{Backend as StateBackend, TrieBackend};
-use runtime_primitives::traits::{Block as BlockT, NumberFor, AuthorityIdFor};
-use crate::in_mem;
+use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState};
+use runtime_primitives::traits::{Block as BlockT, NumberFor, AuthorityIdFor, Zero, Header};
+use crate::in_mem::{self, check_genesis_storage};
 use crate::backend::{AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState};
 use crate::blockchain::HeaderBackend as BlockchainHeaderBackend;
 use crate::error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
@@ -34,18 +35,22 @@ use hash_db::Hasher;
 use trie::MemoryDB;
 use heapsize::HeapSizeOf;
 
+const IN_MEMORY_EXPECT_PROOF: &'static str = "InMemory state backend has Void error type and always suceeds; qed";
+
 /// Light client backend.
-pub struct Backend<S, F> {
+pub struct Backend<S, F, H> {
 	blockchain: Arc<Blockchain<S, F>>,
+	genesis_state: RwLock<Option<InMemoryState<H>>>,
 }
 
 /// Light block (header and justification) import operation.
-pub struct ImportOperation<Block: BlockT, S, F> {
+pub struct ImportOperation<Block: BlockT, S, F, H> {
 	header: Option<Block::Header>,
 	authorities: Option<Vec<AuthorityIdFor<Block>>>,
 	leaf_state: NewBlockState,
 	aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
 	finalized_blocks: Vec<BlockId<Block>>,
+	storage_update: Option<InMemoryState<H>>,
 	_phantom: ::std::marker::PhantomData<(S, F)>,
 }
 
@@ -57,10 +62,21 @@ pub struct OnDemandState<Block: BlockT, S, F> {
 	cached_header: RwLock<Option<Block::Header>>,
 }
 
-impl<S, F> Backend<S, F> {
+/// On-demand or in-memory genesis state.
+pub enum OnDemandOrGenesisState<Block: BlockT, S, F, H> {
+	/// On-demand state - storage values are fetched from remote nodes.
+	OnDemand(OnDemandState<Block, S, F>),
+	/// Genesis state - storage values are stored in-memory.
+	Genesis(InMemoryState<H>),
+}
+
+impl<S, F, H> Backend<S, F, H> {
 	/// Create new light backend.
 	pub fn new(blockchain: Arc<Blockchain<S, F>>) -> Self {
-		Self { blockchain }
+		Self {
+			blockchain,
+			genesis_state: RwLock::new(None),
+		}
 	}
 
 	/// Get shared blockchain reference.
@@ -69,7 +85,7 @@ impl<S, F> Backend<S, F> {
 	}
 }
 
-impl<S: AuxStore, F> AuxStore for Backend<S, F> {
+impl<S: AuxStore, F, H> AuxStore for Backend<S, F, H> {
 	fn insert_aux<
 		'a,
 		'b: 'a,
@@ -85,16 +101,16 @@ impl<S: AuxStore, F> AuxStore for Backend<S, F> {
 	}
 }
 
-impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
+impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
 	Block: BlockT,
 	S: BlockchainStorage<Block>,
 	F: Fetcher<Block>,
 	H: Hasher<Out=Block::Hash>,
 	H::Out: HeapSizeOf + Ord,
 {
-	type BlockImportOperation = ImportOperation<Block, S, F>;
+	type BlockImportOperation = ImportOperation<Block, S, F, H>;
 	type Blockchain = Blockchain<S, F>;
-	type State = OnDemandState<Block, S, F>;
+	type State = OnDemandOrGenesisState<Block, S, F, H>;
 	type ChangesTrieStorage = in_mem::ChangesTrieStorage<H>;
 
 	fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
@@ -104,6 +120,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
 			leaf_state: NewBlockState::Normal,
 			aux_ops: Vec::new(),
 			finalized_blocks: Vec::new(),
+			storage_update: None,
 			_phantom: Default::default(),
 		})
 	}
@@ -116,7 +133,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
 		Ok(())
 	}
 
-	fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> {
+	fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> ClientResult<()> {
 		if !operation.finalized_blocks.is_empty() {
 			for block in operation.finalized_blocks {
 				self.blockchain.storage().finalize_header(block)?;
@@ -124,12 +141,18 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
 		}
 
 		if let Some(header) = operation.header {
+			let is_genesis_import = header.number().is_zero();
 			self.blockchain.storage().import_header(
 				header,
 				operation.authorities,
 				operation.leaf_state,
 				operation.aux_ops,
 			)?;
+
+			// when importing genesis block => remember its state
+			if is_genesis_import {
+				*self.genesis_state.write() = operation.storage_update.take();
+			}
 		} else {
 			for (key, maybe_val) in operation.aux_ops {
 				match maybe_val {
@@ -158,17 +181,23 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
 	}
 
 	fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
-		let block_hash = match block {
-			BlockId::Hash(h) => Some(h),
-			BlockId::Number(n) => self.blockchain.hash(n).unwrap_or_default(),
-		};
+		let block_number = self.blockchain.expect_block_number_from_id(&block)?;
+
+		// 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));
+			}
+		}
 
-		Ok(OnDemandState {
+		// 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.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", block)))?,
+			block: block_hash,
 			cached_header: RwLock::new(None),
-		})
+		}))
 	}
 
 	fn revert(&self, _n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
@@ -176,16 +205,23 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
 	}
 }
 
-impl<S, F, Block, H> RemoteBackend<Block, H> for Backend<S, F>
+impl<S, F, Block, H> RemoteBackend<Block, H> for Backend<S, F, H>
 where
 	Block: BlockT,
 	S: BlockchainStorage<Block>,
 	F: Fetcher<Block>,
 	H: Hasher<Out=Block::Hash>,
 	H::Out: HeapSizeOf + Ord,
-{}
+{
+	fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
+		self.genesis_state.read().is_some()
+			&& self.blockchain.expect_block_number_from_id(block)
+				.map(|num| num.is_zero())
+				.unwrap_or(false)
+	}
+}
 
-impl<S, F, Block, H> BlockImportOperation<Block, H> for ImportOperation<Block, S, F>
+impl<S, F, Block, H> BlockImportOperation<Block, H> for ImportOperation<Block, S, F, H>
 where
 	Block: BlockT,
 	F: Fetcher<Block>,
@@ -193,7 +229,7 @@ where
 	H: Hasher<Out=Block::Hash>,
 	H::Out: HeapSizeOf + Ord,
 {
-	type State = OnDemandState<Block, S, F>;
+	type State = OnDemandOrGenesisState<Block, S, F, H>;
 
 	fn state(&self) -> ClientResult<Option<&Self::State>> {
 		// None means 'locally-stateless' backend
@@ -227,9 +263,19 @@ where
 	}
 
 	fn reset_storage(&mut self, top: StorageMap, children: ChildrenStorageMap) -> ClientResult<H::Out> {
-		let in_mem = in_mem::Backend::<Block, H>::new();
-		let mut op = in_mem.begin_operation()?;
-		op.reset_storage(top, children)
+		check_genesis_storage(&top, &children)?;
+
+		// this is only called when genesis block is imported => shouldn't be performance bottleneck
+		let mut storage: HashMap<Option<Vec<u8>>, StorageMap> = HashMap::new();
+		storage.insert(None, top);
+		for (child_key, child_storage) in children {
+			storage.insert(Some(child_key), child_storage);
+		}
+		let storage_update: InMemoryState<H> = storage.into();
+		let (storage_root, _) = storage_update.storage_root(::std::iter::empty());
+		self.storage_update = Some(storage_update);
+
+		Ok(storage_root)
 	}
 
 	fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
@@ -322,14 +368,139 @@ where
 	}
 }
 
+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: HeapSizeOf + Ord,
+{
+	type Error = ClientError;
+	type Transaction = ();
+	type TrieBackendStorage = MemoryDB<H>;
+
+	fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
+		match *self {
+			OnDemandOrGenesisState::OnDemand(ref state) =>
+				StateBackend::<H>::storage(state, key),
+			OnDemandOrGenesisState::Genesis(ref state) =>
+				Ok(state.storage(key).expect(IN_MEMORY_EXPECT_PROOF)),
+		}
+	}
+
+	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) =>
+				Ok(state.child_storage(storage_key, key).expect(IN_MEMORY_EXPECT_PROOF)),
+		}
+	}
+
+	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),
+		}
+	}
+
+	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),
+		}
+	}
+
+	fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
+	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, ())
+			},
+		}
+	}
+
+	fn child_storage_root<I>(&self, key: &[u8], delta: I) -> (Vec<u8>, bool, Self::Transaction)
+	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) => {
+				let (root, is_equal, _) = state.child_storage_root(key, delta);
+				(root, is_equal, ())
+			},
+		}
+	}
+
+	fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
+		match *self {
+			OnDemandOrGenesisState::OnDemand(ref state) =>
+				StateBackend::<H>::pairs(state),
+			OnDemandOrGenesisState::Genesis(ref state) => state.pairs(),
+		}
+	}
+
+	fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>> {
+		match *self {
+			OnDemandOrGenesisState::OnDemand(ref state) =>
+				StateBackend::<H>::keys(state, prefix),
+			OnDemandOrGenesisState::Genesis(ref state) => state.keys(prefix),
+		}
+	}
+
+	fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
+		match self {
+			OnDemandOrGenesisState::OnDemand(state) => state.try_into_trie_backend(),
+			OnDemandOrGenesisState::Genesis(state) => state.try_into_trie_backend(),
+		}
+	}
+}
+
 #[cfg(test)]
 mod tests {
 	use primitives::Blake2Hasher;
-	use test_client::runtime::Block;
+	use test_client::{self, runtime::Block};
+	use crate::backend::NewBlockState;
 	use crate::light::blockchain::tests::{DummyBlockchain, DummyStorage};
 	use super::*;
 
 	#[test]
+	fn local_state_is_created_when_genesis_state_is_available() {
+		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 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(_) => (),
+			_ => 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())));
+
+		match backend.state_at(BlockId::Number(0)).unwrap() {
+			OnDemandOrGenesisState::OnDemand(_) => (),
+			_ => panic!("unexpected state"),
+		}
+	}
+
 	fn light_aux_store_is_updated_via_non_importing_op() {
 		let backend = Backend::new(Arc::new(DummyBlockchain::new(DummyStorage::new())));
 		let mut op = ClientBackend::<Block, Blake2Hasher>::begin_operation(&backend).unwrap();
diff --git a/core/client/src/light/blockchain.rs b/core/client/src/light/blockchain.rs
index 6cad6c684176d043149821be1397317c3965b3b0..89fe5f8a3a4d7261d97f01486706c9f6781d19e4 100644
--- a/core/client/src/light/blockchain.rs
+++ b/core/client/src/light/blockchain.rs
@@ -199,12 +199,20 @@ pub mod tests {
 			Err(ClientErrorKind::Backend("Test error".into()).into())
 		}
 
-		fn number(&self, _hash: Hash) -> ClientResult<Option<NumberFor<Block>>> {
-			Err(ClientErrorKind::Backend("Test error".into()).into())
+		fn number(&self, hash: Hash) -> ClientResult<Option<NumberFor<Block>>> {
+			if hash == Default::default() {
+				Ok(Some(Default::default()))
+			} else {
+				Err(ClientErrorKind::Backend("Test error".into()).into())
+			}
 		}
 
-		fn hash(&self, _number: u64) -> ClientResult<Option<Hash>> {
-			Err(ClientErrorKind::Backend("Test error".into()).into())
+		fn hash(&self, number: u64) -> ClientResult<Option<Hash>> {
+			if number == 0 {
+				Ok(Some(Default::default()))
+			} else {
+				Err(ClientErrorKind::Backend("Test error".into()).into())
+			}
 		}
 	}
 
@@ -235,7 +243,7 @@ pub mod tests {
 			_state: NewBlockState,
 			_aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
 		) -> ClientResult<()> {
-			Err(ClientErrorKind::Backend("Test error".into()).into())
+			Ok(())
 		}
 
 		fn finalize_header(&self, _block: BlockId<Block>) -> ClientResult<()> {
diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs
index 734a1994bfb1b52b6108ae4cc818d5e433f1e283..05fd9dffeddd0afb2475a82ad7c3806f9a332c29 100644
--- a/core/client/src/light/call_executor.rs
+++ b/core/client/src/light/call_executor.rs
@@ -17,7 +17,7 @@
 //! Light client call exector. Executes methods on remote full nodes, fetching
 //! execution proof and checking it locally.
 
-use std::{collections::HashSet, marker::PhantomData, sync::Arc};
+use std::{collections::HashSet, sync::Arc, panic::UnwindSafe};
 use futures::{IntoFuture, Future};
 
 use codec::{Encode, Decode};
@@ -28,6 +28,7 @@ use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChange
 	create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager};
 use hash_db::Hasher;
 
+use crate::backend::RemoteBackend;
 use crate::blockchain::Backend as ChainBackend;
 use crate::call_executor::CallExecutor;
 use crate::error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
@@ -38,35 +39,43 @@ use trie::MemoryDB;
 
 /// Call executor that executes methods on remote node, querying execution proof
 /// and checking proof by re-executing locally.
-pub struct RemoteCallExecutor<B, F, H> {
+pub struct RemoteCallExecutor<B, F> {
 	blockchain: Arc<B>,
 	fetcher: Arc<F>,
-	_hasher: PhantomData<H>,
 }
 
-impl<B, F, H> Clone for RemoteCallExecutor<B, F, H> {
+/// Remote or local call executor.
+///
+/// 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> {
+	backend: Arc<B>,
+	remote: R,
+	local: L,
+	_block: ::std::marker::PhantomData<Block>,
+}
+
+impl<B, F> Clone for RemoteCallExecutor<B, F> {
 	fn clone(&self) -> Self {
 		RemoteCallExecutor {
 			blockchain: self.blockchain.clone(),
 			fetcher: self.fetcher.clone(),
-			_hasher: Default::default(),
 		}
 	}
 }
 
-impl<B, F, H> RemoteCallExecutor<B, F, H> {
+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, _hasher: PhantomData }
+		RemoteCallExecutor { blockchain, fetcher }
 	}
 }
 
-impl<B, F, Block, H> CallExecutor<Block, H> for RemoteCallExecutor<B, F, H>
+impl<B, F, Block> CallExecutor<Block, Blake2Hasher> for RemoteCallExecutor<B, F>
 where
-	Block: BlockT,
+	Block: BlockT<Hash=H256>,
 	B: ChainBackend<Block>,
 	F: Fetcher<Block>,
-	H: Hasher<Out=Block::Hash>,
 	Block::Hash: Ord,
 {
 	type Error = ClientError;
@@ -118,7 +127,7 @@ where
 	}
 
 	fn call_at_state<
-		S: StateBackend<H>,
+		S: StateBackend<Blake2Hasher>,
 		FF: FnOnce(
 			Result<NativeOrEncoded<R>, Self::Error>,
 			Result<NativeOrEncoded<R>, Self::Error>
@@ -132,13 +141,13 @@ where
 		_call_data: &[u8],
 		_m: ExecutionManager<FF>,
 		_native_call: Option<NC>,
-	) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<H>>)> {
+	) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
 		Err(ClientErrorKind::NotAvailableOnLightClient.into())
 	}
 
-	fn prove_at_trie_state<S: state_machine::TrieBackendStorage<H>>(
+	fn prove_at_trie_state<S: state_machine::TrieBackendStorage<Blake2Hasher>>(
 		&self,
-		_state: &state_machine::TrieBackend<S, H>,
+		_state: &state_machine::TrieBackend<S, Blake2Hasher>,
 		_changes: &mut OverlayedChanges,
 		_method: &str,
 		_call_data: &[u8]
@@ -151,6 +160,177 @@ where
 	}
 }
 
+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,
+{
+	fn clone(&self) -> Self {
+		RemoteOrLocalCallExecutor {
+			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>
+	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;
+
+	fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<Vec<u8>> {
+		match self.backend.is_local_state_available(id) {
+			true => self.local.call(id, method, call_data),
+			false => self.remote.call(id, method, call_data),
+		}
+	}
+
+	fn contextual_call<
+		PB: Fn() -> ClientResult<Block::Header>,
+		EM: Fn(
+			Result<NativeOrEncoded<R>, Self::Error>,
+			Result<NativeOrEncoded<R>, Self::Error>
+		) -> Result<NativeOrEncoded<R>, Self::Error>,
+		R: Encode + Decode + PartialEq,
+		NC: FnOnce() -> R + UnwindSafe,
+	>(
+		&self,
+		at: &BlockId<Block>,
+		method: &str,
+		call_data: &[u8],
+		changes: &mut OverlayedChanges,
+		initialised_block: &mut Option<BlockId<Block>>,
+		prepare_environment_block: PB,
+		_manager: ExecutionManager<EM>,
+		native_call: Option<NC>,
+	) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
+		// there's no actual way/need to specify native/wasm execution strategy on light node
+		// => we can safely ignore passed values
+
+		match self.backend.is_local_state_available(at) {
+			true => CallExecutor::contextual_call::<
+				_,
+				fn(
+					Result<NativeOrEncoded<R>, Local::Error>,
+					Result<NativeOrEncoded<R>, Local::Error>,
+				) -> Result<NativeOrEncoded<R>, Local::Error>,
+				_,
+				NC
+			>(
+				&self.local,
+				at,
+				method,
+				call_data,
+				changes,
+				initialised_block,
+				prepare_environment_block,
+				ExecutionManager::NativeWhenPossible,
+				native_call,
+			).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()),
+			false => CallExecutor::contextual_call::<
+				_,
+				fn(
+					Result<NativeOrEncoded<R>, Remote::Error>,
+					Result<NativeOrEncoded<R>, Remote::Error>,
+				) -> Result<NativeOrEncoded<R>, Remote::Error>,
+				_,
+				NC
+			>(
+				&self.remote,
+				at,
+				method,
+				call_data,
+				changes,
+				initialised_block,
+				prepare_environment_block,
+				ExecutionManager::NativeWhenPossible,
+				native_call,
+			).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()),
+		}
+	}
+
+	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),
+		}
+	}
+
+	fn call_at_state<
+		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() -> R + UnwindSafe,
+	>(&self,
+		state: &S,
+		changes: &mut OverlayedChanges,
+		method: &str,
+		call_data: &[u8],
+		_manager: ExecutionManager<FF>,
+		native_call: Option<NC>,
+	) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
+		// 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,
+			).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).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>>)> {
+		self.remote.prove_at_trie_state(state, changes, method, call_data)
+	}
+
+	fn native_runtime_version(&self) -> Option<&NativeVersion> {
+		None
+	}
+}
+
 /// Prove contextual execution using given block header in environment.
 ///
 /// Method is executed using passed header as environment' current block.
@@ -243,6 +423,9 @@ mod tests {
 	use consensus::BlockOrigin;
 	use test_client::{self, runtime::{Block, Header}, runtime::RuntimeApi, TestClient};
 	use executor::NativeExecutionDispatch;
+	use crate::backend::{Backend, NewBlockState};
+	use crate::in_mem::Backend as InMemBackend;
+	use crate::light::fetcher::tests::OkCallFetcher;
 	use super::*;
 
 	#[test]
@@ -309,4 +492,22 @@ mod tests {
 		let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
 		assert_eq!(local_block.number, 3);
 	}
+
+	#[test]
+	fn code_is_executed_locally_or_remotely() {
+		let backend = Arc::new(InMemBackend::new());
+		let def = H256::default();
+		let header0 = test_client::runtime::Header::new(0, def, def, def, Default::default());
+		let hash0 = header0.hash();
+		let header1 = test_client::runtime::Header::new(1, def, def, hash0, Default::default());
+		let hash1 = header1.hash();
+		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);
+		assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[]).unwrap(), vec![1]);
+		assert_eq!(remote_or_local.call(&BlockId::Number(1), "test_method", &[]).unwrap(), vec![2]);
+	}
 }
diff --git a/core/client/src/light/mod.rs b/core/client/src/light/mod.rs
index 4dc25affd126fed4cc35f87548213e925623cdcf..6bb7f7ac92dbab9ca6307cdc699d889b268a06b0 100644
--- a/core/client/src/light/mod.rs
+++ b/core/client/src/light/mod.rs
@@ -23,18 +23,19 @@ pub mod fetcher;
 
 use std::sync::Arc;
 
+use executor::RuntimeInfo;
 use primitives::{H256, Blake2Hasher};
 use runtime_primitives::BuildStorage;
 use runtime_primitives::traits::Block as BlockT;
 use state_machine::{CodeExecutor, ExecutionStrategy};
 
+use crate::call_executor::LocalCallExecutor;
 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;
+use crate::light::call_executor::{RemoteCallExecutor, RemoteOrLocalCallExecutor};
 use crate::light::fetcher::{Fetcher, LightDataChecker};
-use hash_db::Hasher;
 
 /// 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>> {
@@ -42,37 +43,48 @@ pub fn new_light_blockchain<B: BlockT, S: BlockchainStorage<B>, F>(storage: S) -
 }
 
 /// Create an instance of light client backend.
-pub fn new_light_backend<B: BlockT, S: BlockchainStorage<B>, F: Fetcher<B>>(blockchain: Arc<Blockchain<S, F>>, fetcher: Arc<F>) -> Arc<Backend<S, F>> {
+pub fn new_light_backend<B, S, F>(blockchain: Arc<Blockchain<S, F>>, fetcher: Arc<F>) -> Arc<Backend<S, F, 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>(
-	backend: Arc<Backend<S, F>>,
+pub fn new_light<B, S, F, GS, RA, E>(
+	backend: Arc<Backend<S, F, Blake2Hasher>>,
 	fetcher: Arc<F>,
 	genesis_storage: GS,
-) -> ClientResult<Client<Backend<S, F>, RemoteCallExecutor<Blockchain<S, F>, F, Blake2Hasher>, B, RA>>
-where
-	B: BlockT<Hash=H256>,
-	S: BlockchainStorage<B>,
-	F: Fetcher<B>,
-	GS: BuildStorage,
-
+	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>
+>, B, RA>>
+	where
+		B: BlockT<Hash=H256>,
+		S: BlockchainStorage<B>,
+		F: Fetcher<B>,
+		GS: BuildStorage,
+		E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
 {
-	let executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher);
+	let remote_executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher);
+	let local_executor = LocalCallExecutor::new(backend.clone(), code_executor);
+	let executor = RemoteOrLocalCallExecutor::new(backend.clone(), remote_executor, local_executor);
 	Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeWhenPossible)
 }
 
 /// Create an instance of fetch data checker.
-pub fn new_fetch_checker<E, H, B: BlockT, S: BlockchainStorage<B>, F>(
+pub fn new_fetch_checker<E, B: BlockT, S: BlockchainStorage<B>, F>(
 	blockchain: Arc<Blockchain<S, F>>,
 	executor: E,
-) -> LightDataChecker<E, H, B, S, F>
+) -> LightDataChecker<E, Blake2Hasher, B, S, F>
 	where
-		E: CodeExecutor<H>,
-		H: Hasher,
-
+		E: CodeExecutor<Blake2Hasher>,
 {
 	LightDataChecker::new(blockchain, executor)
 }
diff --git a/core/service/src/components.rs b/core/service/src/components.rs
index c966911996b6278fd703c1382e1cf353316c6258..c1a9c63fe9e086d308542bde4b97e8f30e99bfb9 100644
--- a/core/service/src/components.rs
+++ b/core/service/src/components.rs
@@ -60,16 +60,32 @@ pub type FullExecutor<F> = client::LocalCallExecutor<
 pub type LightBackend<F> = client::light::backend::Backend<
 	client_db::light::LightStorage<<F as ServiceFactory>::Block>,
 	network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>,
+	Blake2Hasher,
 >;
 
 /// Light client executor type for a factory.
-pub type LightExecutor<F> = client::light::call_executor::RemoteCallExecutor<
-	client::light::blockchain::Blockchain<
+pub type LightExecutor<F> = client::light::call_executor::RemoteOrLocalCallExecutor<
+	<F as ServiceFactory>::Block,
+	client::light::backend::Backend<
 		client_db::light::LightStorage<<F as ServiceFactory>::Block>,
+		network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>,
+		Blake2Hasher
+	>,
+	client::light::call_executor::RemoteCallExecutor<
+		client::light::blockchain::Blockchain<
+			client_db::light::LightStorage<<F as ServiceFactory>::Block>,
+			network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>
+		>,
 		network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>
 	>,
-	network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>,
-	Blake2Hasher,
+	client::LocalCallExecutor<
+		client::light::backend::Backend<
+			client_db::light::LightStorage<<F as ServiceFactory>::Block>,
+			network::OnDemand<<F as ServiceFactory>::Block, NetworkService<F>>,
+			Blake2Hasher
+		>,
+		CodeExecutor<F>
+	>
 >;
 
 /// Full client type for a factory.
@@ -499,10 +515,10 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
 		};
 		let db_storage = client_db::light::LightStorage::new(db_settings)?;
 		let light_blockchain = client::light::new_light_blockchain(db_storage);
-		let fetch_checker = Arc::new(client::light::new_fetch_checker::<_, Blake2Hasher, _, _, _>(light_blockchain.clone(), executor));
+		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 client_backend = client::light::new_light_backend(light_blockchain, fetcher.clone());
-		let client = client::light::new_light(client_backend, fetcher.clone(), &config.chain_spec)?;
+		let client = client::light::new_light(client_backend, fetcher.clone(), &config.chain_spec, executor)?;
 		Ok((Arc::new(client), Some(fetcher)))
 	}