Bad block reporting

This commit is contained in:
arkpar 2016-01-15 12:26:04 +01:00
parent e911e3d8a6
commit 0b511a180a
5 changed files with 58 additions and 32 deletions

View File

@ -128,15 +128,16 @@ impl Client {
pub fn import_verified_block(&mut self, bytes: Bytes) { pub fn import_verified_block(&mut self, bytes: Bytes) {
let block = BlockView::new(&bytes); let block = BlockView::new(&bytes);
let header = block.header(); let header = block.header();
if let Err(e) = verify_block_family(&bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) { if let Err(e) = verify_block_family(&header, &bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
// TODO: mark as bad self.queue.mark_as_bad(&header.hash());
return; return;
}; };
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) { let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
Some(p) => p, Some(p) => p,
None => { None => {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash); warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
self.queue.mark_as_bad(&header.hash());
return; return;
}, },
}; };
@ -158,11 +159,13 @@ impl Client {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.queue.mark_as_bad(&header.hash());
return; return;
} }
}; };
if let Err(e) = verify_block_final(&header, result.block().header()) { if let Err(e) = verify_block_final(&header, result.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.queue.mark_as_bad(&header.hash());
return; return;
} }

View File

@ -68,14 +68,14 @@ pub enum BlockError {
#[derive(Debug)] #[derive(Debug)]
pub enum ImportError { pub enum ImportError {
Bad(Error), Bad(Option<Error>),
AlreadyInChain, AlreadyInChain,
AlreadyQueued, AlreadyQueued,
} }
impl From<Error> for ImportError { impl From<Error> for ImportError {
fn from(err: Error) -> ImportError { fn from(err: Error) -> ImportError {
ImportError::Bad(err) ImportError::Bad(Some(err))
} }
} }

View File

@ -9,7 +9,8 @@ use views::*;
/// Sorts them ready for blockchain insertion. /// Sorts them ready for blockchain insertion.
pub struct BlockQueue { pub struct BlockQueue {
engine: Arc<Box<Engine>>, engine: Arc<Box<Engine>>,
message_channel: IoChannel<NetSyncMessage> message_channel: IoChannel<NetSyncMessage>,
bad: HashSet<H256>,
} }
impl BlockQueue { impl BlockQueue {
@ -17,7 +18,8 @@ impl BlockQueue {
pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue { pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
BlockQueue { BlockQueue {
engine: engine, engine: engine,
message_channel: message_channel message_channel: message_channel,
bad: HashSet::new(),
} }
} }
@ -27,16 +29,31 @@ impl BlockQueue {
/// Add a block to the queue. /// Add a block to the queue.
pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult { pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
try!(verify_block_basic(bytes, self.engine.deref().deref()).map_err(|e| { let header = BlockView::new(bytes).header();
if self.bad.contains(&header.hash()) {
return Err(ImportError::Bad(None));
}
if self.bad.contains(&header.parent_hash) {
self.bad.insert(header.hash());
return Err(ImportError::Bad(None));
}
try!(verify_block_basic(&header, bytes, self.engine.deref().deref()).map_err(|e| {
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e); warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
e e
})); }));
try!(verify_block_unordered(bytes, self.engine.deref().deref()).map_err(|e| { try!(verify_block_unordered(&header, bytes, self.engine.deref().deref()).map_err(|e| {
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e); warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), e);
e e
})); }));
try!(self.message_channel.send(UserMessage(SyncMessage::BlockVerified(bytes.to_vec()))).map_err(|e| Error::from(e))); try!(self.message_channel.send(UserMessage(SyncMessage::BlockVerified(bytes.to_vec()))).map_err(|e| Error::from(e)));
Ok(()) Ok(())
} }
pub fn mark_as_bad(&mut self, hash: &H256) {
self.bad.insert(hash.clone());
//TODO: walk the queue
}
} }

View File

@ -473,6 +473,7 @@ impl ChainSync {
if self.peers.contains_key(&peer) { if self.peers.contains_key(&peer) {
info!(target: "sync", "Disconneced {}:{}", peer, io.peer_info(peer)); info!(target: "sync", "Disconneced {}:{}", peer, io.peer_info(peer));
self.clear_peer_download(peer); self.clear_peer_download(peer);
self.peers.remove(&peer);
self.continue_sync(io); self.continue_sync(io);
} }
} }

View File

@ -10,9 +10,7 @@ use engine::Engine;
use blockchain::*; use blockchain::*;
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(bytes: &[u8], engine: &Engine) -> Result<(), Error> { pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
let block = BlockView::new(bytes);
let header = block.header();
try!(verify_header(&header, engine)); try!(verify_header(&header, engine));
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash)); try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
try!(engine.verify_block_basic(&header, Some(bytes))); try!(engine.verify_block_basic(&header, Some(bytes)));
@ -26,9 +24,7 @@ pub fn verify_block_basic(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block /// Still operates on a individual block
/// TODO: return cached transactions, header hash. /// TODO: return cached transactions, header hash.
pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error> { pub fn verify_block_unordered(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
let block = BlockView::new(bytes);
let header = block.header();
try!(engine.verify_block_unordered(&header, Some(bytes))); try!(engine.verify_block_unordered(&header, Some(bytes)));
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) { for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&u, None)); try!(engine.verify_block_unordered(&u, None));
@ -37,10 +33,8 @@ pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error
} }
/// Phase 3 verification. Check block information against parent and uncles. /// Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
// TODO: verify timestamp // TODO: verify timestamp
let block = BlockView::new(bytes);
let header = block.header();
let parent = try!(bc.block_header(&header.parent_hash).ok_or::<Error>(From::from(BlockError::UnknownParent(header.parent_hash.clone())))); let parent = try!(bc.block_header(&header.parent_hash).ok_or::<Error>(From::from(BlockError::UnknownParent(header.parent_hash.clone()))));
try!(verify_parent(&header, &parent)); try!(verify_parent(&header, &parent));
try!(engine.verify_block_family(&header, &parent, Some(bytes))); try!(engine.verify_block_family(&header, &parent, Some(bytes)));
@ -194,6 +188,7 @@ mod tests {
use error::BlockError::*; use error::BlockError::*;
use views::*; use views::*;
use blockchain::*; use blockchain::*;
use engine::*;
use ethereum; use ethereum;
fn create_test_block(header: &Header) -> Bytes { fn create_test_block(header: &Header) -> Bytes {
@ -280,6 +275,16 @@ mod tests {
} }
} }
fn basic_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
let header = BlockView::new(bytes).header();
verify_block_basic(&header, bytes, engine)
}
fn family_test<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
let header = BlockView::new(bytes).header();
verify_block_family(&header, bytes, engine, bc)
}
#[test] #[test]
fn test_verify_block() { fn test_verify_block() {
// Test against morden // Test against morden
@ -348,69 +353,69 @@ mod tests {
bc.insert(create_test_block(&parent7)); bc.insert(create_test_block(&parent7));
bc.insert(create_test_block(&parent8)); bc.insert(create_test_block(&parent8));
check_ok(verify_block_basic(&create_test_block(&good), engine.deref())); check_ok(basic_test(&create_test_block(&good), engine.deref()));
let mut header = good.clone(); let mut header = good.clone();
header.transactions_root = good_transactions_root.clone(); header.transactions_root = good_transactions_root.clone();
header.uncles_hash = good_uncles_hash.clone(); header.uncles_hash = good_uncles_hash.clone();
check_ok(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref())); check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()));
header.gas_limit = min_gas_limit - From::from(1); header.gas_limit = min_gas_limit - From::from(1);
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), check_fail(basic_test(&create_test_block(&header), engine.deref()),
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })); InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
header = good.clone(); header = good.clone();
header.number = BlockNumber::max_value(); header.number = BlockNumber::max_value();
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), check_fail(basic_test(&create_test_block(&header), engine.deref()),
InvalidNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number })); InvalidNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
header = good.clone(); header = good.clone();
header.gas_used = header.gas_limit + From::from(1); header.gas_used = header.gas_limit + From::from(1);
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), check_fail(basic_test(&create_test_block(&header), engine.deref()),
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })); TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
header = good.clone(); header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8); header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), check_fail(basic_test(&create_test_block(&header), engine.deref()),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() })); ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
header = good.clone(); header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8); header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), check_fail(basic_test(&create_test_block(&header), engine.deref()),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() })); ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
header = good.clone(); header = good.clone();
header.uncles_hash = good_uncles_hash.clone(); header.uncles_hash = good_uncles_hash.clone();
check_fail(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()), check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root })); InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
header = good.clone(); header = good.clone();
header.transactions_root = good_transactions_root.clone(); header.transactions_root = good_transactions_root.clone();
check_fail(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()), check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash })); InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
check_ok(verify_block_family(&create_test_block(&good), engine.deref(), &bc)); check_ok(family_test(&create_test_block(&good), engine.deref(), &bc));
check_ok(verify_block_family(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc)); check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc));
header = good.clone(); header = good.clone();
header.parent_hash = H256::random(); header.parent_hash = H256::random();
check_fail(verify_block_family(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
UnknownParent(header.parent_hash)); UnknownParent(header.parent_hash));
header = good.clone(); header = good.clone();
header.timestamp = 10; header.timestamp = 10;
check_fail(verify_block_family(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })); InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
header = good.clone(); header = good.clone();
header.number = 9; header.number = 9;
check_fail(verify_block_family(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number })); InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number }));
header = good.clone(); header = good.clone();
let mut bad_uncles = good_uncles.clone(); let mut bad_uncles = good_uncles.clone();
bad_uncles.push(good_uncle1.clone()); bad_uncles.push(good_uncle1.clone());
check_fail(verify_block_family(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine.deref(), &bc), check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine.deref(), &bc),
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() })); TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
// TODO: some additional uncle checks // TODO: some additional uncle checks