diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs
index d276992f08cdf9b070842b80814e9b4d48fc33ca..5d8d3cb6061567dd9eb0e837f4344c46e5eab7b0 100644
--- a/substrate/core/client/db/src/lib.rs
+++ b/substrate/core/client/db/src/lib.rs
@@ -1169,6 +1169,10 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
 		}
 	}
 
+	fn have_state_at(&self, hash: &Block::Hash, number: NumberFor<Block>) -> bool {
+		!self.storage.state_db.is_pruned(hash, number.as_())
+	}
+
 	fn destroy_state(&self, mut state: Self::State) -> Result<(), client::error::Error> {
 		if let Some(hash) = state.parent_hash.clone() {
 			let is_best = || self.blockchain.meta.read().best_hash == hash;
diff --git a/substrate/core/client/src/backend.rs b/substrate/core/client/src/backend.rs
index a0975db888d7531eada2cde5452a9be5f6e9d2a1..9b063177ff14bd4b66f0fcb55c8ae562a2a18b23 100644
--- a/substrate/core/client/src/backend.rs
+++ b/substrate/core/client/src/backend.rs
@@ -142,6 +142,10 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
 	fn blockchain(&self) -> &Self::Blockchain;
 	/// Returns reference to changes trie storage.
 	fn changes_trie_storage(&self) -> Option<&Self::ChangesTrieStorage>;
+	/// Returns true if state for given block is available.
+	fn have_state_at(&self, hash: &Block::Hash, _number: NumberFor<Block>) -> bool {
+		self.state_at(BlockId::Hash(hash.clone())).is_ok()
+	}
 	/// Returns state backend with post-state of given block.
 	fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State>;
 	/// Destroy state and save any useful data, such as cache.
diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs
index 34723deb8cca1316ec736bfc06273ce54199ad47..37ee1b5ce011a4aee0ddb37a51a72a192a9d8b17 100644
--- a/substrate/core/client/src/client.rs
+++ b/substrate/core/client/src/client.rs
@@ -167,8 +167,10 @@ pub struct ClientInfo<Block: BlockT> {
 pub enum BlockStatus {
 	/// Added to the import queue.
 	Queued,
-	/// Already in the blockchain.
-	InChain,
+	/// Already in the blockchain and the state is available.
+	InChainWithState,
+	/// In the blockchain, but the state is not available.
+	InChainPruned,
 	/// Block or parent is known to be bad.
 	KnownBad,
 	/// Not in the queue or the blockchain.
@@ -1073,9 +1075,19 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
 				return Ok(BlockStatus::Queued);
 			}
 		}
-		match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() {
-			true => Ok(BlockStatus::InChain),
-			false => Ok(BlockStatus::Unknown),
+		let hash_and_number = match id.clone() {
+			BlockId::Hash(hash) => self.backend.blockchain().number(hash)?.map(|n| (hash, n)),
+			BlockId::Number(n) => self.backend.blockchain().hash(n)?.map(|hash| (hash, n)),
+		};
+		match hash_and_number {
+			Some((hash, number)) => {
+				if self.backend().have_state_at(&hash, number) {
+					Ok(BlockStatus::InChainWithState)
+				} else {
+					Ok(BlockStatus::InChainPruned)
+				}
+			}
+			None => Ok(BlockStatus::Unknown),
 		}
 	}
 
@@ -1386,18 +1398,20 @@ impl<B, E, Block, RA> consensus::BlockImport<Block> for Client<B, E, Block, RA>
 		hash: Block::Hash,
 		parent_hash: Block::Hash,
 	) -> Result<ImportResult, Self::Error> {
-		match self.backend.blockchain().status(BlockId::Hash(parent_hash))
+		match self.block_status(&BlockId::Hash(parent_hash))
 			.map_err(|e| ConsensusError::from(ConsensusErrorKind::ClientImport(e.to_string())))?
 		{
-			blockchain::BlockStatus::InChain => {},
-			blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
+			BlockStatus::InChainWithState | BlockStatus::Queued => {},
+			BlockStatus::Unknown | BlockStatus::InChainPruned => return Ok(ImportResult::UnknownParent),
+			BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
 		}
 
-		match self.backend.blockchain().status(BlockId::Hash(hash))
+		match self.block_status(&BlockId::Hash(hash))
 			.map_err(|e| ConsensusError::from(ConsensusErrorKind::ClientImport(e.to_string())))?
 		{
-			blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain),
-			blockchain::BlockStatus::Unknown => {},
+			BlockStatus::InChainWithState | BlockStatus::Queued => return Ok(ImportResult::AlreadyInChain),
+			BlockStatus::Unknown | BlockStatus::InChainPruned => {},
+			BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
 		}
 
 		Ok(ImportResult::imported())
diff --git a/substrate/core/client/src/light/blockchain.rs b/substrate/core/client/src/light/blockchain.rs
index 973096ad0c5c2e1263779e5dd84114544ce35828..add183f67df59390bde9425168bbbea3fce91695 100644
--- a/substrate/core/client/src/light/blockchain.rs
+++ b/substrate/core/client/src/light/blockchain.rs
@@ -109,7 +109,7 @@ impl<S, F, Block> BlockchainHeaderBackend<Block> for Blockchain<S, F> where Bloc
 				};
 
 				// if the header is from future or genesis (we never prune genesis) => return
-				if number.is_zero() || self.storage.status(BlockId::Number(number))? != BlockStatus::InChain {
+				if number.is_zero() || self.storage.status(BlockId::Number(number))? == BlockStatus::Unknown {
 					return Ok(None);
 				}
 
diff --git a/substrate/core/network/src/sync.rs b/substrate/core/network/src/sync.rs
index 76dbeabe798b4885bd7897db53bf4456561c745e..14222798ef4e78d0c2a42e0b00d6a2178552caa1 100644
--- a/substrate/core/network/src/sync.rs
+++ b/substrate/core/network/src/sync.rs
@@ -46,7 +46,6 @@ const JUSTIFICATION_RETRY_WAIT: Duration = Duration::from_secs(10);
 // Number of recently announced blocks to track for each peer.
 const ANNOUNCE_HISTORY_SIZE: usize = 64;
 // Max number of blocks to download for unknown forks.
-// TODO: this should take finality into account. See https://github.com/paritytech/substrate/issues/1606
 const MAX_UNKNOWN_FORK_DOWNLOAD_LEN: u32 = 32;
 
 #[derive(Debug)]
@@ -500,7 +499,7 @@ impl<B: BlockT> ChainSync<B> {
 						self.download_new(protocol, who)
 					}
 				},
-				(Ok(BlockStatus::Queued), _) | (Ok(BlockStatus::InChain), _) => {
+				(Ok(BlockStatus::Queued), _) | (Ok(BlockStatus::InChainWithState), _) | (Ok(BlockStatus::InChainPruned), _) => {
 					debug!(target:"sync", "New peer with known best hash {} ({}).", info.best_hash, info.best_number);
 					self.peers.insert(who, PeerSync {
 						common_number: info.best_number,
@@ -834,7 +833,11 @@ impl<B: BlockT> ChainSync<B> {
 			trace!(target: "sync", "Ignored invalid block announcement from {}: {}", who, hash);
 			return;
 		}
-		let known_parent = self.is_known(protocol, &header.parent_hash());
+		let parent_status = block_status(&*protocol.client(), &self.queue_blocks, header.parent_hash().clone()).ok()
+			.unwrap_or(BlockStatus::Unknown);
+		let known_parent = parent_status != BlockStatus::Unknown;
+		let ancient_parent = parent_status == BlockStatus::InChainPruned;
+
 		let known = self.is_known(protocol, &hash);
 		if let Some(ref mut peer) = self.peers.get_mut(&who) {
 			while peer.recently_announced.len() >= ANNOUNCE_HISTORY_SIZE {
@@ -862,15 +865,28 @@ impl<B: BlockT> ChainSync<B> {
 			let stale = number <= self.best_queued_number;
 			if stale {
 				if !(known_parent || self.is_already_downloading(header.parent_hash())) {
-					trace!(target: "sync", "Considering new unknown stale block announced from {}: {} {:?}", who, hash, header);
-					self.download_unknown_stale(protocol, who, &hash);
+					if protocol.client().block_status(&BlockId::Number(*header.number()))
+						.unwrap_or(BlockStatus::Unknown) == BlockStatus::InChainPruned
+					{
+						trace!(target: "sync", "Ignored unknown ancient block announced from {}: {} {:?}", who, hash, header);
+					} else {
+						trace!(target: "sync", "Considering new unknown stale block announced from {}: {} {:?}", who, hash, header);
+						self.download_unknown_stale(protocol, who, &hash);
+					}
 				} else {
-					trace!(target: "sync", "Considering new stale block announced from {}: {} {:?}", who, hash, header);
-					self.download_stale(protocol, who, &hash);
+					if ancient_parent {
+						trace!(target: "sync", "Ignored ancient stale block announced from {}: {} {:?}", who, hash, header);
+					} else {
+						self.download_stale(protocol, who, &hash);
+					}
 				}
 			} else {
-				trace!(target: "sync", "Considering new block announced from {}: {} {:?}", who, hash, header);
-				self.download_new(protocol, who);
+				if ancient_parent {
+					trace!(target: "sync", "Ignored ancient block announced from {}: {} {:?}", who, hash, header);
+				} else {
+					trace!(target: "sync", "Considering new block announced from {}: {} {:?}", who, hash, header);
+					self.download_new(protocol, who);
+				}
 			}
 		} else {
 			trace!(target: "sync", "Known block announce from {}: {}", who, hash);