Merge branch 'master' of https://github.com/gavofyork/ethcore into evm_tests

This commit is contained in:
debris 2016-01-12 13:46:31 +01:00
commit a2ae5d39fa
17 changed files with 249 additions and 43 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "res/ethereum/tests"]
path = res/ethereum/tests
url = git@github.com:ethereum/tests

1
res/ethereum/tests Submodule

@ -0,0 +1 @@
Subproject commit dc86e6359675440aea59ddb48648a01c799925d8

View File

@ -5,3 +5,8 @@ pub type LogBloom = H2048;
/// Constant 2048-bit datum for 0. Often used as a default. /// Constant 2048-bit datum for 0. Often used as a default.
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]); pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
pub enum Seal {
With,
Without,
}

View File

@ -129,7 +129,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
/// Alter the extra_data for the block. /// Alter the extra_data for the block.
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> { pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
if extra_data.len() > self.engine.maximum_extra_data_size() { if extra_data.len() > self.engine.maximum_extra_data_size() {
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: 0, max: self.engine.maximum_extra_data_size(), found: extra_data.len()})) Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()}))
} else { } else {
self.block.header.set_extra_data(extra_data); self.block.header.set_extra_data(extra_data);
Ok(()) Ok(())
@ -142,7 +142,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
/// that the header itself is actually valid. /// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> { pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
if self.block.uncles.len() >= self.engine.maximum_uncle_count() { if self.block.uncles.len() >= self.engine.maximum_uncle_count() {
return Err(BlockError::TooManyUncles(OutOfBounds{min: 0, max: self.engine.maximum_uncle_count(), found: self.block.uncles.len()})); return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.uncles.len()}));
} }
// TODO: check number // TODO: check number
// TODO: check not a direct ancestor (use last_hashes for that) // TODO: check not a direct ancestor (use last_hashes for that)

View File

@ -11,8 +11,8 @@ pub struct Mismatch<T: fmt::Debug> {
#[derive(Debug)] #[derive(Debug)]
pub struct OutOfBounds<T: fmt::Debug> { pub struct OutOfBounds<T: fmt::Debug> {
pub min: T, pub min: Option<T>,
pub max: T, pub max: Option<T>,
pub found: T, pub found: T,
} }
@ -33,6 +33,11 @@ pub enum ExecutionError {
Internal Internal
} }
#[derive(Debug)]
pub enum TransactionError {
InvalidGasLimit(OutOfBounds<U256>),
}
#[derive(Debug)] #[derive(Debug)]
pub enum BlockError { pub enum BlockError {
TooManyUncles(OutOfBounds<usize>), TooManyUncles(OutOfBounds<usize>),
@ -83,6 +88,13 @@ pub enum Error {
Block(BlockError), Block(BlockError),
UnknownEngineName(String), UnknownEngineName(String),
Execution(ExecutionError), Execution(ExecutionError),
Transaction(TransactionError),
}
impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Error {
Error::Transaction(err)
}
} }
impl From<BlockError> for Error { impl From<BlockError> for Error {
@ -97,6 +109,18 @@ impl From<ExecutionError> for Error {
} }
} }
impl From<CryptoError> for Error {
fn from(err: CryptoError) -> Error {
Error::Util(UtilError::Crypto(err))
}
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Error {
Error::Util(UtilError::Decoder(err))
}
}
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. // TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)] /*#![feature(concat_idents)]
macro_rules! assimilate { macro_rules! assimilate {

View File

@ -53,11 +53,11 @@ impl Engine for Ethash {
} }
let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap()); let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap());
if header.gas_limit < min_gas_limit { if header.gas_limit < min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: From::from(0), found: header.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(); let maximum_extra_data_size = self.maximum_extra_data_size();
if header.number != 0 && header.extra_data.len() > maximum_extra_data_size { if header.number != 0 && header.extra_data.len() > maximum_extra_data_size {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: 0, max: maximum_extra_data_size, found: header.extra_data.len() }))); return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() })));
} }
// TODO: Verify seal (quick) // TODO: Verify seal (quick)
Ok(()) Ok(())
@ -78,7 +78,7 @@ impl Engine for Ethash {
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor; let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
let max_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 { if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas, max: max_gas, found: header.gas_limit }))); return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
} }
Ok(()) Ok(())
} }

View File

@ -12,19 +12,19 @@ pub use self::denominations::*;
use super::spec::*; use super::spec::*;
/// Create a new Olympic chain spec. /// Create a new Olympic chain spec.
pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("res/olympic.json")) } pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec. /// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("res/frontier.json")) } pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier.json")) }
/// Create a new Frontier chain spec as though it never changes to Homestead. /// Create a new Frontier chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("res/frontier_test.json")) } pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_test.json")) }
/// Create a new Homestead chain spec as though it never changed from Frontier. /// Create a new Homestead chain spec as though it never changed from Frontier.
pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("res/homestead_test.json")) } pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/homestead_test.json")) }
/// Create a new Morden chain spec. /// Create a new Morden chain spec.
pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("res/morden.json")) } pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -92,25 +92,25 @@ impl<'a> Executive<'a> {
} }
} }
/// Executes transaction. /// This funtion should be used to execute transaction.
pub fn transact(&mut self, t: &Transaction) -> ExecutionResult { pub fn transact(&mut self, t: &Transaction) -> Result<Executed, Error> {
// TODO: validate transaction signature ?/ sender // TODO: validate transaction signature ?/ sender
let sender = t.sender(); let sender = try!(t.sender());
let nonce = self.state.nonce(&sender); let nonce = self.state.nonce(&sender);
// validate transaction nonce // validate transaction nonce
if t.nonce != nonce { if t.nonce != nonce {
return Err(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce }); return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce }));
} }
// validate if transaction fits into given block // validate if transaction fits into given block
if self.info.gas_used + t.gas > self.info.gas_limit { if self.info.gas_used + t.gas > self.info.gas_limit {
return Err(ExecutionError::BlockGasLimitReached { return Err(From::from(ExecutionError::BlockGasLimitReached {
gas_limit: self.info.gas_limit, gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used, gas_used: self.info.gas_used,
gas: t.gas gas: t.gas
}); }));
} }
// TODO: we might need bigints here, or at least check overflows. // TODO: we might need bigints here, or at least check overflows.
@ -120,7 +120,7 @@ impl<'a> Executive<'a> {
// avoid unaffordable transactions // avoid unaffordable transactions
if balance < total_cost { if balance < total_cost {
return Err(ExecutionError::NotEnoughCash { required: total_cost, is: balance }); return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: balance }));
} }
// NOTE: there can be no invalid transactions from this point. // NOTE: there can be no invalid transactions from this point.
@ -160,7 +160,7 @@ impl<'a> Executive<'a> {
}; };
// finalize here! // finalize here!
self.finalize(t, substate, backup, res) Ok(try!(self.finalize(t, substate, backup, res)))
} }
/// Calls contract function with given contract params. /// Calls contract function with given contract params.
@ -232,7 +232,7 @@ impl<'a> Executive<'a> {
// real ammount to refund // real ammount to refund
let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left; let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left;
let refund_value = refund * t.gas_price; let refund_value = refund * t.gas_price;
self.state.add_balance(&t.sender(), &refund_value); self.state.add_balance(&t.sender().unwrap(), &refund_value);
// fees earned by author // fees earned by author
let fees = (t.gas - refund) * t.gas_price; let fees = (t.gas - refund) * t.gas_price;

View File

@ -34,11 +34,6 @@ pub struct Header {
pub hash: RefCell<Option<H256>>, pub hash: RefCell<Option<H256>>,
} }
pub enum Seal {
With,
Without,
}
impl Header { impl Header {
/// Create a new, default-valued, header. /// Create a new, default-valued, header.
pub fn new() -> Header { pub fn new() -> Header {

View File

@ -2,7 +2,7 @@ use common::*;
use engine::Engine; use engine::Engine;
use executive::Executive; use executive::Executive;
pub type ApplyResult = Result<Receipt, ExecutionError>; pub type ApplyResult = Result<Receipt, Error>;
/// Representation of the entire state of all accounts in the system. /// Representation of the entire state of all accounts in the system.
pub struct State { pub struct State {
@ -76,6 +76,11 @@ impl State {
self.cache.borrow_mut().insert(account.clone(), None); self.cache.borrow_mut().insert(account.clone(), None);
} }
/// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> bool {
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || SecTrieDB::new(&self.db, &self.root).contains(&a)
}
/// Get the balance of account `a`. /// Get the balance of account `a`.
pub fn balance(&self, a: &Address) -> U256 { pub fn balance(&self, a: &Address) -> U256 {
self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8)) self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8))
@ -287,9 +292,12 @@ fn get_from_database() {
fn remove() { fn remove() {
let a = Address::zero(); let a = Address::zero();
let mut s = State::new_temp(); let mut s = State::new_temp();
assert_eq!(s.exists(&a), false);
s.inc_nonce(&a); s.inc_nonce(&a);
assert_eq!(s.exists(&a), true);
assert_eq!(s.nonce(&a), U256::from(1u64)); assert_eq!(s.nonce(&a), U256::from(1u64));
s.kill_account(&a); s.kill_account(&a);
assert_eq!(s.exists(&a), false);
assert_eq!(s.nonce(&a), U256::from(0u64)); assert_eq!(s.nonce(&a), U256::from(0u64));
} }
@ -300,20 +308,24 @@ fn remove_from_database() {
let mut s = State::new_temp(); let mut s = State::new_temp();
s.inc_nonce(&a); s.inc_nonce(&a);
s.commit(); s.commit();
assert_eq!(s.exists(&a), true);
assert_eq!(s.nonce(&a), U256::from(1u64)); assert_eq!(s.nonce(&a), U256::from(1u64));
s.drop() s.drop()
}; };
let (r, db) = { let (r, db) = {
let mut s = State::from_existing(db, r, U256::from(0u8)); let mut s = State::from_existing(db, r, U256::from(0u8));
assert_eq!(s.exists(&a), true);
assert_eq!(s.nonce(&a), U256::from(1u64)); assert_eq!(s.nonce(&a), U256::from(1u64));
s.kill_account(&a); s.kill_account(&a);
s.commit(); s.commit();
assert_eq!(s.exists(&a), false);
assert_eq!(s.nonce(&a), U256::from(0u64)); assert_eq!(s.nonce(&a), U256::from(0u64));
s.drop() s.drop()
}; };
let s = State::from_existing(db, r, U256::from(0u8)); let s = State::from_existing(db, r, U256::from(0u8));
assert_eq!(s.exists(&a), false);
assert_eq!(s.nonce(&a), U256::from(0u64)); assert_eq!(s.nonce(&a), U256::from(0u64));
} }

View File

@ -1,4 +1,7 @@
use util::*; use util::*;
use basic_types::*;
use error::*;
use evm::Schedule;
pub enum Action { pub enum Action {
Create, Create,
@ -15,12 +18,18 @@ pub struct Transaction {
pub value: U256, pub value: U256,
pub data: Bytes, pub data: Bytes,
// signature
pub v: u8,
pub r: U256,
pub s: U256,
hash: RefCell<Option<H256>>, //TODO: make this private hash: RefCell<Option<H256>>, //TODO: make this private
} }
impl RlpStandard for Transaction { impl Transaction {
fn rlp_append(&self, s: &mut RlpStream) { /// Append object into RLP stream, optionally with or without the signature.
s.append_list(6); pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) {
s.append_list(6 + match with_seal { Seal::With => 3, _ => 0 });
s.append(&self.nonce); s.append(&self.nonce);
s.append(&self.gas_price); s.append(&self.gas_price);
s.append(&self.gas); s.append(&self.gas);
@ -30,7 +39,22 @@ impl RlpStandard for Transaction {
}; };
s.append(&self.value); s.append(&self.value);
s.append(&self.data); s.append(&self.data);
match with_seal {
Seal::With => { s.append(&(self.v as u16)).append(&self.r).append(&self.s); },
_ => {}
}
} }
/// Get the RLP serialisation of the object, optionally with or without the signature.
pub fn rlp_bytes_opt(&self, with_seal: Seal) -> Bytes {
let mut s = RlpStream::new();
self.rlp_append_opt(&mut s, with_seal);
s.out()
}
}
impl RlpStandard for Transaction {
fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_opt(s, Seal::With) }
} }
impl Transaction { impl Transaction {
@ -54,8 +78,41 @@ impl Transaction {
/// Returns transaction type. /// Returns transaction type.
pub fn action(&self) -> &Action { &self.action } pub fn action(&self) -> &Action { &self.action }
/// 0 is `v` is 27, 1 if 28, and 4 otherwise.
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } }
/// Construct a signature object from the sig.
pub fn signature(&self) -> Signature { Signature::from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
/// The message hash of the transaction.
pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() }
/// Returns transaction sender. /// Returns transaction sender.
pub fn sender(&self) -> Address { Address::new() } pub fn sender(&self) -> Result<Address, Error> { Ok(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3())) }
/// Get the transaction cost in gas for the given params.
pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> U256 {
// CRITICAL TODO XXX FIX NEED BIGINT!!!!!
data.iter().fold(
U256::from(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}),
|g, b| g + U256::from(match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas})
)
}
/// Get the transaction cost in gas for this transaction.
pub fn gas_required(&self, schedule: &Schedule) -> U256 {
Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule)
}
/// Do basic validation, checking for valid signature and minimum gas,
pub fn validate(self, schedule: &Schedule) -> Result<Transaction, Error> {
try!(self.sender());
if self.gas < self.gas_required(&schedule) {
Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(self.gas_required(&schedule)), max: None, found: self.gas})))
} else {
Ok(self)
}
}
} }
impl Decodable for Action { impl Decodable for Action {
@ -72,17 +129,126 @@ impl Decodable for Action {
impl Decodable for Transaction { impl Decodable for Transaction {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = try!(decoder.as_list()); let d = try!(decoder.as_list());
if d.len() != 9 {
let transaction = Transaction { return Err(DecoderError::RlpIncorrectListLen);
}
Ok(Transaction {
nonce: try!(Decodable::decode(&d[0])), nonce: try!(Decodable::decode(&d[0])),
gas_price: try!(Decodable::decode(&d[1])), gas_price: try!(Decodable::decode(&d[1])),
gas: try!(Decodable::decode(&d[2])), gas: try!(Decodable::decode(&d[2])),
action: try!(Decodable::decode(&d[3])), action: try!(Decodable::decode(&d[3])),
value: try!(Decodable::decode(&d[4])), value: try!(Decodable::decode(&d[4])),
data: try!(Decodable::decode(&d[5])), data: try!(Decodable::decode(&d[5])),
v: try!(u16::decode(&d[6])) as u8,
r: try!(Decodable::decode(&d[7])),
s: try!(Decodable::decode(&d[8])),
hash: RefCell::new(None) hash: RefCell::new(None)
}; })
Ok(transaction)
} }
} }
pub fn clean(s: &str) -> &str {
if s.len() >= 2 && &s[0..2] == "0x" {
&s[2..]
} else {
s
}
}
pub fn bytes_from_json(json: &Json) -> Bytes {
let s = json.as_string().unwrap();
if s.len() % 2 == 1 {
FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![])
} else {
FromHex::from_hex(clean(s)).unwrap_or(vec![])
}
}
pub fn address_from_json(json: &Json) -> Address {
let s = json.as_string().unwrap();
if s.len() % 2 == 1 {
address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..])
} else {
address_from_hex(clean(s))
}
}
pub fn u256_from_json(json: &Json) -> U256 {
let s = json.as_string().unwrap();
if s.len() >= 2 && &s[0..2] == "0x" {
// hex
U256::from_str(&s[2..]).unwrap()
}
else {
// dec
U256::from_dec_str(s).unwrap()
}
}
#[cfg(test)]
mod tests {
use util::*;
use evm::Schedule;
use header::BlockNumber;
use super::*;
#[test]
fn sender_test() {
let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
assert_eq!(t.data, b"");
assert_eq!(t.gas, U256::from(0x5208u64));
assert_eq!(t.gas_price, U256::from(0x01u64));
assert_eq!(t.nonce, U256::from(0x00u64));
if let Action::Call(ref to) = t.action {
assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87"));
} else { panic!(); }
assert_eq!(t.value, U256::from(0x0au64));
assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e"));
}
fn do_json_test(json_data: &[u8]) -> Vec<String> {
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
let schedule = Schedule::new_frontier();
for (name, test) in json.as_object().unwrap() {
let mut fail = false;
let mut fail_unless = |cond: bool| if !cond && fail { failed.push(name.to_string()); fail = true };
let _ = BlockNumber::from_str(test["blocknumber"].as_string().unwrap()).unwrap();
let rlp = bytes_from_json(&test["rlp"]);
let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(&schedule));
fail_unless(test.find("transaction").is_none() == res.is_err());
if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) {
let t = res.unwrap();
fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender)));
fail_unless(t.data == bytes_from_json(&tx["data"]));
fail_unless(t.gas == u256_from_json(&tx["gasLimit"]));
fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"]));
fail_unless(t.nonce == u256_from_json(&tx["nonce"]));
fail_unless(t.value == u256_from_json(&tx["value"]));
if let Action::Call(ref to) = t.action {
fail_unless(to == &address_from_json(&tx["to"]));
} else {
fail_unless(bytes_from_json(&tx["to"]).len() == 0);
}
}
}
for f in failed.iter() {
println!("FAILED: {:?}", f);
}
failed
}
macro_rules! declare_test {
($test_set_name: ident/$name: ident) => {
#[test]
#[allow(non_snake_case)]
fn $name() {
assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($name), ".json"))).len() == 0);
}
}
}
declare_test!{TransactionTests/ttTransactionTest}
declare_test!{TransactionTests/tt10mbDataField}
declare_test!{TransactionTests/ttWrongRLPTransaction}
}

View File

@ -47,7 +47,7 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
let num_uncles = Rlp::new(bytes).at(2).item_count(); let num_uncles = Rlp::new(bytes).at(2).item_count();
if num_uncles != 0 { if num_uncles != 0 {
if num_uncles > engine.maximum_uncle_count() { if num_uncles > engine.maximum_uncle_count() {
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: 0, max: engine.maximum_uncle_count(), found: num_uncles }))); return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
} }
let mut excluded = HashSet::new(); let mut excluded = HashSet::new();
@ -83,10 +83,10 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 }; let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
if depth > 6 { if depth > 6 {
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: header.number - depth, max: header.number - 1, found: uncle.number }))); return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
} }
else if depth < 1 { else if depth < 1 {
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: header.number - depth, max: header.number - 1, found: uncle.number }))); return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
} }
// cB // cB
@ -115,10 +115,10 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res
/// Check basic header parameters. /// Check basic header parameters.
fn verify_header(header: &Header) -> Result<(), Error> { fn verify_header(header: &Header) -> Result<(), Error> {
if header.number > From::from(BlockNumber::max_value()) { if header.number > From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: From::from(BlockNumber::max_value()), min: 0, found: header.number }))) 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 { if header.gas_used > header.gas_limit {
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: header.gas_limit, min: From::from(0), found: header.gas_used }))); return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
} }
Ok(()) Ok(())
} }
@ -129,10 +129,10 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() }))) return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() })))
} }
if header.timestamp <= parent.timestamp { if header.timestamp <= parent.timestamp {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: u64::max_value(), min: parent.timestamp + 1, found: header.timestamp }))) return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
} }
if header.number <= parent.number { if header.number <= parent.number {
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: BlockNumber::max_value(), min: parent.number + 1, found: header.number }))); return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number })));
} }
Ok(()) Ok(())
} }