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);