|
|
|
|
@@ -10,9 +10,7 @@ use engine::Engine;
|
|
|
|
|
use blockchain::*;
|
|
|
|
|
|
|
|
|
|
/// 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> {
|
|
|
|
|
let block = BlockView::new(bytes);
|
|
|
|
|
let header = block.header();
|
|
|
|
|
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
|
|
|
|
try!(verify_header(&header, engine));
|
|
|
|
|
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
|
|
|
|
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.
|
|
|
|
|
/// Still operates on a individual block
|
|
|
|
|
/// TODO: return cached transactions, header hash.
|
|
|
|
|
pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
|
|
|
|
let block = BlockView::new(bytes);
|
|
|
|
|
let header = block.header();
|
|
|
|
|
pub fn verify_block_unordered(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
|
|
|
|
try!(engine.verify_block_unordered(&header, Some(bytes)));
|
|
|
|
|
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
|
|
|
|
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.
|
|
|
|
|
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
|
|
|
|
|
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()))));
|
|
|
|
|
try!(verify_parent(&header, &parent));
|
|
|
|
|
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
|
|
|
|
@@ -194,6 +188,7 @@ mod tests {
|
|
|
|
|
use error::BlockError::*;
|
|
|
|
|
use views::*;
|
|
|
|
|
use blockchain::*;
|
|
|
|
|
use engine::*;
|
|
|
|
|
use ethereum;
|
|
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
fn test_verify_block() {
|
|
|
|
|
// Test against morden
|
|
|
|
|
@@ -348,69 +353,69 @@ mod tests {
|
|
|
|
|
bc.insert(create_test_block(&parent7));
|
|
|
|
|
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();
|
|
|
|
|
header.transactions_root = good_transactions_root.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);
|
|
|
|
|
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 }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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 }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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 }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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() }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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() }));
|
|
|
|
|
|
|
|
|
|
header = good.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 }));
|
|
|
|
|
|
|
|
|
|
header = good.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 }));
|
|
|
|
|
|
|
|
|
|
check_ok(verify_block_family(&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(&good), engine.deref(), &bc));
|
|
|
|
|
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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 }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
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 }));
|
|
|
|
|
|
|
|
|
|
header = good.clone();
|
|
|
|
|
let mut bad_uncles = good_uncles.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() }));
|
|
|
|
|
|
|
|
|
|
// TODO: some additional uncle checks
|
|
|
|
|
|