openethereum/crates/ethcore/src/verification/verification.rs

1154 lines
39 KiB
Rust

// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! 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 std::{
collections::HashSet,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use bytes::Bytes;
use hash::keccak;
use parity_util_mem::MallocSizeOf;
use rlp::Rlp;
use triehash::ordered_trie_root;
use unexpected::{Mismatch, OutOfBounds};
use blockchain::*;
use call_contract::CallContract;
use client::BlockInfo;
use engines::{EthEngine, MAX_UNCLE_AGE};
use error::{BlockError, Error};
use types::{header::Header, transaction::SignedTransaction, BlockNumber};
use verification::queue::kind::blocks::Unverified;
use time_utils::CheckedSystemTime;
/// Preprocessed block data gathered in `verify_block_unordered` call
#[derive(MallocSizeOf)]
pub struct PreverifiedBlock {
/// Populated block header
pub header: Header,
/// Populated block transactions
pub transactions: Vec<SignedTransaction>,
/// Populated block uncles
pub uncles: Vec<Header>,
/// Block bytes
pub bytes: Bytes,
}
/// t_nb 4.0 Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(
block: &Unverified,
engine: &dyn EthEngine,
check_seal: bool,
) -> Result<(), Error> {
// t_nb 4.1 verify header params
verify_header_params(&block.header, engine, true, check_seal)?;
// t_nb 4.2 verify header time (addded in new OE version)
// t_nb 4.3 verify block integrity
verify_block_integrity(block)?;
if check_seal {
// t_nb 4.4 Check block seal. It calls engine to verify block basic
engine.verify_block_basic(&block.header)?;
}
// t_nb 4.5 for all uncled verify header and call engine to verify block basic
for uncle in &block.uncles {
// t_nb 4.5.1
verify_header_params(uncle, engine, false, check_seal)?;
if check_seal {
// t_nb 4.5.2
engine.verify_block_basic(uncle)?;
}
}
// t_nb 4.6 call engine.gas_limit_override (Used only by Aura)
if let Some(gas_limit) = engine.gas_limit_override(&block.header) {
if *block.header.gas_limit() != gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(gas_limit),
max: Some(gas_limit),
found: *block.header.gas_limit(),
})));
}
}
// t_nb 4.7 for every transaction call engine.verify_transaction_basic
for t in &block.transactions {
engine.verify_transaction_basic(t, &block.header)?;
}
Ok(())
}
// t_nb 5.0 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(
block: Unverified,
engine: &dyn EthEngine,
check_seal: bool,
) -> Result<PreverifiedBlock, Error> {
let header = block.header;
if check_seal {
// t_nb 5.1
engine.verify_block_unordered(&header)?;
for uncle in &block.uncles {
// t_nb 5.2
engine.verify_block_unordered(uncle)?;
}
}
// Verify transactions.
let nonce_cap = if header.number() >= engine.params().dust_protection_transition {
Some((engine.params().nonce_cap_increment * header.number()).into())
} else {
None
};
// t_nb 5.3 iterate over all transactions
let transactions = block
.transactions
.into_iter()
.map(|t| {
// t_nb 5.3.1 call verify_unordered. Check signatures and calculate address
let t = engine.verify_transaction_unordered(t, &header)?;
// t_nb 5.3.2 check if nonce is more then max nonce (EIP-168 and EIP169)
if let Some(max_nonce) = nonce_cap {
if t.tx().nonce >= max_nonce {
return Err(BlockError::TooManyTransactions(t.sender()).into());
}
}
Ok(t)
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(PreverifiedBlock {
header,
transactions,
uncles: block.uncles,
bytes: block.bytes,
})
}
/// Parameters for full verification of block family
pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> {
/// Preverified block
pub block: &'a PreverifiedBlock,
/// Block provider to use during verification
pub block_provider: &'a dyn BlockProvider,
/// Engine client to use during verification
pub client: &'a C,
}
/// t_nb 6.3 Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family<C: BlockInfo + CallContract>(
header: &Header,
parent: &Header,
engine: &dyn EthEngine,
do_full: Option<FullFamilyParams<C>>,
) -> Result<(), Error> {
// TODO: verify timestamp
// t_nb 6.3.1 verify parent
verify_parent(&header, &parent, engine)?;
engine.verify_block_family(&header, &parent)?;
let params = match do_full {
Some(x) => x,
None => return Ok(()),
};
// t_nb 6.3.2 verify uncles
verify_uncles(params.block, params.block_provider, engine)?;
// t_nb 6.3.3 verify all transactions
for tx in &params.block.transactions {
// transactions are verified against the parent header since the current
// state wasn't available when the tx was created
engine
.machine()
.verify_transaction(tx, parent, params.client)?;
}
Ok(())
}
fn verify_uncles(
block: &PreverifiedBlock,
bc: &dyn BlockProvider,
engine: &dyn EthEngine,
) -> Result<(), Error> {
let header = &block.header;
let num_uncles = block.uncles.len();
let max_uncles = engine.maximum_uncle_count(header.number());
if num_uncles != 0 {
if num_uncles > max_uncles {
return Err(From::from(BlockError::TooManyUncles(OutOfBounds {
min: None,
max: Some(max_uncles),
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..MAX_UNCLE_AGE {
match bc.block_details(&hash) {
Some(details) => {
excluded.insert(details.parent);
let b = bc
.block(&hash)
.expect("parent already known to be stored; qed");
excluded.extend(b.uncle_hashes());
hash = details.parent;
}
None => break,
}
}
let mut verified = HashSet::new();
for uncle in &block.uncles {
if excluded.contains(&uncle.hash()) {
return Err(From::from(BlockError::UncleInChain(uncle.hash())));
}
if verified.contains(&uncle.hash()) {
return Err(From::from(BlockError::DuplicateUncle(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 > MAX_UNCLE_AGE as u64 {
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 = bc.block_header_data(&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(),
)));
}
let uncle_parent = uncle_parent.decode()?;
verify_parent(&uncle, &uncle_parent, engine)?;
engine.verify_block_family(&uncle, &uncle_parent)?;
verified.insert(uncle.hash());
}
}
Ok(())
}
/// Phase 4 verification. Check block information against transaction enactment results,
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
if expected.state_root() != got.state_root() {
return Err(From::from(BlockError::InvalidStateRoot(Mismatch {
expected: *expected.state_root(),
found: *got.state_root(),
})));
}
if expected.gas_used() != got.gas_used() {
return Err(From::from(BlockError::InvalidGasUsed(Mismatch {
expected: *expected.gas_used(),
found: *got.gas_used(),
})));
}
if expected.log_bloom() != got.log_bloom() {
return Err(From::from(BlockError::InvalidLogBloom(Box::new(
Mismatch {
expected: *expected.log_bloom(),
found: *got.log_bloom(),
},
))));
}
if expected.receipts_root() != got.receipts_root() {
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch {
expected: *expected.receipts_root(),
found: *got.receipts_root(),
})));
}
Ok(())
}
/// Check basic header parameters.
pub fn verify_header_params(
header: &Header,
engine: &dyn EthEngine,
is_full: bool,
check_seal: bool,
) -> Result<(), Error> {
if check_seal {
let expected_seal_fields = engine.seal_fields(header);
if header.seal().len() != expected_seal_fields {
return Err(From::from(BlockError::InvalidSealArity(Mismatch {
expected: expected_seal_fields,
found: header.seal().len(),
})));
}
}
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(),
})));
}
if engine.gas_limit_override(header).is_none() {
let min_gas_limit = engine.min_gas_limit();
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(),
})));
}
if let Some(limit) = engine.maximum_gas_limit() {
if header.gas_limit() > &limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: None,
max: Some(limit),
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(),
})));
}
if let Some(ref ext) = engine.machine().ethash_extensions() {
if header.number() >= ext.dao_hardfork_transition
&& header.number() <= ext.dao_hardfork_transition + 9
&& header.extra_data()[..] != b"dao-hard-fork"[..]
{
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds {
min: None,
max: None,
found: 0,
})));
}
}
if is_full {
const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15);
// this will resist overflow until `year 2037`
let max_time = SystemTime::now() + ACCEPTABLE_DRIFT;
let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9;
let timestamp =
CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp()))
.ok_or(BlockError::TimestampOverflow)?;
if timestamp > invalid_threshold {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds {
max: Some(max_time),
min: None,
found: timestamp,
})));
}
if timestamp > max_time {
return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds {
max: Some(max_time),
min: None,
found: timestamp,
})));
}
}
Ok(())
}
/// Check header parameters agains parent header.
fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Result<(), Error> {
assert!(
header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(),
"Parent hash should already have been verified; qed"
);
if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) {
let now = SystemTime::now();
let min = CheckedSystemTime::checked_add(
now,
Duration::from_secs(parent.timestamp().saturating_add(1)),
)
.ok_or(BlockError::TimestampOverflow)?;
let found = CheckedSystemTime::checked_add(now, Duration::from_secs(header.timestamp()))
.ok_or(BlockError::TimestampOverflow)?;
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds {
max: None,
min: Some(min),
found,
})));
}
if header.number() != parent.number() + 1 {
return Err(From::from(BlockError::InvalidNumber(Mismatch {
expected: parent.number() + 1,
found: header.number(),
})));
}
if header.number() == 0 {
return Err(BlockError::RidiculousNumber(OutOfBounds {
min: Some(1),
max: None,
found: header.number(),
})
.into());
}
if engine.gas_limit_override(header).is_none() {
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
let parent_gas_limit = *parent.gas_limit();
let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(min_gas),
max: Some(max_gas),
found: *header.gas_limit(),
})));
}
}
Ok(())
}
/// Verify block data against header: transactions root and uncles hash.
fn verify_block_integrity(block: &Unverified) -> Result<(), Error> {
let block_rlp = Rlp::new(&block.bytes);
let tx = block_rlp.at(1)?;
let expected_root = ordered_trie_root(tx.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// This is already checked in Unverified structure and that is why we are okay to asume that data is valid.
r.data().expect(
"Unverified block should already check if raw list of transactions is valid",
)
}
}));
if &expected_root != block.header.transactions_root() {
bail!(BlockError::InvalidTransactionsRoot(Mismatch {
expected: expected_root,
found: *block.header.transactions_root(),
}));
}
let expected_uncles = keccak(block_rlp.at(2)?.as_raw());
if &expected_uncles != block.header.uncles_hash() {
bail!(BlockError::InvalidUnclesHash(Mismatch {
expected: expected_uncles,
found: *block.header.uncles_hash(),
}));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use blockchain::{BlockDetails, BlockReceipts, TransactionAddress};
use crypto::publickey::{Generator, Random};
use engines::EthEngine;
use error::{BlockError::*, ErrorKind};
use ethereum_types::{Address, BloomRef, H256, U256};
use hash::keccak;
use rlp;
use spec::{CommonParams, Spec};
use std::{
collections::{BTreeMap, HashMap},
time::{SystemTime, UNIX_EPOCH},
};
use test_helpers::{create_test_block, create_test_block_with_data};
use triehash::ordered_trie_root;
use types::{
encoded,
log_entry::{LocalizedLogEntry, LogEntry},
transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
};
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(ErrorKind::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),
}
}
fn check_fail_timestamp(result: Result<(), Error>, temp: bool) {
let name = if temp {
"TemporarilyInvalid"
} else {
"InvalidTimestamp"
};
match result {
Err(Error(ErrorKind::Block(BlockError::InvalidTimestamp(_)), _)) if !temp => (),
Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) if temp => (),
Err(other) => panic!(
"Block verification failed.\nExpected: {}\nGot: {:?}",
name, other
),
Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name),
}
}
struct TestBlockChain {
blocks: HashMap<H256, Bytes>,
numbers: HashMap<BlockNumber, H256>,
}
impl Default for TestBlockChain {
fn default() -> Self {
TestBlockChain::new()
}
}
impl TestBlockChain {
pub fn new() -> Self {
TestBlockChain {
blocks: HashMap::new(),
numbers: HashMap::new(),
}
}
pub fn insert(&mut self, bytes: Bytes) {
let header = Unverified::from_rlp(bytes.clone()).unwrap().header;
let hash = header.hash();
self.blocks.insert(hash, bytes);
self.numbers.insert(header.number(), hash);
}
}
impl BlockProvider for TestBlockChain {
fn is_known(&self, hash: &H256) -> bool {
self.blocks.contains_key(hash)
}
fn first_block(&self) -> Option<H256> {
unimplemented!()
}
/// Get raw block data
fn block(&self, hash: &H256) -> Option<encoded::Block> {
self.blocks.get(hash).cloned().map(encoded::Block::new)
}
fn block_header_data(&self, hash: &H256) -> Option<encoded::Header> {
self.block(hash)
.map(|b| b.header_view().rlp().as_raw().to_vec())
.map(encoded::Header::new)
}
fn block_body(&self, hash: &H256) -> Option<encoded::Body> {
self.block(hash)
.map(|b| BlockChain::block_to_body(&b.into_inner()))
.map(encoded::Body::new)
}
fn best_ancient_block(&self) -> Option<H256> {
None
}
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.blocks.get(hash).map(|bytes| {
let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header;
BlockDetails {
number: header.number(),
total_difficulty: *header.difficulty(),
parent: *header.parent_hash(),
children: Vec::new(),
is_finalized: false,
}
})
}
fn transaction_address(&self, _hash: &H256) -> Option<TransactionAddress> {
unimplemented!()
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.numbers.get(&index).cloned()
}
fn block_receipts(&self, _hash: &H256) -> Option<BlockReceipts> {
unimplemented!()
}
fn blocks_with_bloom<'a, B, I, II>(
&self,
_blooms: II,
_from_block: BlockNumber,
_to_block: BlockNumber,
) -> Vec<BlockNumber>
where
BloomRef<'a>: From<B>,
II: IntoIterator<Item = B, IntoIter = I> + Copy,
I: Iterator<Item = B>,
Self: Sized,
{
unimplemented!()
}
fn logs<F>(
&self,
_blocks: Vec<H256>,
_matches: F,
_limit: Option<usize>,
) -> Vec<LocalizedLogEntry>
where
F: Fn(&LogEntry) -> bool,
Self: Sized,
{
unimplemented!()
}
}
fn basic_test(bytes: &[u8], engine: &dyn EthEngine) -> Result<(), Error> {
let unverified = Unverified::from_rlp(bytes.to_vec())?;
verify_block_basic(&unverified, engine, true)
}
fn family_test<BC>(bytes: &[u8], engine: &dyn EthEngine, bc: &BC) -> Result<(), Error>
where
BC: BlockProvider,
{
let block = Unverified::from_rlp(bytes.to_vec()).unwrap();
let header = block.header;
let transactions: Vec<_> = block
.transactions
.into_iter()
.map(SignedTransaction::new)
.collect::<Result<_, _>>()?;
// TODO: client is really meant to be used for state query here by machine
// additions that need access to state (tx filter in specific)
// no existing tests need access to test, so having this not function
// is fine.
let client = ::client::TestBlockChainClient::default();
let parent = bc
.block_header_data(header.parent_hash())
.ok_or(BlockError::UnknownParent(*header.parent_hash()))?
.decode()?;
let block = PreverifiedBlock {
header,
transactions,
uncles: block.uncles,
bytes: bytes.to_vec(),
};
let full_params = FullFamilyParams {
block: &block,
block_provider: bc as &dyn BlockProvider,
client: &client,
};
verify_block_family(&block.header, &parent, engine, Some(full_params))
}
fn unordered_test(bytes: &[u8], engine: &dyn EthEngine) -> Result<(), Error> {
let un = Unverified::from_rlp(bytes.to_vec())?;
verify_block_unordered(un, engine, false)?;
Ok(())
}
#[test]
fn test_verify_block_basic_with_invalid_transactions() {
let spec = Spec::new_test();
let engine = &*spec.engine;
let block = {
let mut rlp = rlp::RlpStream::new_list(3);
let mut header = Header::default();
// that's an invalid transaction list rlp
let invalid_transactions = vec![vec![0u8]];
header.set_transactions_root(ordered_trie_root(&invalid_transactions));
header.set_gas_limit(engine.min_gas_limit());
rlp.append(&header);
rlp.append_list::<Vec<u8>, _>(&invalid_transactions);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.out()
};
assert!(basic_test(&block, engine).is_err());
}
#[test]
fn test_verify_block() {
use rlp::RlpStream;
// Test against morden
let mut good = Header::new();
let spec = Spec::new_test();
let engine = &*spec.engine;
let min_gas_limit = engine.min_gas_limit();
good.set_gas_limit(min_gas_limit);
good.set_timestamp(40);
good.set_number(10);
let keypair = Random.generate();
let tr1 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(40_000),
nonce: U256::one(),
})
.sign(keypair.secret(), None);
let tr2 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(40_000),
nonce: U256::from(2),
})
.sign(keypair.secret(), None);
let tr3 = TypedTransaction::Legacy(Transaction {
action: Action::Call(Address::from_low_u64_be(0x0)),
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(0),
nonce: U256::zero(),
})
.null_sign(0);
let good_transactions = [tr1.clone(), tr2.clone()];
let eip86_transactions = [tr3.clone()];
let diff_inc = U256::from(0x40);
let mut parent6 = good.clone();
parent6.set_number(6);
let mut parent7 = good.clone();
parent7.set_number(7);
parent7.set_parent_hash(parent6.hash());
parent7.set_difficulty(parent6.difficulty().clone() + diff_inc);
parent7.set_timestamp(parent6.timestamp() + 10);
let mut parent8 = good.clone();
parent8.set_number(8);
parent8.set_parent_hash(parent7.hash());
parent8.set_difficulty(parent7.difficulty().clone() + diff_inc);
parent8.set_timestamp(parent7.timestamp() + 10);
let mut good_uncle1 = good.clone();
good_uncle1.set_number(9);
good_uncle1.set_parent_hash(parent8.hash());
good_uncle1.set_difficulty(parent8.difficulty().clone() + diff_inc);
good_uncle1.set_timestamp(parent8.timestamp() + 10);
let mut ex = good_uncle1.extra_data().to_vec();
ex.push(1u8);
good_uncle1.set_extra_data(ex);
let mut good_uncle2 = good.clone();
good_uncle2.set_number(8);
good_uncle2.set_parent_hash(parent7.hash());
good_uncle2.set_difficulty(parent7.difficulty().clone() + diff_inc);
good_uncle2.set_timestamp(parent7.timestamp() + 10);
let mut ex = good_uncle2.extra_data().to_vec();
ex.push(2u8);
good_uncle2.set_extra_data(ex);
let good_uncles = vec![good_uncle1.clone(), good_uncle2.clone()];
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append_list(&good_uncles);
let good_uncles_hash = keccak(uncles_rlp.as_raw());
let good_transactions_root =
ordered_trie_root(good_transactions.iter().map(|t| t.encode()));
let eip86_transactions_root =
ordered_trie_root(eip86_transactions.iter().map(|t| t.encode()));
let mut parent = good.clone();
parent.set_number(9);
parent.set_timestamp(parent8.timestamp() + 10);
parent.set_parent_hash(parent8.hash());
parent.set_difficulty(parent8.difficulty().clone() + diff_inc);
good.set_parent_hash(parent.hash());
good.set_difficulty(parent.difficulty().clone() + diff_inc);
good.set_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));
let mut bad_header = good.clone();
bad_header.set_transactions_root(eip86_transactions_root.clone());
bad_header.set_uncles_hash(good_uncles_hash.clone());
match basic_test(&create_test_block_with_data(&bad_header, &eip86_transactions, &good_uncles), engine) {
Err(Error(ErrorKind::Transaction(ref e), _)) if e == &crypto::publickey::Error::InvalidSignature.into() => (),
e => panic!("Block verification failed.\nExpected: Transaction Error (Invalid Signature)\nGot: {:?}", e),
}
let mut header = good.clone();
header.set_transactions_root(good_transactions_root.clone());
header.set_uncles_hash(good_uncles_hash.clone());
check_ok(basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
));
header.set_gas_limit(min_gas_limit - 1);
check_fail(
basic_test(&create_test_block(&header), engine),
InvalidGasLimit(OutOfBounds {
min: Some(min_gas_limit),
max: None,
found: header.gas_limit().clone(),
}),
);
header = good.clone();
header.set_number(BlockNumber::max_value());
check_fail(
basic_test(&create_test_block(&header), engine),
RidiculousNumber(OutOfBounds {
max: Some(BlockNumber::max_value()),
min: None,
found: header.number(),
}),
);
header = good.clone();
let gas_used = header.gas_limit().clone() + 1;
header.set_gas_used(gas_used);
check_fail(
basic_test(&create_test_block(&header), engine),
TooMuchGasUsed(OutOfBounds {
max: Some(header.gas_limit().clone()),
min: None,
found: header.gas_used().clone(),
}),
);
header = good.clone();
let mut ex = header.extra_data().to_vec();
ex.resize(engine.maximum_extra_data_size() + 1, 0u8);
header.set_extra_data(ex);
check_fail(
basic_test(&create_test_block(&header), engine),
ExtraDataOutOfBounds(OutOfBounds {
max: Some(engine.maximum_extra_data_size()),
min: None,
found: header.extra_data().len(),
}),
);
header = good.clone();
let mut ex = header.extra_data().to_vec();
ex.resize(engine.maximum_extra_data_size() + 1, 0u8);
header.set_extra_data(ex);
check_fail(
basic_test(&create_test_block(&header), engine),
ExtraDataOutOfBounds(OutOfBounds {
max: Some(engine.maximum_extra_data_size()),
min: None,
found: header.extra_data().len(),
}),
);
header = good.clone();
header.set_uncles_hash(good_uncles_hash.clone());
check_fail(
basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
),
InvalidTransactionsRoot(Mismatch {
expected: good_transactions_root.clone(),
found: header.transactions_root().clone(),
}),
);
header = good.clone();
header.set_transactions_root(good_transactions_root.clone());
check_fail(
basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
),
InvalidUnclesHash(Mismatch {
expected: good_uncles_hash.clone(),
found: header.uncles_hash().clone(),
}),
);
check_ok(family_test(&create_test_block(&good), engine, &bc));
check_ok(family_test(
&create_test_block_with_data(&good, &good_transactions, &good_uncles),
engine,
&bc,
));
header = good.clone();
header.set_parent_hash(H256::random());
check_fail(
family_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
&bc,
),
UnknownParent(header.parent_hash().clone()),
);
header = good.clone();
header.set_timestamp(10);
check_fail_timestamp(
family_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
&bc,
),
false,
);
header = good.clone();
// will return `BlockError::TimestampOverflow` when timestamp > `i32::max_value()`
header.set_timestamp(i32::max_value() as u64);
check_fail_timestamp(
basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
),
false,
);
header = good.clone();
header.set_timestamp(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 20,
);
check_fail_timestamp(
basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
),
true,
);
header = good.clone();
header.set_timestamp(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 10,
);
header.set_uncles_hash(good_uncles_hash.clone());
header.set_transactions_root(good_transactions_root.clone());
check_ok(basic_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
));
header = good.clone();
header.set_number(9);
check_fail(
family_test(
&create_test_block_with_data(&header, &good_transactions, &good_uncles),
engine,
&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,
&bc,
),
TooManyUncles(OutOfBounds {
max: Some(engine.maximum_uncle_count(header.number())),
min: None,
found: bad_uncles.len(),
}),
);
header = good.clone();
bad_uncles = vec![good_uncle1.clone(), good_uncle1.clone()];
check_fail(
family_test(
&create_test_block_with_data(&header, &good_transactions, &bad_uncles),
engine,
&bc,
),
DuplicateUncle(good_uncle1.hash()),
);
header = good.clone();
header.set_gas_limit(0.into());
header.set_difficulty(
"0000000000000000000000000000000000000000000000000000000000020000"
.parse::<U256>()
.unwrap(),
);
match family_test(&create_test_block(&header), engine, &bc) {
Err(Error(ErrorKind::Block(InvalidGasLimit(_)), _)) => {}
Err(_) => {
panic!("should be invalid difficulty fail");
}
_ => {
panic!("Should be error, got Ok");
}
}
// TODO: some additional uncle checks
}
#[test]
fn dust_protection() {
use crypto::publickey::{Generator, Random};
use engines::NullEngine;
use machine::EthereumMachine;
use types::transaction::{Action, Transaction};
let mut params = CommonParams::default();
params.dust_protection_transition = 0;
params.nonce_cap_increment = 2;
let mut header = Header::default();
header.set_number(1);
let keypair = Random.generate();
let bad_transactions: Vec<_> = (0..3)
.map(|i| {
TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::zero(),
data: Vec::new(),
gas: 0.into(),
gas_price: U256::zero(),
nonce: i.into(),
})
.sign(keypair.secret(), None)
})
.collect();
let good_transactions = [bad_transactions[0].clone(), bad_transactions[1].clone()];
let machine = EthereumMachine::regular(params, BTreeMap::new());
let engine = NullEngine::new(Default::default(), machine);
check_fail(
unordered_test(
&create_test_block_with_data(&header, &bad_transactions, &[]),
&engine,
),
TooManyTransactions(keypair.address()),
);
unordered_test(
&create_test_block_with_data(&header, &good_transactions, &[]),
&engine,
)
.unwrap();
}
}