/// Block and transaction verification functions /// /// Block verification is done in 3 steps /// 1. Quick verification upon adding to the block queue /// 2. Signatures verification done in the queue. /// 3. Final verification against the blockchain done before enactment. use common::*; use engine::Engine; use blockchain::*; /// Preprocessed block data gathered in `verify_block_unordered` call pub struct PreVerifiedBlock { /// Populated block header pub header: Header, /// Populated block transactions pub transactions: Vec, /// Block bytes pub bytes: Bytes, } /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block 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))); for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { try!(verify_header(&u, engine)); try!(engine.verify_block_basic(&u, None)); } // Verify transactions. // TODO: either use transaction views or cache the decoded transactions. let v = BlockView::new(bytes); for t in v.transactions() { try!(engine.verify_transaction_basic(&t, &header)); } Ok(()) } /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Still operates on a individual block /// Returns a PreVerifiedBlock structure populated with transactions pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result { try!(engine.verify_block_unordered(&header, Some(&bytes))); for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { try!(engine.verify_block_unordered(&u, None)); } // Verify transactions. let mut transactions = Vec::new(); { let v = BlockView::new(&bytes); for t in v.transactions() { try!(engine.verify_transaction(&t, &header)); transactions.push(t); } } Ok(PreVerifiedBlock { header: header, transactions: transactions, bytes: bytes, }) } /// Phase 3 verification. Check block information against parent and uncles. pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { // TODO: verify timestamp let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone())))); try!(verify_parent(&header, &parent)); try!(engine.verify_block_family(&header, &parent, Some(bytes))); let num_uncles = Rlp::new(bytes).at(2).item_count(); if num_uncles != 0 { if num_uncles > engine.maximum_uncle_count() { return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles }))); } let mut excluded = HashSet::new(); excluded.insert(header.hash()); let mut hash = header.parent_hash.clone(); excluded.insert(hash.clone()); for _ in 0..6 { match bc.block_details(&hash) { Some(details) => { excluded.insert(details.parent.clone()); let b = bc.block(&hash).unwrap(); excluded.extend(BlockView::new(&b).uncle_hashes()); hash = details.parent; } None => break } } for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { if excluded.contains(&uncle.hash()) { return Err(From::from(BlockError::UncleInChain(uncle.hash()))) } // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() // 1 2 // 2 // 3 // 4 // 5 // 6 7 // (8 Invalid) let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 }; if depth > 6 { return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number }))); } else if depth < 1 { return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number }))); } // cB // cB.p^1 1 depth, valid uncle // cB.p^2 ---/ 2 // cB.p^3 -----/ 3 // cB.p^4 -------/ 4 // cB.p^5 ---------/ 5 // cB.p^6 -----------/ 6 // cB.p^7 -------------/ // cB.p^8 let mut expected_uncle_parent = header.parent_hash.clone(); let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone())))); for _ in 0..depth { match bc.block_details(&expected_uncle_parent) { Some(details) => { expected_uncle_parent = details.parent; }, None => break } } if expected_uncle_parent != uncle_parent.hash() { return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); } try!(verify_parent(&uncle, &uncle_parent)); try!(engine.verify_block_family(&uncle, &uncle_parent, Some(bytes))); } } Ok(()) } /// Phase 4 verification. Check block information against transaction enactment results, pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> { if expected.gas_used != got.gas_used { return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used, found: got.gas_used }))) } if expected.receipts_root != got.receipts_root { return Err(From::from(BlockError::InvalidReceiptsStateRoot(Mismatch { expected: expected.receipts_root.clone(), found: got.receipts_root.clone() }))) } if expected.state_root != got.state_root { return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root.clone(), found: got.state_root.clone() }))) } if expected.log_bloom != got.log_bloom { return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom.clone(), found: got.log_bloom.clone() }))) } Ok(()) } /// Check basic header parameters. fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> { if header.number >= From::from(BlockNumber::max_value()) { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number }))) } if header.gas_used > header.gas_limit { return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }))); } let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap()); if header.gas_limit < min_gas_limit { return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }))); } let maximum_extra_data_size = engine.maximum_extra_data_size(); if header.number != 0 && header.extra_data.len() > maximum_extra_data_size { return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() }))); } Ok(()) } /// Check header parameters agains parent header. fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> { if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash { return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() }))) } if header.timestamp <= parent.timestamp { return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }))) } if header.number != parent.number + 1 { return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }))); } Ok(()) } /// Verify block data against header: transactions root and uncles hash. fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> { let block = Rlp::new(block); let tx = block.at(1); let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here if expected_root != transactions_root { return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() }))) } let expected_uncles = &block.at(2).as_raw().sha3(); if expected_uncles != uncles_hash { return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() }))) } Ok(()) } #[cfg(test)] mod tests { use util::*; use header::*; use verification::*; use extras::*; use error::*; use error::BlockError::*; use views::*; use blockchain::*; use engine::*; use spec::*; use transaction::*; use basic_types::*; use tests::helpers::*; fn check_ok(result: Result<(), Error>) { result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); } fn check_fail(result: Result<(), Error>, e: BlockError) { match result { Err(Error::Block(ref error)) if *error == e => (), Err(other) => panic!("Block verification failed.\nExpected: {:?}\nGot: {:?}", e, other), Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e), } } struct TestBlockChain { blocks: HashMap, numbers: HashMap, } impl TestBlockChain { pub fn new() -> TestBlockChain { TestBlockChain { blocks: HashMap::new(), numbers: HashMap::new(), } } pub fn insert(&mut self, bytes: Bytes) { let number = BlockView::new(&bytes).header_view().number(); let hash = BlockView::new(&bytes).header_view().sha3(); self.blocks.insert(hash.clone(), bytes); self.numbers.insert(number, hash.clone()); } } impl BlockProvider for TestBlockChain { fn is_known(&self, hash: &H256) -> bool { self.blocks.contains_key(hash) } /// Get raw block data fn block(&self, hash: &H256) -> Option { self.blocks.get(hash).cloned() } /// Get the familial details concerning a block. fn block_details(&self, hash: &H256) -> Option { self.blocks.get(hash).map(|bytes| { let header = BlockView::new(bytes).header(); BlockDetails { number: header.number, total_difficulty: header.difficulty, parent: header.parent_hash, children: Vec::new(), } }) } /// Get the hash of given block's number. fn block_hash(&self, index: BlockNumber) -> Option { self.numbers.get(&index).cloned() } } fn basic_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> { let header = BlockView::new(bytes).header(); verify_block_basic(&header, bytes, engine) } fn family_test(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 let mut good = Header::new(); let spec = Spec::new_test(); let engine = spec.to_engine().unwrap(); let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap()); let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap()); good.gas_limit = min_gas_limit; good.difficulty = min_difficulty; good.timestamp = 40; good.number = 10; let tr1 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(1)); let tr2 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(2)); let good_transactions = [ &tr1, &tr2 ]; let diff_inc = U256::from(0x40); let mut parent6 = good.clone(); parent6.number = 6; let mut parent7 = good.clone(); parent7.number = 7; parent7.parent_hash = parent6.hash(); parent7.difficulty = parent6.difficulty + diff_inc; parent7.timestamp = parent6.timestamp + 10; let mut parent8 = good.clone(); parent8.number = 8; parent8.parent_hash = parent7.hash(); parent8.difficulty = parent7.difficulty + diff_inc; parent8.timestamp = parent7.timestamp + 10; let mut good_uncle1 = good.clone(); good_uncle1.number = 9; good_uncle1.parent_hash = parent8.hash(); good_uncle1.difficulty = parent8.difficulty + diff_inc; good_uncle1.timestamp = parent8.timestamp + 10; good_uncle1.extra_data.push(1u8); let mut good_uncle2 = good.clone(); good_uncle2.number = 8; good_uncle2.parent_hash = parent7.hash(); good_uncle2.difficulty = parent7.difficulty + diff_inc; good_uncle2.timestamp = parent7.timestamp + 10; good_uncle2.extra_data.push(2u8); let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ]; let mut uncles_rlp = RlpStream::new(); uncles_rlp.append(&good_uncles); let good_uncles_hash = uncles_rlp.as_raw().sha3(); let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| t.rlp_bytes_opt(Seal::With)).collect()); let mut parent = good.clone(); parent.number = 9; parent.timestamp = parent8.timestamp + 10; parent.parent_hash = parent8.hash(); parent.difficulty = parent8.difficulty + diff_inc; good.parent_hash = parent.hash(); good.difficulty = parent.difficulty + diff_inc; good.timestamp = parent.timestamp + 10; let mut bc = TestBlockChain::new(); bc.insert(create_test_block(&good)); bc.insert(create_test_block(&parent)); bc.insert(create_test_block(&parent6)); bc.insert(create_test_block(&parent7)); bc.insert(create_test_block(&parent8)); 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(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(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(basic_test(&create_test_block(&header), engine.deref()), RidiculousNumber(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(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(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(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(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(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(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(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(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(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })); header = good.clone(); let mut bad_uncles = good_uncles.clone(); bad_uncles.push(good_uncle1.clone()); 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 } }