Merge branch 'master' into gav

This commit is contained in:
Gav Wood 2016-01-12 16:50:02 +01:00
commit 5abffb518d
9 changed files with 575 additions and 143 deletions

View File

@ -47,6 +47,55 @@ impl BestBlock {
}
}
/// Interface for querying blocks by hash and by number.
pub trait BlockProvider {
/// Returns true if the given block is known
/// (though not necessarily a part of the canon chain).
fn is_known(&self, hash: &H256) -> bool;
/// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes>;
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails>;
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256>;
/// Get the partial-header of a block.
fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
}
/// Get a list of uncles for a given block.
/// Returns None if block deos not exist.
fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
}
/// Get a list of uncle hashes for a given block.
/// Returns None if block does not exist.
fn uncle_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes())
}
/// Get the number of given block's hash.
fn block_number(&self, hash: &H256) -> Option<BlockNumber> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
}
/// Get a list of transactions for a given block.
/// Returns None if block deos not exist.
fn transactions(&self, hash: &H256) -> Option<Vec<Transaction>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).transactions())
}
/// Returns reference to genesis hash.
fn genesis_hash(&self) -> H256 {
self.block_hash(0).expect("Genesis hash should always exist")
}
}
/// Structure providing fast access to blockchain data.
///
/// **Does not do input data verification.**
@ -67,6 +116,48 @@ pub struct BlockChain {
blocks_db: DB
}
impl BlockProvider for BlockChain {
/// Returns true if the given block is known
/// (though not necessarily a part of the canon chain).
fn is_known(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.block_details)
}
/// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes> {
{
let read = self.blocks.read().unwrap();
match read.get(hash) {
Some(v) => return Some(v.clone()),
None => ()
}
}
let opt = self.blocks_db.get(hash)
.expect("Low level database error. Some issue with disk?");
match opt {
Some(b) => {
let bytes: Bytes = b.to_vec();
let mut write = self.blocks.write().unwrap();
write.insert(hash.clone(), bytes.clone());
Some(bytes)
},
None => None
}
}
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.query_extras(hash, &self.block_details)
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.query_extras(&index, &self.block_hashes)
}
}
impl BlockChain {
/// Create new instance of blockchain from given Genesis
///
@ -363,61 +454,11 @@ impl BlockChain {
(batch, Some(best_block))
}
/// Returns true if the given block is known
/// (though not necessarily a part of the canon chain).
pub fn is_known(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.block_details)
}
/// Returns true if transaction is known.
pub fn is_known_transaction(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.transaction_addresses)
}
/// Returns reference to genesis hash.
pub fn genesis_hash(&self) -> H256 {
self.block_hash(0).expect("Genesis hash should always exist")
}
/// Get the partial-header of a block.
pub fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
}
/// Get a list of transactions for a given block.
/// Returns None if block deos not exist.
pub fn transactions(&self, hash: &H256) -> Option<Vec<Transaction>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).transactions())
}
/// Get a list of transaction hashes for a given block.
/// Returns None if block does not exist.
pub fn transaction_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).transaction_hashes())
}
/// Get a list of uncles for a given block.
/// Returns None if block deos not exist.
pub fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
}
/// Get a list of uncle hashes for a given block.
/// Returns None if block does not exist.
pub fn uncle_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes())
}
/// Get the familial details concerning a block.
pub fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.query_extras(hash, &self.block_details)
}
/// Get the hash of given block's number.
pub fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.query_extras(&index, &self.block_hashes)
}
/// Get best block hash.
pub fn best_block_hash(&self) -> H256 {
self.best_block.read().unwrap().hash.clone()
@ -433,40 +474,11 @@ impl BlockChain {
self.best_block.read().unwrap().total_difficulty
}
/// Get the number of given block's hash.
pub fn block_number(&self, hash: &H256) -> Option<BlockNumber> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
}
/// Get the transactions' log blooms of a block.
pub fn log_blooms(&self, hash: &H256) -> Option<BlockLogBlooms> {
self.query_extras(hash, &self.block_logs)
}
/// Get raw block data
pub fn block(&self, hash: &H256) -> Option<Bytes> {
{
let read = self.blocks.read().unwrap();
match read.get(hash) {
Some(v) => return Some(v.clone()),
None => ()
}
}
let opt = self.blocks_db.get(hash)
.expect("Low level database error. Some issue with disk?");
match opt {
Some(b) => {
let bytes: Bytes = b.to_vec();
let mut write = self.blocks.write().unwrap();
write.insert(hash.clone(), bytes.clone());
Some(bytes)
},
None => None
}
}
fn query_extras<K, T>(&self, hash: &K, cache: &RwLock<HashMap<K, T>>) -> Option<T> where
T: Clone + Decodable + ExtrasIndexable,
K: ExtrasSliceConvertable + Eq + Hash + Clone {

View File

@ -1,5 +1,5 @@
use util::*;
use blockchain::BlockChain;
use blockchain::{BlockChain, BlockProvider};
use views::BlockView;
use error::*;
use header::BlockNumber;

View File

@ -3,13 +3,13 @@
use util::*;
use header::BlockNumber;
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct Mismatch<T: fmt::Debug> {
pub expected: T,
pub found: T,
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct OutOfBounds<T: fmt::Debug> {
pub min: Option<T>,
pub max: Option<T>,
@ -38,7 +38,7 @@ pub enum TransactionError {
InvalidGasLimit(OutOfBounds<U256>),
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum BlockError {
TooManyUncles(OutOfBounds<usize>),
UncleWrongGeneration,

View File

@ -51,14 +51,6 @@ impl Engine for Ethash {
if header.difficulty < min_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
}
let min_gas_limit = decode(self.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 = self.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() })));
}
// TODO: Verify seal (quick)
Ok(())
}

View File

@ -21,6 +21,8 @@ struct Substate {
logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero->zero.
refunds_count: U256,
/// Created contracts.
contracts_created: Vec<Address>
}
impl Substate {
@ -30,11 +32,13 @@ impl Substate {
suicides: HashSet::new(),
logs: vec![],
refunds_count: U256::zero(),
contracts_created: vec![]
}
}
}
/// Transaction execution receipt.
#[derive(Debug)]
pub struct Executed {
/// Gas paid up front for execution of transaction.
pub gas: U256,
@ -54,6 +58,13 @@ pub struct Executed {
/// Execution ended running out of gas.
pub out_of_gas: bool,
/// Addresses of contracts created during execution of transaction.
/// Ordered from earliest creation.
///
/// eg. sender creates contract A and A in constructor creates contract B
///
/// B creation ends first, and it will be the first element of the vector.
pub contracts_created: Vec<Address>
}
/// Transaction execution result.
@ -64,7 +75,7 @@ pub struct Executive<'a> {
state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
depth: usize
}
impl<'a> Executive<'a> {
@ -85,7 +96,7 @@ impl<'a> Executive<'a> {
state: state,
info: info,
engine: engine,
depth: depth,
depth: depth
}
}
@ -122,8 +133,9 @@ impl<'a> Executive<'a> {
// NOTE: there can be no invalid transactions from this point.
self.state.inc_nonce(&sender);
let mut substate = Substate::new();
self.state.sub_balance(&sender, &gas_cost);
let mut substate = Substate::new();
let backup = self.state.clone();
let res = match t.action() {
@ -215,6 +227,7 @@ impl<'a> Executive<'a> {
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
out_of_gas: true,
contracts_created: vec![]
})
},
Ok(gas_left) => {
@ -248,6 +261,7 @@ impl<'a> Executive<'a> {
cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs,
out_of_gas: false,
contracts_created: substate.contracts_created
})
}
}
@ -331,7 +345,7 @@ impl<'a> Ext for Externalities<'a> {
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), evm::Error> {
// if balance is insufficient or we are to deep, return
if self.state.balance(&self.params.address) < *value && self.depth >= 1024 {
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
return Ok((gas, None));
}
@ -360,7 +374,7 @@ impl<'a> Ext for Externalities<'a> {
let mut call_gas = call_gas;
let is_call = receive_address == code_address;
if is_call && self.state.code(&code_address).is_none() {
if is_call && !self.state.exists(&code_address) {
gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
}
@ -376,9 +390,8 @@ impl<'a> Ext for Externalities<'a> {
let gas = gas - gas_cost;
//println!("depth: {:?}", self.depth);
// if balance is insufficient or we are to deep, return
if self.state.balance(&self.params.address) < *value && self.depth >= 1024 {
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
return Ok(gas + call_gas)
}
@ -394,7 +407,9 @@ impl<'a> Ext for Externalities<'a> {
};
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.call(&params, self.substate, output).map(|gas_left| gas + gas_left.low_u64())
ex.call(&params, self.substate, output).map(|gas_left| {
gas + gas_left.low_u64()
})
}
fn extcode(&self, address: &Address) -> Vec<u8> {
@ -402,6 +417,7 @@ impl<'a> Ext for Externalities<'a> {
}
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, evm::Error> {
println!("ret");
match &mut self.output {
&mut OutputPolicy::Return(ref mut slice) => unsafe {
let len = cmp::min(slice.len(), data.len());
@ -411,7 +427,10 @@ impl<'a> Ext for Externalities<'a> {
&mut OutputPolicy::InitContract => {
let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64;
if return_cost > gas {
return Err(evm::Error::OutOfGas);
return match self.schedule.exceptional_failed_code_deposit {
true => Err(evm::Error::OutOfGas),
false => Ok(gas)
}
}
let mut code = vec![];
code.reserve(data.len());
@ -421,6 +440,7 @@ impl<'a> Ext for Externalities<'a> {
}
let address = &self.params.address;
self.state.init_code(address, code);
self.substate.contracts_created.push(address.clone());
Ok(gas - return_cost)
}
}
@ -451,9 +471,35 @@ mod tests {
use common::*;
use state::*;
use ethereum;
use null_engine::*;
use engine::*;
use spec::*;
use evm::Schedule;
use super::Substate;
struct TestEngine {
spec: Spec,
stack_limit: usize
}
impl TestEngine {
fn new(stack_limit: usize) -> TestEngine {
TestEngine {
spec: ethereum::new_frontier(),
stack_limit: stack_limit
}
}
}
impl Engine for TestEngine {
fn name(&self) -> &str { "TestEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
let mut schedule = Schedule::new_frontier();
schedule.stack_limit = self.stack_limit;
schedule
}
}
#[test]
fn test_contract_address() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
@ -463,35 +509,114 @@ mod tests {
#[test]
// TODO: replace params with transactions!
fn test_executive() {
fn test_sender_balance() {
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let address = contract_address(&sender, &U256::zero());
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(0x174876e800u64);
params.gas = U256::from(100_000);
params.code = "3331600055".from_hex().unwrap();
params.value = U256::from(0x7);
let mut state = State::new_temp();
state.add_balance(&sender, &U256::from(0x100u64));
let info = EnvInfo::new();
let engine = NullEngine::new_boxed(ethereum::new_frontier());
let engine = TestEngine::new(0);
let mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, engine.deref());
let _res = ex.create(&params, &mut substate);
}
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(&params, &mut substate).unwrap()
};
assert_eq!(gas_left, U256::from(79_975));
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0xf9u64)));
assert_eq!(state.balance(&sender), U256::from(0xf9));
assert_eq!(state.balance(&address), U256::from(0x7));
// 0 cause contract hasn't returned
assert_eq!(substate.contracts_created.len(), 0);
// TODO: just test state root.
}
#[test]
fn test_create_contract() {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 17 - push 17
// f0 - create
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = code.clone();
params.value = U256::from(100);
let mut state = State::new_temp();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::new();
let engine = TestEngine::new(0);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(&params, &mut substate).unwrap()
};
assert_eq!(gas_left, U256::from(47_976));
assert_eq!(substate.contracts_created.len(), 0);
}
#[test]
fn test_create_contract_without_stack_limit() {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 17 - push 17
// f0 - create
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
let next_address = contract_address(&address, &U256::zero());
@ -499,23 +624,80 @@ mod tests {
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(0x174876e800u64);
params.code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036000f0600055".from_hex().unwrap();
params.gas = U256::from(100_000);
params.code = code.clone();
params.value = U256::from(100);
let mut state = State::new_temp();
state.add_balance(&sender, &U256::from(0x100u64));
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::new();
let engine = NullEngine::new_boxed(ethereum::new_frontier());
let engine = TestEngine::new(1024);
let mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, engine.deref());
let _res = ex.create(&params, &mut substate);
println!("res: {:?}", _res);
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(&params, &mut substate).unwrap();
}
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(next_address.clone()));
assert_eq!(state.code(&next_address).unwrap(), "6000355415600957005b602035600035".from_hex().unwrap());
//assert!(false);
assert_eq!(substate.contracts_created.len(), 1);
assert_eq!(substate.contracts_created[0], next_address);
}
#[test]
fn test_aba_calls() {
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 18 - push 18
// 73 945304eb96065b2a98b57a48a06ae28d285a71b5 - push this address
// 61 03e8 - push 1000
// f1 - message call
// 58 - get PC
// 55 - sstore
let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855".from_hex().unwrap();
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 17 - push 17
// 73 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 - push this address
// 61 0x01f4 - push 500
// f1 - message call
// 60 01 - push 1
// 01 - add
// 58 - get PC
// 55 - sstore
let code_b = "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855".from_hex().unwrap();
let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let mut params = ActionParams::new();
params.address = address_a.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = code_a.clone();
params.value = U256::from(100_000);
let mut state = State::new_temp();
state.init_code(&address_a, code_a.clone());
state.init_code(&address_b, code_b.clone());
state.add_balance(&sender, &U256::from(100_000));
let info = EnvInfo::new();
let engine = TestEngine::new(0);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.call(&params, &mut substate, &mut []).unwrap()
};
assert_eq!(gas_left, U256::from(73_237));
assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))), H256::from(&U256::from(1)));
}
#[test]
@ -537,28 +719,27 @@ mod tests {
// 03 - sub
// f1 - message call (self in this case)
// 60 01 - push 1
// 55 - store
// 55 - sstore
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap();
let address = contract_address(&sender, &U256::zero());
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(0x590b3);
params.gas_price = U256::one();
params.gas = U256::from(100_000);
params.code = code.clone();
println!("init gas: {:?}", params.gas.low_u64());
let mut state = State::new_temp();
state.init_code(&address, code.clone());
let info = EnvInfo::new();
let engine = NullEngine::new_boxed(ethereum::new_frontier());
let engine = TestEngine::new(0);
let mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, engine.deref());
let _res = ex.call(&params, &mut substate, &mut []);
println!("res: {:?}", _res);
}
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.call(&params, &mut substate, &mut []).unwrap()
};
assert_eq!(gas_left, U256::from(59_870));
assert_eq!(state.storage_at(&address, &H256::from(&U256::zero())), H256::from(&U256::from(1)));
assert_eq!(state.storage_at(&address, &H256::from(&U256::one())), H256::from(&U256::from(1)));
}
}

View File

@ -10,7 +10,7 @@ pub type BlockNumber = u64;
/// which is non-specific.
///
/// Doesn't do all that much on its own.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Header {
// TODO: make all private.
pub parent_hash: H256,

View File

@ -2,6 +2,7 @@ use util::*;
use basic_types::LogBloom;
/// A single log's entry.
#[derive(Debug)]
pub struct LogEntry {
pub address: Address,
pub topics: Vec<H256>,
@ -59,4 +60,4 @@ mod tests {
let log = LogEntry::new(address, vec![], vec![]);
assert_eq!(log.bloom(), bloom);
}
}
}

View File

@ -1,5 +1,5 @@
use util::*;
use blockchain::BlockChain;
use blockchain::*;
use views::{BlockView};
use verification::*;
use error::*;

View File

@ -7,17 +7,17 @@
use common::*;
use engine::Engine;
use blockchain::BlockChain;
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();
try!(verify_header(&header));
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::<Header>()) {
try!(verify_header(&u));
try!(verify_header(&u, engine));
try!(engine.verify_block_basic(&u, None));
}
Ok(())
@ -37,7 +37,7 @@ 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_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Result<(), Error> {
pub fn verify_block_final<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
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()))));
@ -67,9 +67,8 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
}
for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::<Error>(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
if excluded.contains(&uncle_parent.hash()) {
return Err(From::from(BlockError::UncleInChain(uncle_parent.hash())))
if excluded.contains(&uncle.hash()) {
return Err(From::from(BlockError::UncleInChain(uncle.hash())))
}
// m_currentBlock.number() - uncle.number() m_cB.n - uP.n()
@ -99,13 +98,20 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
// 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::<Error>(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
for _ in 0..depth {
expected_uncle_parent = bc.block_details(&expected_uncle_parent).unwrap().parent;
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_final(&uncle, &uncle_parent, Some(bytes)));
}
}
@ -113,13 +119,21 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
}
/// Check basic header parameters.
fn verify_header(header: &Header) -> Result<(), Error> {
if header.number > From::from(BlockNumber::max_value()) {
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
if header.number >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::InvalidNumber(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(())
}
@ -152,3 +166,235 @@ fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &
Ok(())
}
#[cfg(test)]
mod tests {
use util::*;
use header::*;
use verification::*;
use extras::*;
use error::*;
use error::BlockError::*;
use views::*;
use blockchain::*;
use ethereum;
fn create_test_block(header: &Header) -> Bytes {
let mut rlp = RlpStream::new_list(3);
rlp.append(header);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.out()
}
fn create_test_block_with_data(header: &Header, transactions: &[Bytes], uncles: &[Header]) -> Bytes {
let mut rlp = RlpStream::new_list(3);
rlp.append(header);
rlp.append_list(transactions.len());
for t in transactions {
rlp.append_raw(t, 1);
}
rlp.append_list(uncles.len());
for h in uncles {
rlp.append(h);
}
rlp.out()
}
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<H256, Bytes>,
numbers: HashMap<BlockNumber, H256>,
}
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<Bytes> {
self.blocks.get(hash).map(|b| b.clone())
}
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
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<H256> {
self.numbers.get(&index).map(|h| h.clone())
}
}
#[test]
fn test_verify_block() {
// Test against morden
let mut good = Header::new();
let spec = ethereum::new_morden();
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 good_transactions = vec![ vec![ 1u8 ], vec![ 2u8 ] ]; // TODO: proper transactions
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.clone());
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(verify_block_basic(&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()));
header.gas_limit = min_gas_limit - From::from(1);
check_fail(verify_block_basic(&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()),
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()),
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()),
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()),
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()),
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()),
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
check_ok(verify_block_final(&create_test_block(&good), engine.deref(), &bc));
check_ok(verify_block_final(&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_final(&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_final(&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_final(&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_final(&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
}
}