Repository: paritytech/parity-bitcoin (language: rust)

Issue:
Backports from parity-zcash
 - [x] [Minimize chain structures hashing (part#1)](https://github.com/paritytech/parity-zcash/pull/59)
 - [x] [Penalize peer when receiving unknown headers message](https://github.com/paritytech/parity-zcash/pull/61)
- [x] [Minimize chain structures hashing (part#2)](https://github.com/paritytech/parity-zcash/pull/63)
- [x] [Disable TransactionDoubleSpend for coinbase transactions](https://github.com/paritytech/parity-zcash/pull/71) (probably even merge `pruning` branch before merging that?)
- [x] [Cache transaction outputs in ChainAcceptor](https://github.com/paritytech/parity-zcash/pull/72)
- [x] [Name all spawned threads](https://github.com/paritytech/parity-zcash/pull/73)
- [x] [Hash header(s) without holding sync lock](https://github.com/paritytech/parity-zcash/pull/74)
- [x] [Display actual import error when import has failed](https://github.com/paritytech/parity-zcash/pull/76)
- [x] [Handle reorganizations during blocks import](https://github.com/paritytech/parity-zcash/pull/78)
- [x] [Ignore zero-filled gaps in blk files](https://github.com/paritytech/parity-zcash/pull/83)
- [x] [Improve error reporting in import handler](https://github.com/paritytech/parity-zcash/pull/86)

Non-mandatory candidates:
- [ ] [Verify headers in separate thread](https://github.com/paritytech/parity-zcash/pull/67) + [Fixed panic when header is verifying for (Verifying | Stored) block](https://github.com/paritytech/parity-zcash/pull/99)
- [ ] [Actually verify headers](https://github.com/paritytech/parity-zcash/pull/69)
- [ ] [Do not verify headers twice during synchronization](https://github.com/paritytech/parity-zcash/pull/70)


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index b4abb493e..744975af3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,3 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
 [[package]]
 name = "abstract-ns"
 version = "0.3.4"
diff --git a/chain/src/transaction.rs b/chain/src/transaction.rs
index 4b104fb23..e4cbcb80f 100644
--- a/chain/src/transaction.rs
+++ b/chain/src/transaction.rs
@@ -16,7 +16,7 @@ const WITNESS_MARKER: u8 = 0;
 /// Must be nonzero.
 const WITNESS_FLAG: u8 = 1;
 
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serializable, Deserializable)]
+#[derive(Debug, PartialEq, Eq, Clone, Default, Serializable, Deserializable, Hash)]
 pub struct OutPoint {
 	pub hash: H256,
 	pub index: u32,
diff --git a/db/src/block_chain_db.rs b/db/src/block_chain_db.rs
index c23be1540..c8a9b3a32 100644
--- a/db/src/block_chain_db.rs
+++ b/db/src/block_chain_db.rs
@@ -251,10 +251,19 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
 		let mut best_block = self.best_block.write();
 		let block = match self.indexed_block(hash.clone().into()) {
 			Some(block) => block,
-			None => return Err(Error::CannotCanonize),
+			None => {
+				error!(target: "db", "Block is not found during canonization: {}", hash.reversed());
+				return Err(Error::CannotCanonize);
+			},
 		};
 
 		if best_block.hash != block.header.raw.previous_header_hash {
+			error!(
+				target: "db",
+				"Wrong best block during canonization. Best {}, parent: {}",
+				best_block.hash.reversed(),
+				block.header.raw.previous_header_hash.reversed(),
+			);
 			return Err(Error::CannotCanonize);
 		}
 
@@ -295,7 +304,16 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
 					},
 					Entry::Vacant(entry) => {
 						let mut meta = self.transaction_meta(&input.previous_output.hash)
-							.ok_or(Error::CannotCanonize)?;
+							.ok_or_else(|| {
+								error!(
+									target: "db",
+									"Cannot find tx meta during canonization of tx {}: {}/{}",
+									tx.hash.reversed(),
+									input.previous_output.hash.reversed(),
+									input.previous_output.index,
+								);
+								Error::CannotCanonize
+							})?;
 						meta.denote_used(input.previous_output.index as usize);
 						entry.insert(meta);
 					}
@@ -316,7 +334,10 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
 		let mut best_block = self.best_block.write();
 		let block = match self.indexed_block(best_block.hash.clone().into()) {
 			Some(block) => block,
-			None => return Err(Error::CannotCanonize),
+			None => {
+				error!(target: "db", "Block is not found during decanonization: {}", best_block.hash.reversed());
+				return Err(Error::CannotDecanonize)
+			},
 		};
 		let block_number = best_block.number;
 		let block_hash = best_block.hash.clone();
@@ -351,7 +372,16 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
 					},
 					Entry::Vacant(entry) => {
 						let mut meta = self.transaction_meta(&input.previous_output.hash)
-							.ok_or(Error::CannotCanonize)?;
+							.ok_or_else(|| {
+								error!(
+									target: "db",
+									"Cannot find tx meta during decanonization of tx {}: {}/{}",
+									tx.hash.reversed(),
+									input.previous_output.hash.reversed(),
+									input.previous_output.index,
+								);
+								Error::CannotDecanonize
+							})?;
 						meta.denote_unused(input.previous_output.index as usize);
 						entry.insert(meta);
 					}
diff --git a/import/src/block.rs b/import/src/block.rs
index fb8938812..fd14fada5 100644
--- a/import/src/block.rs
+++ b/import/src/block.rs
@@ -12,12 +12,18 @@ pub struct Block {
 
 impl Deserializable for Block {
 	fn deserialize<T>(reader: &mut Reader<T>) -> Result<Self, ReaderError> where T: io::Read {
-		let block = Block {
-			magic: try!(reader.read()),
-			block_size: try!(reader.read()),
-			block: try!(reader.read()),
-		};
+		// We never knew how to parse blocks index file => we were assuming that blocks are stored
+		// in the *.blk files next to each other, without any gaps.
+		// It seems that this isn't true && there are (sometimes) zero-filled (always???) gaps between
+		// adjacent blocks.
+		// The straightforward soultion is to skip zero bytes. This will work because magic is designed
+		// not to have zero bytes in it AND block is always prefixed with magic.
+		reader.skip_while(&|byte| byte == 0)?;
 
-		Ok(block)
+		Ok(Block {
+			magic: reader.read()?,
+			block_size: reader.read()?,
+			block: reader.read()?,
+		})
 	}
-}
+}
\ No newline at end of file
diff --git a/p2p/src/p2p.rs b/p2p/src/p2p.rs
index 1c4bc5d46..93b3441f1 100644
--- a/p2p/src/p2p.rs
+++ b/p2p/src/p2p.rs
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
 use parking_lot::RwLock;
 use futures::{Future, finished, failed};
 use futures::stream::Stream;
-use futures_cpupool::CpuPool;
+use futures_cpupool::{CpuPool, Builder as CpuPoolBuilder};
 use tokio_io::IoFuture;
 use tokio_core::net::{TcpListener, TcpStream};
 use tokio_core::reactor::{Handle, Remote, Timeout, Interval};
@@ -430,7 +430,10 @@ impl Drop for P2P {
 
 impl P2P {
 	pub fn new(config: Config, local_sync_node: LocalSyncNodeRef, handle: Handle) -> Result<Self, Box<error::Error>> {
-		let pool = CpuPool::new(config.threads);
+		let pool = CpuPoolBuilder::new()
+			.name_prefix("I/O thread")
+			.pool_size(config.threads)
+			.create();
 
 		let context = try!(Context::new(local_sync_node, pool.clone(), handle.remote().clone(), config.clone()));
 
diff --git a/pbtc/commands/import.rs b/pbtc/commands/import.rs
index 99144951b..41f60f46e 100644
--- a/pbtc/commands/import.rs
+++ b/pbtc/commands/import.rs
@@ -7,14 +7,16 @@ pub fn import(cfg: Config, matches: &ArgMatches) -> Result<(), String> {
 	try!(init_db(&cfg));
 
 	let blk_path = matches.value_of("PATH").expect("PATH is required in cli.yml; qed");
+	let blk_dir = ::import::open_blk_dir(blk_path)
+		.map_err(|err| format!("Failed to open import directory: {}", err))?;
 
 	let mut writer = create_sync_blocks_writer(cfg.db, cfg.consensus, cfg.verification_params);
-
-	let blk_dir = try!(::import::open_blk_dir(blk_path).map_err(|_| "Import directory does not exist".to_owned()));
 	let mut counter = 0;
+	let mut previous_hash = None;
 	for blk in blk_dir {
 		// TODO: verify magic!
-		let blk = try!(blk.map_err(|_| "Cannot read block".to_owned()));
+		let blk = blk.map_err(|err| format!("Cannot read block: {:?}. Previous block: {:?}", err, previous_hash))?;
+		let blk_hash = blk.block.hash().reversed();
 		match writer.append_block(blk.block) {
 			Ok(_) => {
 				counter += 1;
@@ -23,8 +25,10 @@ pub fn import(cfg: Config, matches: &ArgMatches) -> Result<(), String> {
 				}
 			}
 			Err(Error::TooManyOrphanBlocks) => return Err("Too many orphan (unordered) blocks".into()),
-			Err(_) => return Err("Cannot append block".into()),
+			Err(err) => return Err(format!("Cannot append block: {:?}. Block: {}", err, blk_hash)),
 		}
+
+		previous_hash = Some(blk_hash);
 	}
 
 	info!("Finished import of {} blocks", counter);
diff --git a/serialization/src/reader.rs b/serialization/src/reader.rs
index 653a99b56..95566ac10 100644
--- a/serialization/src/reader.rs
+++ b/serialization/src/reader.rs
@@ -89,6 +89,24 @@ impl<R> Reader<R> where R: io::Read {
 		T::deserialize(&mut reader)
 	}
 
+	pub fn skip_while(&mut self, predicate: &Fn(u8) -> bool) -> Result<(), Error> {
+		let mut next_buffer = [0u8];
+		loop {
+			let next = match self.peeked.take() {
+				Some(peeked) => peeked,
+				None => match self.buffer.read(&mut next_buffer)? {
+					0 => return Ok(()),
+					_ => next_buffer[0],
+				},
+			};
+
+			if !predicate(next) {
+				self.peeked = Some(next);
+				return Ok(());
+			}
+		}
+	}
+
 	pub fn read_slice(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
 		io::Read::read_exact(self, bytes).map_err(|_| Error::UnexpectedEnd)
 	}
diff --git a/storage/src/error.rs b/storage/src/error.rs
index 0e11a1372..b9beee093 100644
--- a/storage/src/error.rs
+++ b/storage/src/error.rs
@@ -3,9 +3,12 @@ pub enum Error {
 	/// Low level database error
 	#[display(fmt = "Database error: {}", _0)]
 	DatabaseError(String),
-	/// Invalid block
+	/// Cannot canonize block
 	#[display(fmt = "Cannot canonize block")]
 	CannotCanonize,
+	/// Cannot decanonize block
+	#[display(fmt = "Cannot decanonize block")]
+	CannotDecanonize,
 	/// Uknown parent
 	#[display(fmt = "Block parent is unknown")]
 	UnknownParent,
diff --git a/storage/src/lib.rs b/storage/src/lib.rs
index 1efb20613..80ea4901e 100644
--- a/storage/src/lib.rs
+++ b/storage/src/lib.rs
@@ -37,5 +37,6 @@ pub use duplex_store::{DuplexTransactionOutputProvider, NoopStore, transaction_i
 pub use error::Error;
 pub use store::{AsSubstore, Store, SharedStore, CanonStore, ConfigStore};
 pub use transaction_meta::TransactionMeta;
-pub use transaction_provider::{TransactionProvider, TransactionOutputProvider, TransactionMetaProvider};
-
+pub use transaction_provider::{
+	TransactionProvider, TransactionOutputProvider, TransactionMetaProvider, CachedTransactionOutputProvider,
+};
diff --git a/storage/src/transaction_provider.rs b/storage/src/transaction_provider.rs
index c16b626a8..376baa89d 100644
--- a/storage/src/transaction_provider.rs
+++ b/storage/src/transaction_provider.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+use parking_lot::RwLock;
 use hash::H256;
 use bytes::Bytes;
 use chain::{Transaction, OutPoint, TransactionOutput};
@@ -32,3 +34,40 @@ pub trait TransactionMetaProvider: Send + Sync {
 	/// Otherwise returns transaction meta object
 	fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
 }
+
+/// Transaction output provider that caches all read outputs.
+///
+/// Not intended for long-lasting life, because it never clears its internal
+/// cache. The backing storage is considered readonly for the cache lifetime.
+pub struct CachedTransactionOutputProvider<'a> {
+	backend: &'a TransactionOutputProvider,
+	cached_outputs: RwLock<HashMap<OutPoint, Option<TransactionOutput>>>,
+}
+
+impl<'a> CachedTransactionOutputProvider<'a> {
+	/// Create new cached tx output provider backed by passed provider.
+	pub fn new(backend: &'a TransactionOutputProvider) -> Self {
+		CachedTransactionOutputProvider {
+			backend,
+			cached_outputs: RwLock::new(HashMap::new()),
+		}
+	}
+}
+
+impl<'a> TransactionOutputProvider for CachedTransactionOutputProvider<'a> {
+	fn transaction_output(&self, outpoint: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
+		let cached_value = self.cached_outputs.read().get(outpoint).cloned();
+		match cached_value {
+			Some(cached_value) => cached_value,
+			None => {
+				let value_from_backend = self.backend.transaction_output(outpoint, transaction_index);
+				self.cached_outputs.write().insert(outpoint.clone(), value_from_backend.clone());
+				value_from_backend
+			},
+		}
+	}
+
+	fn is_spent(&self, outpoint: &OutPoint) -> bool {
+		self.backend.is_spent(outpoint)
+	}
+}
diff --git a/sync/src/blocks_writer.rs b/sync/src/blocks_writer.rs
index 431a6b814..f421b2c2f 100644
--- a/sync/src/blocks_writer.rs
+++ b/sync/src/blocks_writer.rs
@@ -6,6 +6,7 @@ use storage;
 use network::ConsensusParams;
 use primitives::hash::H256;
 use super::Error;
+use synchronization_chain::Chain;
 use synchronization_verifier::{Verifier, SyncVerifier, VerificationTask,
 	VerificationSink, BlockVerificationSink, TransactionVerificationSink};
 use types::StorageRef;
@@ -24,27 +25,27 @@ pub struct BlocksWriter {
 	/// Blocks verifier
 	verifier: SyncVerifier<BlocksWriterSink>,
 	/// Verification events receiver
-	sink: Arc<BlocksWriterSinkData>,
+	sink: Arc<Mutex<BlocksWriterSinkData>>,
 }
 
 /// Verification events receiver
 struct BlocksWriterSink {
 	/// Reference to blocks writer data
-	data: Arc<BlocksWriterSinkData>,
+	data: Arc<Mutex<BlocksWriterSinkData>>,
 }
 
 /// Blocks writer data
 struct BlocksWriterSinkData {
-	/// Blocks storage
-	storage: StorageRef,
+	/// Synchronization chain.
+	chain: Chain,
 	/// Last verification error
-	err: Mutex<Option<Error>>,
+	err: Option<Error>,
 }
 
 impl BlocksWriter {
 	/// Create new synchronous blocks writer
 	pub fn new(storage: StorageRef, consensus: ConsensusParams, verification_params: VerificationParameters) -> BlocksWriter {
-		let sink_data = Arc::new(BlocksWriterSinkData::new(storage.clone()));
+		let sink_data = Arc::new(Mutex::new(BlocksWriterSinkData::new(storage.clone(), consensus.clone())));
 		let sink = Arc::new(BlocksWriterSink::new(sink_data.clone()));
 		let verifier = SyncVerifier::new(consensus, storage.clone(), sink, verification_params);
 		BlocksWriter {
@@ -77,7 +78,7 @@ impl BlocksWriter {
 		verification_queue.push_front(block);
 		while let Some(block) = verification_queue.pop_front() {
 			self.verifier.verify_block(block);
-			if let Some(err) = self.sink.error() {
+			if let Some(err) = self.sink.lock().error() {
 				return Err(err);
 			}
 		}
@@ -88,7 +89,7 @@ impl BlocksWriter {
 
 impl BlocksWriterSink {
 	/// Create new verification events receiver
-	pub fn new(data: Arc<BlocksWriterSinkData>) -> Self {
+	pub fn new(data: Arc<Mutex<BlocksWriterSinkData>>) -> Self {
 		BlocksWriterSink {
 			data: data,
 		}
@@ -97,16 +98,16 @@ impl BlocksWriterSink {
 
 impl BlocksWriterSinkData {
 	/// Create new blocks writer data
-	pub fn new(storage: StorageRef) -> Self {
+	pub fn new(storage: StorageRef, consensus: ConsensusParams) -> Self {
 		BlocksWriterSinkData {
-			storage: storage,
-			err: Mutex::new(None),
+			chain: Chain::new(storage, consensus, Default::default()),
+			err: None,
 		}
 	}
 
 	/// Take last verification error
-	pub fn error(&self) -> Option<Error> {
-		self.err.lock().take()
+	pub fn error(&mut self) -> Option<Error> {
+		self.err.take()
 	}
 }
 
@@ -115,19 +116,16 @@ impl VerificationSink for BlocksWriterSink {
 
 impl BlockVerificationSink for BlocksWriterSink {
 	fn on_block_verification_success(&self, block: chain::IndexedBlock) -> Option<Vec<VerificationTask>> {
-		let hash = block.hash().clone();
-		if let Err(err) = self.data.storage.insert(block) {
-			*self.data.err.lock() = Some(Error::Database(err));
-		}
-		if let Err(err) = self.data.storage.canonize(&hash) {
-			*self.data.err.lock() = Some(Error::Database(err));
+		let mut data = self.data.lock();
+		if let Err(err) = data.chain.insert_best_block(block) {
+			data.err = Some(Error::Database(err));
 		}
 
 		None
 	}
 
 	fn on_block_verification_error(&self, err: &str, _hash: &H256) {
-		*self.data.err.lock() = Some(Error::Verification(err.into()));
+		self.data.lock().err = Some(Error::Verification(err.into()));
 	}
 }
 
@@ -209,4 +207,23 @@ mod tests {
 		assert!(blocks_target.append_block(test_data::block_h1().into()).is_ok());
 		assert_eq!(db.best_block().number, 1);
 	}
+
+	#[test]
+	fn blocks_write_able_to_reorganize() {
+		// (1) b0 ---> (2) b1
+		//        \--> (3) b2 ---> (4 - reorg) b3
+		let b0 = test_data::block_builder().header().build().build();
+		let b1 = test_data::block_builder().header().nonce(1).parent(b0.hash()).build().build();
+		let b2 = test_data::block_builder().header().nonce(2).parent(b0.hash()).build().build();
+		let b3 = test_data::block_builder().header().parent(b2.hash()).build().build();
+
+		let db = Arc::new(BlockChainDatabase::init_test_chain(vec![b0.into()]));
+		let mut blocks_target = BlocksWriter::new(db.clone(), ConsensusParams::new(Network::Testnet, ConsensusFork::BitcoinCore), VerificationParameters {
+			verification_level: VerificationLevel::NoVerification,
+			verification_edge: 0u8.into(),
+		});
+		assert_eq!(blocks_target.append_block(b1.into()), Ok(()));
+		assert_eq!(blocks_target.append_block(b2.into()), Ok(()));
+		assert_eq!(blocks_target.append_block(b3.into()), Ok(()));
+	}
 }
diff --git a/sync/src/lib.rs b/sync/src/lib.rs
index 26923991b..ddd03968e 100644
--- a/sync/src/lib.rs
+++ b/sync/src/lib.rs
@@ -48,7 +48,7 @@ use primitives::hash::H256;
 use verification::BackwardsCompatibleChainVerifier as ChainVerifier;
 
 /// Sync errors.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum Error {
 	/// Too many orphan blocks.
 	TooManyOrphanBlocks,
diff --git a/sync/src/synchronization_client_core.rs b/sync/src/synchronization_client_core.rs
index a91607b4c..fe5d8de1d 100644
--- a/sync/src/synchronization_client_core.rs
+++ b/sync/src/synchronization_client_core.rs
@@ -301,7 +301,20 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
 		// => all headers are also unknown to us
 		let header0 = headers[0].clone();
 		if self.chain.block_state(&header0.raw.previous_header_hash) == BlockState::Unknown {
-			warn!(target: "sync", "Previous header of the first header from peer#{} `headers` message is unknown. First: {}. Previous: {}", peer_index, header0.hash.to_reversed_str(), header0.raw.previous_header_hash.to_reversed_str());
+			warn!(
+				target: "sync",
+				"Previous header of the first header from peer#{} `headers` message is unknown. First: {}. Previous: {}",
+				peer_index,
+				header0.hash.to_reversed_str(),
+				header0.raw.previous_header_hash.to_reversed_str(),
+			);
+
+			// there could be competing chains that are running the network with the same magic (like Zcash vs ZelCash)
+			// => providing unknown headers. Penalize node so that it'll disconnect
+			if self.peers_tasks.penalize(peer_index) {
+				self.peers.misbehaving(peer_index, "Too many failures.");
+			}
+
 			return;
 		}
 
diff --git a/sync/src/synchronization_manager.rs b/sync/src/synchronization_manager.rs
index 23ab3f1c9..10adfee3f 100644
--- a/sync/src/synchronization_manager.rs
+++ b/sync/src/synchronization_manager.rs
@@ -316,7 +316,7 @@ mod tests {
 	use std::collections::HashSet;
 	use primitives::hash::H256;
 	use synchronization_peers::PeersImpl;
-	use synchronization_peers_tasks::PeersTasks;
+	use synchronization_peers_tasks::{PeersTasks, TrustLevel};
 	use super::{ManagePeersConfig, ManageUnknownBlocksConfig, ManageOrphanTransactionsConfig, manage_synchronization_peers_blocks,
 		manage_unknown_orphaned_blocks, manage_orphaned_transactions};
 	use utils::{OrphanBlocksPool, OrphanTransactionsPool};
@@ -335,10 +335,12 @@ mod tests {
 	fn manage_bad_peers() {
 		use std::thread::sleep;
 		use std::time::Duration;
-		let config = ManagePeersConfig { new_block_failure_interval_ms: 0, ..Default::default() };
+		let config = ManagePeersConfig { trusted_block_failure_interval_ms: 0, ..Default::default() };
 		let mut peers = PeersTasks::default();
 		peers.on_blocks_requested(1, &vec![H256::from(0)]);
 		peers.on_blocks_requested(2, &vec![H256::from(1)]);
+		peers.get_peer_stats_mut(1).unwrap().set_trust(TrustLevel::Trusted);
+		peers.get_peer_stats_mut(2).unwrap().set_trust(TrustLevel::Trusted);
 		sleep(Duration::from_millis(1));
 
 		let managed_tasks = manage_synchronization_peers_blocks(&config, Arc::new(PeersImpl::default()), &mut peers).0;
diff --git a/sync/src/synchronization_peers_tasks.rs b/sync/src/synchronization_peers_tasks.rs
index 7d026d1d0..67083191d 100644
--- a/sync/src/synchronization_peers_tasks.rs
+++ b/sync/src/synchronization_peers_tasks.rs
@@ -154,6 +154,12 @@ impl PeersTasks {
 		self.stats.get(&peer_index)
 	}
 
+	/// Get mutable reference to peer statistics
+	#[cfg(test)]
+	pub fn get_peer_stats_mut(&mut self, peer_index: PeerIndex) -> Option<&mut PeerStats> {
+		self.stats.get_mut(&peer_index)
+	}
+
 	/// Mark peer as useful.
 	pub fn useful_peer(&mut self, peer_index: PeerIndex) {
 		// if peer is unknown => insert to idle queue
@@ -304,12 +310,7 @@ impl PeersTasks {
 
 	/// We have failed to get block from peer during given period
 	pub fn on_peer_block_failure(&mut self, peer_index: PeerIndex) -> bool {
-		self.stats.get_mut(&peer_index)
-			.map(|s| {
-				s.failures += 1;
-				s.failures > MAX_PEER_FAILURES
-			})
-			.unwrap_or_default()
+		self.penalize(peer_index)
 	}
 
 	/// We have failed to get headers from peer during given period
@@ -318,10 +319,20 @@ impl PeersTasks {
 		self.headers_requests.remove(&peer_index);
 		self.idle_for_headers.insert(peer_index);
 
+		self.penalize(peer_index)
+	}
+
+	/// Penalize peer. Returns true if the peer score is too low to keep connection.
+	pub fn penalize(&mut self, peer_index: PeerIndex) -> bool {
 		self.stats.get_mut(&peer_index)
 			.map(|s| {
-				s.failures += 1;
-				s.failures > MAX_PEER_FAILURES
+				if s.trust == TrustLevel::Trusted {
+					s.failures += 1;
+					s.failures > MAX_PEER_FAILURES
+				} else {
+					s.failures = MAX_PEER_FAILURES;
+					true
+				}
 			})
 			.unwrap_or_default()
 	}
@@ -374,6 +385,11 @@ impl PeerStats {
 	pub fn trust(&self) -> TrustLevel {
 		self.trust
 	}
+
+	#[cfg(test)]
+	pub fn set_trust(&mut self, trust: TrustLevel) {
+		self.trust = trust;
+	}
 }
 
 impl fmt::Debug for Information {
diff --git a/verification/src/accept_chain.rs b/verification/src/accept_chain.rs
index a366deb57..49b197b1d 100644
--- a/verification/src/accept_chain.rs
+++ b/verification/src/accept_chain.rs
@@ -1,5 +1,8 @@
 use rayon::prelude::{IntoParallelRefIterator, IndexedParallelIterator, ParallelIterator};
-use storage::{DuplexTransactionOutputProvider, Store};
+use storage::{
+	DuplexTransactionOutputProvider, TransactionOutputProvider, TransactionMetaProvider,
+	BlockHeaderProvider,
+};
 use network::ConsensusParams;
 use error::Error;
 use canon::CanonBlock;
@@ -16,19 +19,36 @@ pub struct ChainAcceptor<'a> {
 }
 
 impl<'a> ChainAcceptor<'a> {
-	pub fn new(store: &'a Store, consensus: &'a ConsensusParams, verification_level: VerificationLevel, block: CanonBlock<'a>, height: u32, median_time_past: u32, deployments: &'a BlockDeployments) -> Self {
+	pub fn new(
+		tx_out_provider: &'a TransactionOutputProvider,
+		tx_meta_provider: &'a TransactionMetaProvider,
+		header_provider: &'a BlockHeaderProvider,
+		consensus: &'a ConsensusParams,
+		verification_level: VerificationLevel,
+		block: CanonBlock<'a>,
+		height: u32,
+		median_time_past: u32,
+		deployments: &'a BlockDeployments,
+	) -> Self {
 		trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
-		let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
-		let headers = store.as_block_header_provider();
+		let output_store = DuplexTransactionOutputProvider::new(tx_out_provider, block.raw());
 
 		ChainAcceptor {
-			block: BlockAcceptor::new(store.as_transaction_output_provider(), consensus, block, height, median_time_past, deployments, headers),
-			header: HeaderAcceptor::new(headers, consensus, block.header(), height, deployments),
+			block: BlockAcceptor::new(
+				tx_out_provider,
+				consensus,
+				block,
+				height,
+				median_time_past,
+				deployments,
+				header_provider,
+			),
+			header: HeaderAcceptor::new(header_provider, consensus, block.header(), height, deployments),
 			transactions: block.transactions()
 				.into_iter()
 				.enumerate()
 				.map(|(tx_index, tx)| TransactionAcceptor::new(
-						store.as_transaction_meta_provider(),
+						tx_meta_provider,
 						output_store,
 						consensus,
 						tx,
diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs
index bcda742c8..aed3e79c6 100644
--- a/verification/src/accept_transaction.rs
+++ b/verification/src/accept_transaction.rs
@@ -441,6 +441,10 @@ impl<'a> TransactionDoubleSpend<'a> {
 	}
 
 	fn check(&self) -> Result<(), TransactionError> {
+		if self.transaction.raw.is_coinbase() {
+			return Ok(());
+		}
+
 		for input in &self.transaction.raw.inputs {
 			if self.store.is_spent(&input.previous_output) {
 				return Err(TransactionError::UsingSpentOutput(
diff --git a/verification/src/chain_verifier.rs b/verification/src/chain_verifier.rs
index 2a9a5bfd7..a48609e86 100644
--- a/verification/src/chain_verifier.rs
+++ b/verification/src/chain_verifier.rs
@@ -3,7 +3,7 @@
 use hash::H256;
 use chain::{IndexedBlock, IndexedBlockHeader, BlockHeader, Transaction};
 use storage::{SharedStore, TransactionOutputProvider, BlockHeaderProvider, BlockOrigin,
-	DuplexTransactionOutputProvider, NoopStore};
+	DuplexTransactionOutputProvider, NoopStore, CachedTransactionOutputProvider};
 use network::ConsensusParams;
 use error::{Error, TransactionError};
 use canon::{CanonBlock, CanonTransaction};
@@ -43,43 +43,80 @@ impl BackwardsCompatibleChainVerifier {
 
 		assert_eq!(Some(self.store.best_block().hash), self.store.block_hash(self.store.best_block().number));
 		let block_origin = self.store.block_origin(&block.header)?;
-		trace!(target: "verification", "verify_block: {:?} best_block: {:?} block_origin: {:?}", block.hash().reversed(), self.store.best_block(), block_origin);
+		trace!(
+			target: "verification",
+			"verify_block: {:?} best_block: {:?} block_origin: {:?}",
+			block.hash().reversed(),
+			self.store.best_block(),
+			block_origin,
+		);
 
 		let median_time_past = median_timestamp_inclusive(block.header.raw.previous_header_hash.clone(), self.store.as_block_header_provider());
+		let canon_block = CanonBlock::new(block);
 		match block_origin {
 			BlockOrigin::KnownBlock => {
 				// there should be no known blocks at this point
 				unreachable!();
 			},
 			BlockOrigin::CanonChain { block_number } => {
+				let tx_out_provider = CachedTransactionOutputProvider::new(self.store.as_store().as_transaction_output_provider());
+				let tx_meta_provider = self.store.as_store().as_transaction_meta_provider();
 				let header_provider = self.store.as_store().as_block_header_provider();
 				let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
-				let canon_block = CanonBlock::new(block);
-				let chain_acceptor = ChainAcceptor::new(self.store.as_store(), &self.consensus, verification_level,
-					canon_block, block_number, median_time_past, &deployments);
+				let chain_acceptor = ChainAcceptor::new(
+					&tx_out_provider,
+					tx_meta_provider,
+					header_provider,
+					&self.consensus,
+					verification_level,
+					canon_block,
+					block_number,
+					median_time_past,
+					&deployments,
+				);
 				chain_acceptor.check()?;
 			},
 			BlockOrigin::SideChain(origin) => {
 				let block_number = origin.block_number;
-				let header_provider = self.store.as_store().as_block_header_provider();
-				let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
 				let fork = self.store.fork(origin)?;
-				let canon_block = CanonBlock::new(block);
-				let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
-					block_number, median_time_past, &deployments);
+				let tx_out_provider = CachedTransactionOutputProvider::new(fork.store().as_transaction_output_provider());
+				let tx_meta_provider = fork.store().as_transaction_meta_provider();
+				let header_provider = fork.store().as_block_header_provider();
+				let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
+				let chain_acceptor = ChainAcceptor::new(
+					&tx_out_provider,
+					tx_meta_provider,
+					header_provider,
+					&self.consensus,
+					verification_level,
+					canon_block,
+					block_number,
+					median_time_past,
+					&deployments,
+				);
 				chain_acceptor.check()?;
 			},
 			BlockOrigin::SideChainBecomingCanonChain(origin) => {
 				let block_number = origin.block_number;
-				let header_provider = self.store.as_store().as_block_header_provider();
-				let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
 				let fork = self.store.fork(origin)?;
-				let canon_block = CanonBlock::new(block);
-				let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
-					block_number, median_time_past, &deployments);
+				let tx_out_provider = CachedTransactionOutputProvider::new(fork.store().as_transaction_output_provider());
+				let tx_meta_provider = fork.store().as_transaction_meta_provider();
+				let header_provider = fork.store().as_block_header_provider();
+				let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
+				let chain_acceptor = ChainAcceptor::new(
+					&tx_out_provider,
+					tx_meta_provider,
+					header_provider,
+					&self.consensus,
+					verification_level,
+					canon_block,
+					block_number,
+					median_time_past,
+					&deployments,
+				);
 				chain_acceptor.check()?;
 			},
-		}
+		};
 
 		assert_eq!(Some(self.store.best_block().hash), self.store.block_hash(self.store.best_block().number));
 		Ok(())


Test patch: