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

This commit is contained in:
arkpar 2016-01-12 13:31:49 +01:00
commit fdcf9e62ec
35 changed files with 2182 additions and 218 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

View File

@ -12,7 +12,7 @@ env_logger = "0.3"
ethcore-util = { path = "../ethcore-util" }
rustc-serialize = "0.3"
flate2 = "0.2"
rocksdb = "0.2.1"
rocksdb = "0.2"
heapsize = "0.2.0"
rust-crypto = "0.2.34"
time = "0.1"
@ -20,4 +20,5 @@ time = "0.1"
evmjit = { path = "rust-evmjit", optional = true }
[features]
default = ["jit"]
jit = ["evmjit"]

1
res/ethereum/tests Submodule

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

View File

@ -3,6 +3,8 @@ name = "evmjit"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
[lib]
crate-type = ["dylib"]
[dependencies]
libc = "0.2.2"
tiny-keccak = "1.0"

View File

@ -7,27 +7,52 @@
//! use evmjit::*;
//!
//! fn main() {
//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty());
//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), ExtHandle::empty());
//! assert_eq!(context.exec(), ReturnCode::Stop);
//! }
//! ```
//!
//!
//! To verify that c abi is "imported" correctly, run:
//!
//! ```bash
//! nm your_executable -g | grep ext
//! ```
//!
//! It should give the following output:
//!
//! ```bash
//! 00000001000779e0 T _ext_balance
//! 0000000100077a10 T _ext_blockhash
//! 0000000100077a90 T _ext_call
//! 0000000100077a40 T _ext_create
//! 0000000100077b50 T _ext_extcode
//! 0000000100077b80 T _ext_log
//! 0000000100077b20 T _ext_sha3
//! 0000000100077980 T _ext_sload
//! 00000001000779b0 T _ext_sstore
//! ```
extern crate libc;
extern crate tiny_keccak;
use std::ops::{Deref, DerefMut};
use self::ffi::*;
pub use self::ffi::JitReturnCode as ReturnCode;
pub use self::ffi::JitI256 as I256;
pub use self::ffi::JitH256 as H256;
/// Component oriented safe handle to `JitRuntimeData`.
/// Takes care of proper initialization and destruction of `RuntimeData`.
///
/// This handle must be used to create runtime data,
/// cause underneath it's a `C++` structure. Incombatible with rust
/// structs.
pub struct RuntimeDataHandle {
runtime_data: *mut JitRuntimeData
}
impl RuntimeDataHandle {
/// Creates new handle.
/// Creates new `RuntimeData` handle.
pub fn new() -> Self {
RuntimeDataHandle {
runtime_data: unsafe { evmjit_create_runtime_data() }
@ -51,26 +76,61 @@ impl Drop for RuntimeDataHandle {
}
}
/// Safe handle for jit context.
impl Deref for RuntimeDataHandle {
type Target = JitRuntimeData;
fn deref(&self) -> &Self::Target {
self.runtime_data()
}
}
impl DerefMut for RuntimeDataHandle {
fn deref_mut(&mut self) -> &mut Self::Target {
self.mut_runtime_data()
}
}
/// Takes care of proper initialization and destruction of jit context.
///
/// This handle must be used to create context,
/// cause underneath it's a `C++` structure. Incombatible with rust
/// structs.
pub struct ContextHandle {
context: *mut JitContext,
_data_handle: RuntimeDataHandle
data_handle: RuntimeDataHandle,
}
impl ContextHandle {
/// Creates new context handle.
pub fn new(mut data_handle: RuntimeDataHandle, mut env: EnvHandle) -> Self {
let context = unsafe { evmjit_create_context(data_handle.mut_runtime_data(), &mut env) };
ContextHandle {
context: context,
_data_handle: data_handle
}
///
/// This function is unsafe cause ext lifetime is not considered
/// We also can't make ExtHandle a member of `ContextHandle` structure,
/// cause this would be a move operation or it would require a template
/// lifetime to a reference. Both solutions are not possible.
pub unsafe fn new(data_handle: RuntimeDataHandle, ext: &mut ExtHandle) -> Self {
let mut handle = ContextHandle {
context: std::mem::uninitialized(),
data_handle: data_handle,
};
handle.context = evmjit_create_context(handle.data_handle.mut_runtime_data(), ext);
handle
}
/// Executes context.
pub fn exec(&mut self) -> JitReturnCode {
unsafe { evmjit_exec(self.context) }
}
/// Returns output data.
pub fn output_data(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) }
}
/// Returns gas left.
pub fn gas_left(&self) -> u64 {
self.data_handle.gas as u64
}
}
impl Drop for ContextHandle {
@ -79,76 +139,76 @@ impl Drop for ContextHandle {
}
}
/// Component oriented wrapper around jit env c interface.
pub trait Env {
/// Component oriented wrapper around jit ext c interface.
pub trait Ext {
fn sload(&self, index: *const JitI256, out_value: *mut JitI256);
fn sstore(&mut self, index: *const JitI256, value: *const JitI256);
fn balance(&self, address: *const JitI256, out_value: *mut JitI256);
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitI256);
fn balance(&self, address: *const JitH256, out_value: *mut JitI256);
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitH256);
fn create(&mut self,
io_gas: *mut u64,
endowment: *const JitI256,
init_beg: *const u8,
init_size: *const u64,
address: *mut JitI256);
init_size: u64,
address: *mut JitH256);
fn call(&mut self,
io_gas: *mut u64,
call_gas: *const u64,
receive_address: *const JitI256,
call_gas: u64,
receive_address: *const JitH256,
value: *const JitI256,
in_beg: *const u8,
in_size: *const u64,
in_size: u64,
out_beg: *mut u8,
out_size: *mut u64,
code_address: JitI256) -> bool;
out_size: u64,
code_address: *const JitH256) -> bool;
fn log(&mut self,
beg: *const u8,
size: *const u64,
topic1: *const JitI256,
topic2: *const JitI256,
topic3: *const JitI256,
topic4: *const JitI256);
size: u64,
topic1: *const JitH256,
topic2: *const JitH256,
topic3: *const JitH256,
topic4: *const JitH256);
fn extcode(&self, address: *const JitI256, size: *mut u64) -> *const u8;
fn extcode(&self, address: *const JitH256, size: *mut u64) -> *const u8;
}
/// C abi compatible wrapper for jit env implementers.
pub struct EnvHandle {
env_impl: Option<Box<Env>>
/// C abi compatible wrapper for jit ext implementers.
pub struct ExtHandle {
ext_impl: Option<Box<Ext>>
}
impl EnvHandle {
/// Creates new environment wrapper for given implementation
pub fn new<T>(env_impl: T) -> Self where T: Env + 'static {
EnvHandle { env_impl: Some(Box::new(env_impl)) }
impl ExtHandle {
/// Creates new extironment wrapper for given implementation
pub fn new<T>(ext_impl: T) -> Self where T: Ext + 'static {
ExtHandle { ext_impl: Some(Box::new(ext_impl)) }
}
/// Creates empty environment.
/// Creates empty extironment.
/// It can be used to for any operations.
pub fn empty() -> Self {
EnvHandle { env_impl: None }
ExtHandle { ext_impl: None }
}
}
impl Deref for EnvHandle {
type Target = Box<Env>;
impl Deref for ExtHandle {
type Target = Box<Ext>;
fn deref(&self) -> &Self::Target {
match self.env_impl {
Some(ref env) => env,
None => { panic!(); }
match self.ext_impl {
Some(ref ext) => ext,
None => { panic!("Handle is empty!"); }
}
}
}
impl DerefMut for EnvHandle {
impl DerefMut for ExtHandle {
fn deref_mut(&mut self) -> &mut Self::Target {
match self.env_impl {
Some(ref mut env) => env,
None => { panic!(); }
match self.ext_impl {
Some(ref mut ext) => ext,
None => { panic!("Handle is empty!"); }
}
}
}
@ -156,7 +216,7 @@ impl DerefMut for EnvHandle {
/// ffi functions
pub mod ffi {
use std::slice;
use libc;
use std::mem;
use tiny_keccak::Keccak;
use super::*;
@ -178,109 +238,142 @@ pub mod ffi {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
/// Signed 256 bit integer.
pub struct JitI256 {
pub words: [u64; 4]
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
/// Jit Hash
pub struct JitH256 {
pub words: [u64; 4]
}
impl From<JitH256> for JitI256 {
fn from(mut hash: JitH256) -> JitI256 {
unsafe {
{
let bytes: &mut [u8] = slice::from_raw_parts_mut(hash.words.as_mut_ptr() as *mut u8, 32);
bytes.reverse();
}
mem::transmute(hash)
}
}
}
impl From<JitI256> for JitH256 {
fn from(mut i: JitI256) -> JitH256 {
unsafe {
{
let bytes: &mut [u8] = slice::from_raw_parts_mut(i.words.as_mut_ptr() as *mut u8, 32);
bytes.reverse();
}
mem::transmute(i)
}
}
}
#[repr(C)]
#[derive(Debug)]
/// Jit runtime data.
pub struct JitRuntimeData {
pub gas: i64,
pub gas_price: i64,
pub call_data: *const libc::c_char,
pub call_data: *const u8,
pub call_data_size: u64,
pub address: JitI256,
pub caller: JitI256,
pub origin: JitI256,
pub call_value: JitI256,
pub coinbase: JitI256,
pub author: JitI256,
pub difficulty: JitI256,
pub gas_limit: JitI256,
pub number: u64,
pub timestamp: i64,
pub code: *const libc::c_char,
pub code: *const u8,
pub code_size: u64,
pub code_hash: JitI256
}
#[no_mangle]
pub unsafe extern fn env_sload(env: *const EnvHandle, index: *const JitI256, out_value: *mut JitI256) {
let env = &*env;
env.sload(index, out_value);
pub unsafe extern "C" fn env_sload(ext: *const ExtHandle, index: *const JitI256, out_value: *mut JitI256) {
let ext = &*ext;
ext.sload(index, out_value);
}
#[no_mangle]
pub unsafe extern fn env_sstore(env: *mut EnvHandle, index: *const JitI256, value: *const JitI256) {
let env = &mut *env;
env.sstore(index, value);
pub unsafe extern "C" fn env_sstore(ext: *mut ExtHandle, index: *mut JitI256, value: *mut JitI256) {
let ext = &mut *ext;
ext.sstore(index, value);
}
#[no_mangle]
pub unsafe extern fn env_balance(env: *const EnvHandle, address: *const JitI256, out_value: *mut JitI256) {
let env = &*env;
env.balance(address, out_value);
pub unsafe extern "C" fn env_balance(ext: *const ExtHandle, address: *const JitH256, out_value: *mut JitI256) {
let ext = &*ext;
ext.balance(address, out_value);
}
#[no_mangle]
pub unsafe extern fn env_blockhash(env: *const EnvHandle, number: *const JitI256, out_hash: *mut JitI256) {
let env = &*env;
env.blockhash(number, out_hash);
pub unsafe extern "C" fn env_blockhash(ext: *const ExtHandle, number: *const JitI256, out_hash: *mut JitH256) {
let ext = &*ext;
ext.blockhash(number, out_hash);
}
#[no_mangle]
pub unsafe extern fn env_create(env: *mut EnvHandle,
pub unsafe extern "C" fn env_create(ext: *mut ExtHandle,
io_gas: *mut u64,
endowment: *const JitI256,
init_beg: *const u8,
init_size: *const u64,
address: *mut JitI256) {
let env = &mut *env;
env.create(io_gas, endowment, init_beg, init_size, address);
init_size: u64,
address: *mut JitH256) {
let ext = &mut *ext;
ext.create(io_gas, endowment, init_beg, init_size, address);
}
#[no_mangle]
pub unsafe extern fn env_call(env: *mut EnvHandle,
pub unsafe extern "C" fn env_call(ext: *mut ExtHandle,
io_gas: *mut u64,
call_gas: *const u64,
receive_address: *const JitI256,
call_gas: u64,
receive_address: *const JitH256,
value: *const JitI256,
in_beg: *const u8,
in_size: *const u64,
in_size: u64,
out_beg: *mut u8,
out_size: *mut u64,
code_address: JitI256) -> bool {
let env = &mut *env;
env.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
out_size: u64,
code_address: *const JitH256) -> bool {
let ext = &mut *ext;
ext.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
}
#[no_mangle]
pub unsafe extern fn env_sha3(begin: *const u8, size: *const u64, out_hash: *mut JitI256) {
pub unsafe extern "C" fn env_sha3(begin: *const u8, size: u64, out_hash: *mut JitH256) {
let out_hash = &mut *out_hash;
let input = slice::from_raw_parts(begin, *size as usize);
let input = slice::from_raw_parts(begin, size as usize);
let outlen = out_hash.words.len() * 8;
let output = slice::from_raw_parts_mut(out_hash.words.as_mut_ptr() as *mut u8, outlen);
let mut sha3 = Keccak::new_sha3_256();
let mut sha3 = Keccak::new_keccak256();
sha3.update(input);
sha3.finalize(output);
}
#[no_mangle]
pub unsafe extern fn env_extcode(env: *const EnvHandle, address: *const JitI256, size: *mut u64) -> *const u8 {
let env = &*env;
env.extcode(address, size)
pub unsafe extern "C" fn env_extcode(ext: *const ExtHandle, address: *const JitH256, size: *mut u64) -> *const u8 {
let ext = &*ext;
ext.extcode(address, size)
}
#[no_mangle]
pub unsafe extern fn env_log(env: *mut EnvHandle,
pub unsafe extern "C" fn env_log(ext: *mut ExtHandle,
beg: *const u8,
size: *const u64,
topic1: *const JitI256,
topic2: *const JitI256,
topic3: *const JitI256,
topic4: *const JitI256) {
let env = &mut *env;
env.log(beg, size, topic1, topic2, topic3, topic4);
size: u64,
topic1: *const JitH256,
topic2: *const JitH256,
topic3: *const JitH256,
topic4: *const JitH256) {
let ext = &mut *ext;
ext.log(beg, size, topic1, topic2, topic3, topic4);
}
@ -292,11 +385,11 @@ pub mod ffi {
pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode;
}
// ExtHandle is not a C type, so we need to allow "improper_ctypes"
#[link(name="evmjit")]
// EnvHandle does not have to by a C type
#[allow(improper_ctypes)]
extern "C" {
pub fn evmjit_create_context(data: *mut JitRuntimeData, env: *mut EnvHandle) -> *mut JitContext;
pub fn evmjit_create_context(data: *mut JitRuntimeData, ext: *mut ExtHandle) -> *mut JitContext;
}
}
@ -304,7 +397,7 @@ pub mod ffi {
fn ffi_test() {
unsafe {
let data = evmjit_create_runtime_data();
let context = evmjit_create_context(data, &mut EnvHandle::empty());
let context = evmjit_create_context(data, &mut ExtHandle::empty());
let code = evmjit_exec(context);
assert_eq!(code, JitReturnCode::Stop);
@ -316,6 +409,16 @@ fn ffi_test() {
#[test]
fn handle_test() {
let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty());
assert_eq!(context.exec(), ReturnCode::Stop);
unsafe {
let mut context = ContextHandle::new(RuntimeDataHandle::new(), &mut ExtHandle::empty());
assert_eq!(context.exec(), ReturnCode::Stop);
}
}
#[test]
fn hash_to_int() {
let h = H256 { words:[0x0123456789abcdef, 0, 0, 0] };
let i = I256::from(h);
assert_eq!([0u64, 0, 0, 0xefcdab8967452301], i.words);
assert_eq!(H256::from(i).words, h.words);
}

42
src/action_params.rs Normal file
View File

@ -0,0 +1,42 @@
//! Evm input params.
use util::hash::*;
use util::uint::*;
use util::bytes::*;
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
/// Action (call/create) input params. Everything else should be specified in Externalities.
#[derive(Clone, Debug)]
pub struct ActionParams {
/// Address of currently executed code.
pub address: Address,
/// Sender of current part of the transaction.
pub sender: Address,
/// Transaction initiator.
pub origin: Address,
/// Gas paid up front for transaction execution
pub gas: U256,
/// Gas price.
pub gas_price: U256,
/// Transaction value.
pub value: U256,
/// Code being executed.
pub code: Bytes,
/// Input data.
pub data: Bytes
}
impl ActionParams {
pub fn new() -> ActionParams {
ActionParams {
address: Address::new(),
sender: Address::new(),
origin: Address::new(),
gas: U256::zero(),
gas_price: U256::zero(),
value: U256::zero(),
code: vec![],
data: vec![],
}
}
}

View File

@ -5,3 +5,8 @@ pub type LogBloom = H2048;
/// Constant 2048-bit datum for 0. Often used as a default.
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.
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
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 {
self.block.header.set_extra_data(extra_data);
Ok(())
@ -142,7 +142,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
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 not a direct ancestor (use last_hashes for that)
@ -169,13 +169,13 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
/// If valid, it will be executed, and archived together with the receipt.
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
let env_info = self.env_info();
match self.block.state.apply(&env_info, self.engine, &t, true) {
Ok(x) => {
match self.block.state.apply(&env_info, self.engine, &t) {
Ok(receipt) => {
self.block.archive_set.insert(h.unwrap_or_else(||t.hash()));
self.block.archive.push(Entry { transaction: t, receipt: x.receipt });
self.block.archive.push(Entry { transaction: t, receipt: receipt });
Ok(&self.block.archive.last().unwrap().receipt)
}
Err(x) => Err(x)
Err(x) => Err(From::from(x))
}
}

View File

@ -2,10 +2,11 @@ pub use util::*;
pub use basic_types::*;
pub use error::*;
pub use env_info::*;
pub use evm_schedule::*;
pub use views::*;
pub use builtin::*;
pub use header::*;
pub use account::*;
pub use transaction::*;
pub use receipt::*;
pub use log_entry::*;
pub use receipt::*;
pub use action_params::*;

View File

@ -1,6 +1,7 @@
use common::*;
use block::Block;
use spec::Spec;
use evm::Schedule;
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
/// Provides hooks into each of the major parts of block import.
@ -22,7 +23,7 @@ pub trait Engine : Sync + Send {
fn spec(&self) -> &Spec;
/// Get the EVM schedule for the given `env_info`.
fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule;
fn schedule(&self, env_info: &EnvInfo) -> Schedule;
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }

View File

@ -22,3 +22,25 @@ pub struct EnvInfo {
/// The gas used.
pub gas_used: U256,
}
impl EnvInfo {
pub fn new() -> EnvInfo {
EnvInfo {
number: 0,
author: Address::new(),
timestamp: 0,
difficulty: U256::zero(),
gas_limit: U256::zero(),
last_hashes: vec![],
gas_used: U256::zero()
}
}
}
/// TODO: it should be the other way around.
/// `new` should call `default`.
impl Default for EnvInfo {
fn default() -> Self {
EnvInfo::new()
}
}

View File

@ -11,11 +11,33 @@ pub struct Mismatch<T: fmt::Debug> {
#[derive(Debug, PartialEq, Eq)]
pub struct OutOfBounds<T: fmt::Debug> {
pub min: T,
pub max: T,
pub min: Option<T>,
pub max: Option<T>,
pub found: T,
}
/// Result of executing the transaction.
#[derive(PartialEq, Debug)]
pub enum ExecutionError {
/// Returned when block (gas_used + gas) > gas_limit.
///
/// If gas =< gas_limit, upstream may try to execute the transaction
/// in next block.
BlockGasLimitReached { gas_limit: U256, gas_used: U256, gas: U256 },
/// Returned when transaction nonce does not match state nonce.
InvalidNonce { expected: U256, is: U256 },
/// Returned when cost of transaction (value + gas_price * gas) exceeds
/// current sender balance.
NotEnoughCash { required: U256, is: U256 },
/// Returned when internal evm error occurs.
Internal
}
#[derive(Debug)]
pub enum TransactionError {
InvalidGasLimit(OutOfBounds<U256>),
}
#[derive(Debug, PartialEq, Eq)]
pub enum BlockError {
TooManyUncles(OutOfBounds<usize>),
@ -65,6 +87,14 @@ pub enum Error {
Util(UtilError),
Block(BlockError),
UnknownEngineName(String),
Execution(ExecutionError),
Transaction(TransactionError),
}
impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Error {
Error::Transaction(err)
}
}
impl From<BlockError> for Error {
@ -73,6 +103,24 @@ impl From<BlockError> for Error {
}
}
impl From<ExecutionError> for Error {
fn from(err: ExecutionError) -> Error {
Error::Execution(err)
}
}
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.
/*#![feature(concat_idents)]
macro_rules! assimilate {

View File

@ -2,6 +2,7 @@ use common::*;
use block::*;
use spec::*;
use engine::*;
use evm::Schedule;
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// mainnet chains in the Olympic, Frontier and Homestead eras.
@ -26,7 +27,7 @@ impl Engine for Ethash {
/// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
fn spec(&self) -> &Spec { &self.spec }
fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
/// Apply the block reward on finalisation of the block.
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
@ -45,7 +46,7 @@ impl Engine for Ethash {
}
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
if header.difficulty < min_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
@ -54,12 +55,12 @@ impl Engine for Ethash {
Ok(())
}
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// TODO: Verify seal (full)
Ok(())
}
fn verify_block_final(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
fn verify_block_final(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// Check difficulty is correct given the two timestamps.
let expected_difficulty = self.calculate_difficuty(header, parent);
if header.difficulty != expected_difficulty {
@ -69,12 +70,12 @@ impl Engine for Ethash {
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: 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(())
}
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> result::Result<(), Error> { Ok(()) }
}
impl Ethash {

View File

@ -12,19 +12,19 @@ pub use self::denominations::*;
use super::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.
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.
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.
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.
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)]
mod tests {

29
src/evm/evm.rs Normal file
View File

@ -0,0 +1,29 @@
//! Evm interface.
use common::*;
use evm::Ext;
/// Evm errors.
#[derive(Debug)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
/// transaction execution. But it does not mean that transaction
/// was invalid. Balance still should be transfered and nonce
/// should be increased.
OutOfGas,
/// Returned on evm internal error. Should never be ignored during development.
/// Likely to cause consensus issues.
Internal,
}
/// Evm result.
///
/// Returns gas_left if execution is successfull, otherwise error.
pub type Result = result::Result<U256, Error>;
/// Evm interface.
pub trait Evm {
/// This function should be used to execute transaction.
fn exec(&self, params: &ActionParams, ext: &mut Ext) -> Result;
}

61
src/evm/ext.rs Normal file
View File

@ -0,0 +1,61 @@
//! Interface for Evm externalities.
use util::hash::*;
use util::uint::*;
use util::bytes::*;
use evm::{Schedule, Error};
use env_info::*;
// TODO: replace all u64 with u256
pub trait Ext {
/// Returns a value for given key.
fn sload(&self, key: &H256) -> H256;
/// Stores a value for given key.
fn sstore(&mut self, key: H256, value: H256);
/// Returns address balance.
fn balance(&self, address: &Address) -> U256;
/// Returns the hash of one of the 256 most recent complete blocks.
fn blockhash(&self, number: &U256) -> H256;
/// Creates new contract.
///
/// If contract creation is successfull, return gas_left and contract address,
/// If depth is too big or transfer value exceeds balance, return None
/// Otherwise return appropriate `Error`.
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), Error>;
/// Message call.
///
/// If call is successfull, returns gas left.
/// otherwise `Error`.
fn call(&mut self,
gas: u64,
call_gas: u64,
receive_address: &Address,
value: &U256,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> Result<u64, Error>;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Vec<u8>;
/// Creates log entry with given topics and data
fn log(&mut self, topics: Vec<H256>, data: Bytes);
/// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high.
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, Error>;
/// Should be called when contract commits suicide.
fn suicide(&mut self);
/// Returns schedule.
fn schedule(&self) -> &Schedule;
/// Returns environment info.
fn env_info(&self) -> &EnvInfo;
}

25
src/evm/factory.rs Normal file
View File

@ -0,0 +1,25 @@
//! Evm factory.
use evm::Evm;
/// Evm factory. Creates appropriate Evm.
pub struct Factory;
impl Factory {
/// Returns jit vm
#[cfg(feature = "jit")]
pub fn create() -> Box<Evm> {
Box::new(super::jit::JitEvm)
}
/// Returns native rust evm
#[cfg(not(feature = "jit"))]
pub fn create() -> Box<Evm> {
unimplemented!();
}
}
#[test]
fn test_create_vm() {
let _vm = Factory::create();
}

391
src/evm/jit.rs Normal file
View File

@ -0,0 +1,391 @@
//! Just in time compiler execution environment.
use common::*;
use evmjit;
use evm;
/// Ethcore representation of evmjit runtime data.
struct RuntimeData {
gas: U256,
gas_price: U256,
call_data: Vec<u8>,
address: Address,
caller: Address,
origin: Address,
call_value: U256,
author: Address,
difficulty: U256,
gas_limit: U256,
number: u64,
timestamp: u64,
code: Vec<u8>
}
impl RuntimeData {
fn new() -> RuntimeData {
RuntimeData {
gas: U256::zero(),
gas_price: U256::zero(),
call_data: vec![],
address: Address::new(),
caller: Address::new(),
origin: Address::new(),
call_value: U256::zero(),
author: Address::new(),
difficulty: U256::zero(),
gas_limit: U256::zero(),
number: 0,
timestamp: 0,
code: vec![]
}
}
}
/// Should be used to convert jit types to ethcore
trait FromJit<T>: Sized {
fn from_jit(input: T) -> Self;
}
/// Should be used to covert ethcore types to jit
trait IntoJit<T> {
fn into_jit(self) -> T;
}
impl<'a> FromJit<&'a evmjit::I256> for U256 {
fn from_jit(input: &'a evmjit::I256) -> Self {
unsafe {
let mut res: U256 = mem::uninitialized();
ptr::copy(input.words.as_ptr(), res.0.as_mut_ptr(), 4);
res
}
}
}
impl<'a> FromJit<&'a evmjit::I256> for H256 {
fn from_jit(input: &'a evmjit::I256) -> Self {
let u = U256::from_jit(input);
H256::from(&u)
}
}
impl<'a> FromJit<&'a evmjit::I256> for Address {
fn from_jit(input: &'a evmjit::I256) -> Self {
Address::from(H256::from_jit(input))
}
}
impl<'a> FromJit<&'a evmjit::H256> for H256 {
fn from_jit(input: &'a evmjit::H256) -> Self {
H256::from_jit(&evmjit::I256::from(input.clone()))
}
}
impl<'a> FromJit<&'a evmjit::H256> for Address {
fn from_jit(input: &'a evmjit::H256) -> Self {
Address::from(H256::from_jit(input))
}
}
impl IntoJit<evmjit::I256> for U256 {
fn into_jit(self) -> evmjit::I256 {
unsafe {
let mut res: evmjit::I256 = mem::uninitialized();
ptr::copy(self.0.as_ptr(), res.words.as_mut_ptr(), 4);
res
}
}
}
impl IntoJit<evmjit::I256> for H256 {
fn into_jit(self) -> evmjit::I256 {
let mut ret = [0; 4];
for i in 0..self.bytes().len() {
let rev = self.bytes().len() - 1 - i;
let pos = rev / 8;
ret[pos] += (self.bytes()[i] as u64) << (rev % 8) * 8;
}
evmjit::I256 { words: ret }
}
}
impl IntoJit<evmjit::H256> for H256 {
fn into_jit(self) -> evmjit::H256 {
let i: evmjit::I256 = self.into_jit();
From::from(i)
}
}
impl IntoJit<evmjit::I256> for Address {
fn into_jit(self) -> evmjit::I256 {
H256::from(self).into_jit()
}
}
impl IntoJit<evmjit::H256> for Address {
fn into_jit(self) -> evmjit::H256 {
H256::from(self).into_jit()
}
}
impl IntoJit<evmjit::RuntimeDataHandle> for RuntimeData {
fn into_jit(self) -> evmjit::RuntimeDataHandle {
let mut data = evmjit::RuntimeDataHandle::new();
assert!(self.gas <= U256::from(u64::max_value()), "evmjit gas must be lower than 2 ^ 64");
assert!(self.gas_price <= U256::from(u64::max_value()), "evmjit gas_price must be lower than 2 ^ 64");
data.gas = self.gas.low_u64() as i64;
data.gas_price = self.gas_price.low_u64() as i64;
data.call_data = self.call_data.as_ptr();
data.call_data_size = self.call_data.len() as u64;
mem::forget(self.call_data);
data.address = self.address.into_jit();
data.caller = self.caller.into_jit();
data.origin = self.origin.into_jit();
data.call_value = self.call_value.into_jit();
data.author = self.author.into_jit();
data.difficulty = self.difficulty.into_jit();
data.gas_limit = self.gas_limit.into_jit();
data.number = self.number;
data.timestamp = self.timestamp as i64;
data.code = self.code.as_ptr();
data.code_size = self.code.len() as u64;
data.code_hash = self.code.sha3().into_jit();
mem::forget(self.code);
data
}
}
/// Externalities adapter. Maps callbacks from evmjit to externalities trait.
///
/// Evmjit doesn't have to know about children execution failures.
/// This adapter 'catches' them and moves upstream.
struct ExtAdapter<'a> {
ext: &'a mut evm::Ext,
err: &'a mut Option<evm::Error>
}
impl<'a> ExtAdapter<'a> {
fn new(ext: &'a mut evm::Ext, err: &'a mut Option<evm::Error>) -> Self {
ExtAdapter {
ext: ext,
err: err
}
}
}
impl<'a> evmjit::Ext for ExtAdapter<'a> {
fn sload(&self, index: *const evmjit::I256, out_value: *mut evmjit::I256) {
unsafe {
let i = H256::from_jit(&*index);
let o = self.ext.sload(&i);
*out_value = o.into_jit();
}
}
fn sstore(&mut self, index: *const evmjit::I256, value: *const evmjit::I256) {
unsafe {
self.ext.sstore(H256::from_jit(&*index), H256::from_jit(&*value));
}
}
fn balance(&self, address: *const evmjit::H256, out_value: *mut evmjit::I256) {
unsafe {
let a = Address::from_jit(&*address);
let o = self.ext.balance(&a);
*out_value = o.into_jit();
}
}
fn blockhash(&self, number: *const evmjit::I256, out_hash: *mut evmjit::H256) {
unsafe {
let n = U256::from_jit(&*number);
let o = self.ext.blockhash(&n);
*out_hash = o.into_jit();
}
}
fn create(&mut self,
io_gas: *mut u64,
endowment: *const evmjit::I256,
init_beg: *const u8,
init_size: u64,
address: *mut evmjit::H256) {
unsafe {
match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
Ok((gas_left, opt)) => {
*io_gas = gas_left;
if let Some(addr) = opt {
*address = addr.into_jit();
}
},
Err(err @ evm::Error::OutOfGas) => {
*self.err = Some(err);
// hack to propagate `OutOfGas` to evmjit and stop
// the execution immediately.
// Works, cause evmjit uses i64, not u64
*io_gas = -1i64 as u64
},
Err(err) => *self.err = Some(err)
}
}
}
fn call(&mut self,
io_gas: *mut u64,
call_gas: u64,
receive_address: *const evmjit::H256,
value: *const evmjit::I256,
in_beg: *const u8,
in_size: u64,
out_beg: *mut u8,
out_size: u64,
code_address: *const evmjit::H256) -> bool {
unsafe {
let res = self.ext.call(*io_gas,
call_gas,
&Address::from_jit(&*receive_address),
&U256::from_jit(&*value),
slice::from_raw_parts(in_beg, in_size as usize),
&Address::from_jit(&*code_address),
slice::from_raw_parts_mut(out_beg, out_size as usize));
match res {
Ok(gas_left) => {
*io_gas = gas_left;
true
},
Err(err @ evm::Error::OutOfGas) => {
*self.err = Some(err);
// hack to propagate `OutOfGas` to evmjit and stop
// the execution immediately.
// Works, cause evmjit uses i64, not u64
*io_gas = -1i64 as u64;
false
},
Err(err) => {
*self.err = Some(err);
false
}
}
}
}
fn log(&mut self,
beg: *const u8,
size: u64,
topic1: *const evmjit::H256,
topic2: *const evmjit::H256,
topic3: *const evmjit::H256,
topic4: *const evmjit::H256) {
unsafe {
let mut topics = vec![];
if !topic1.is_null() {
topics.push(H256::from_jit(&*topic1));
}
if !topic2.is_null() {
topics.push(H256::from_jit(&*topic2));
}
if !topic3.is_null() {
topics.push(H256::from_jit(&*topic3));
}
if !topic4.is_null() {
topics.push(H256::from_jit(&*topic4));
}
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
self.ext.log(topics, bytes_ref.to_vec());
}
}
fn extcode(&self, address: *const evmjit::H256, size: *mut u64) -> *const u8 {
unsafe {
let code = self.ext.extcode(&Address::from_jit(&*address));
*size = code.len() as u64;
let ptr = code.as_ptr();
mem::forget(code);
ptr
}
}
}
pub struct JitEvm;
impl evm::Evm for JitEvm {
fn exec(&self, params: &ActionParams, ext: &mut evm::Ext) -> evm::Result {
let mut optional_err = None;
// Dirty hack. This is unsafe, but we interact with ffi, so it's justified.
let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, &mut optional_err)) };
let mut ext_handle = evmjit::ExtHandle::new(ext_adapter);
let mut data = RuntimeData::new();
data.gas = params.gas;
data.gas_price = params.gas_price;
data.call_data = params.data.clone();
data.address = params.address.clone();
data.caller = params.sender.clone();
data.origin = params.origin.clone();
data.call_value = params.value;
data.code = params.code.clone();
data.author = ext.env_info().author.clone();
data.difficulty = ext.env_info().difficulty;
data.gas_limit = ext.env_info().gas_limit;
data.number = ext.env_info().number;
data.timestamp = ext.env_info().timestamp;
let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) };
let res = context.exec();
// check in adapter if execution of children contracts failed.
if let Some(err) = optional_err {
return Err(err);
}
match res {
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
evmjit::ReturnCode::Return => ext.ret(context.gas_left(), context.output_data()).map(|gas_left| U256::from(gas_left)),
evmjit::ReturnCode::Suicide => {
// what if there is a suicide and we run out of gas just after?
ext.suicide();
Ok(U256::from(context.gas_left()))
},
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
_err => Err(evm::Error::Internal)
}
}
}
#[test]
fn test_to_and_from_u256() {
let u = U256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap();
let j = u.into_jit();
let u2 = U256::from_jit(&j);
assert_eq!(u, u2);
}
#[test]
fn test_to_and_from_h256() {
let h = H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap();
let j: ::evmjit::I256 = h.clone().into_jit();
let h2 = H256::from_jit(&j);
assert_eq!(h, h2);
let j: ::evmjit::H256 = h.clone().into_jit();
let h2 = H256::from_jit(&j);
assert_eq!(h, h2);
}
#[test]
fn test_to_and_from_address() {
let a = Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap();
let j: ::evmjit::I256 = a.clone().into_jit();
let a2 = Address::from_jit(&j);
assert_eq!(a, a2);
let j: ::evmjit::H256 = a.clone().into_jit();
let a2 = Address::from_jit(&j);
assert_eq!(a, a2);
}

16
src/evm/mod.rs Normal file
View File

@ -0,0 +1,16 @@
//! Ethereum virtual machine.
pub mod ext;
pub mod evm;
pub mod factory;
pub mod schedule;
#[cfg(feature = "jit" )]
mod jit;
#[cfg(test)]
mod tests;
pub use self::evm::{Evm, Error, Result};
pub use self::ext::Ext;
pub use self::factory::Factory;
pub use self::schedule::Schedule;

View File

@ -1,5 +1,7 @@
//! Cost schedule and other parameterisations for the EVM.
/// Definition of the cost schedule and other parameterisations for the EVM.
pub struct EvmSchedule {
pub struct Schedule {
pub exceptional_failed_code_deposit: bool,
pub have_delegate_call: bool,
pub stack_limit: usize,
@ -32,19 +34,19 @@ pub struct EvmSchedule {
pub copy_gas: usize,
}
impl EvmSchedule {
impl Schedule {
/// Schedule for the Frontier-era of the Ethereum main net.
pub fn new_frontier() -> EvmSchedule {
pub fn new_frontier() -> Schedule {
Self::new(false, false, 21000)
}
/// Schedule for the Homestead-era of the Ethereum main net.
pub fn new_homestead() -> EvmSchedule {
pub fn new_homestead() -> Schedule {
Self::new(true, true, 53000)
}
fn new(efcd: bool, hdc: bool, tcg: usize) -> EvmSchedule {
EvmSchedule{
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule{
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
stack_limit: 1024,
@ -77,4 +79,4 @@ impl EvmSchedule {
copy_gas: 3,
}
}
}
}

425
src/evm/tests.rs Normal file
View File

@ -0,0 +1,425 @@
use common::*;
use evm;
use evm::{Ext, Schedule, Factory};
struct FakeLogEntry {
topics: Vec<H256>,
data: Bytes
}
/// Fake externalities test structure.
///
/// Can't do recursive calls.
#[derive(Default)]
struct FakeExt {
store: HashMap<H256, H256>,
_balances: HashMap<Address, U256>,
blockhashes: HashMap<U256, H256>,
codes: HashMap<Address, Bytes>,
logs: Vec<FakeLogEntry>,
_suicides: HashSet<Address>,
info: EnvInfo
}
impl FakeExt {
fn new() -> Self { FakeExt::default() }
}
impl Ext for FakeExt {
fn sload(&self, key: &H256) -> H256 {
self.store.get(key).unwrap_or(&H256::new()).clone()
}
fn sstore(&mut self, key: H256, value: H256) {
self.store.insert(key, value);
}
fn balance(&self, _address: &Address) -> U256 {
unimplemented!();
}
fn blockhash(&self, number: &U256) -> H256 {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}
fn create(&mut self, _gas: u64, _value: &U256, _code: &[u8]) -> result::Result<(u64, Option<Address>), evm::Error> {
unimplemented!();
}
fn call(&mut self,
_gas: u64,
_call_gas: u64,
_receive_address: &Address,
_value: &U256,
_data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> result::Result<u64, evm::Error> {
unimplemented!();
}
fn extcode(&self, address: &Address) -> Vec<u8> {
self.codes.get(address).unwrap_or(&Bytes::new()).clone()
}
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
self.logs.push(FakeLogEntry {
topics: topics,
data: data
});
}
fn ret(&mut self, _gas: u64, _data: &[u8]) -> result::Result<u64, evm::Error> {
unimplemented!();
}
fn suicide(&mut self) {
unimplemented!();
}
fn schedule(&self) -> &Schedule {
unimplemented!();
}
fn env_info(&self) -> &EnvInfo {
&self.info
}
}
#[test]
fn test_add() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_988));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap());
}
#[test]
fn test_sha3() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "6000600020600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_961));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap());
}
#[test]
fn test_address() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "30600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap());
}
#[test]
fn test_origin() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "32600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.origin = origin.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
}
#[test]
fn test_sender() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "33600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
}
#[test]
fn test_extcodecopy() {
// 33 - sender
// 3b - extcodesize
// 60 00 - push 0
// 60 00 - push 0
// 33 - sender
// 3c - extcodecopy
// 60 00 - push 0
// 51 - load word from memory
// 60 00 - push 0
// 55 - sstore
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "333b60006000333c600051600055".from_hex().unwrap();
let sender_code = "6005600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.codes.insert(sender, sender_code);
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_935));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("6005600055000000000000000000000000000000000000000000000000000000").unwrap());
}
#[test]
fn test_log_empty() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "60006000a0".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(99_619));
assert_eq!(ext.logs.len(), 1);
assert_eq!(ext.logs[0].topics.len(), 0);
assert_eq!(ext.logs[0].data, vec![]);
}
#[test]
fn test_log_sender() {
// 60 ff - push ff
// 60 00 - push 00
// 53 - mstore
// 33 - sender
// 60 20 - push 20
// 60 00 - push 0
// a1 - log with 1 topic
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let code = "60ff6000533360206000a1".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(98_974));
assert_eq!(ext.logs.len(), 1);
assert_eq!(ext.logs[0].topics.len(), 1);
assert_eq!(ext.logs[0].topics[0], H256::from_str("000000000000000000000000cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap());
}
#[test]
fn test_blockhash() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "600040600055".from_hex().unwrap();
let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_974));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash);
}
#[test]
fn test_calldataload() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "600135600055".from_hex().unwrap();
let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap();
let mut params = ActionParams::new();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = code;
params.data = data;
let mut ext = FakeExt::new();
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_991));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").unwrap());
}
#[test]
fn test_author() {
let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "41600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.info.author = author;
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap());
}
#[test]
fn test_timestamp() {
let timestamp = 0x1234;
let code = "42600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.info.timestamp = timestamp;
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
#[test]
fn test_number() {
let number = 0x1234;
let code = "43600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.info.number = number;
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
#[test]
fn test_difficulty() {
let difficulty = U256::from(0x1234);
let code = "44600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.info.difficulty = difficulty;
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
#[test]
fn test_gas_limit() {
let gas_limit = U256::from(0x1234);
let code = "45600055".from_hex().unwrap();
let mut params = ActionParams::new();
params.gas = U256::from(100_000);
params.code = code;
let mut ext = FakeExt::new();
ext.info.gas_limit = gas_limit;
let gas_left = {
let vm = Factory::create();
vm.exec(&params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}

564
src/executive.rs Normal file
View File

@ -0,0 +1,564 @@
//! Transaction Execution environment.
use common::*;
use state::*;
use engine::*;
use evm::{self, Schedule, Factory, Ext};
/// Returns new address created from address and given nonce.
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
let mut stream = RlpStream::new_list(2);
stream.append(address);
stream.append(nonce);
From::from(stream.out().sha3())
}
/// State changes which should be applied in finalize,
/// after transaction is fully executed.
struct Substate {
/// Any accounts that have suicided.
suicides: HashSet<Address>,
/// Any logs.
logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero->zero.
refunds_count: U256,
}
impl Substate {
/// Creates new substate.
fn new() -> Self {
Substate {
suicides: HashSet::new(),
logs: vec![],
refunds_count: U256::zero(),
}
}
}
/// Transaction execution receipt.
pub struct Executed {
/// Gas paid up front for execution of transaction.
pub gas: U256,
/// Gas used during execution of transaction.
pub gas_used: U256,
/// Gas refunded after the execution of transaction.
/// To get gas that was required up front, add `refunded` and `gas_used`.
pub refunded: U256,
/// Cumulative gas used in current block so far.
///
/// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)`
///
/// where `tn` is current transaction.
pub cumulative_gas_used: U256,
/// Vector of logs generated by transaction.
pub logs: Vec<LogEntry>,
/// Execution ended running out of gas.
pub out_of_gas: bool,
}
/// Transaction execution result.
pub type ExecutionResult = Result<Executed, ExecutionError>;
/// Transaction executor.
pub struct Executive<'a> {
state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
}
impl<'a> Executive<'a> {
/// Basic constructor.
pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine) -> Self {
Executive::new_with_depth(state, info, engine, 0)
}
/// Populates executive from parent properties. Increments executive depth.
fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self {
Executive::new_with_depth(state, info, engine, depth + 1)
}
/// Helper constructor. Should be used to create `Executive` with desired depth.
/// Private.
fn new_with_depth(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self {
Executive {
state: state,
info: info,
engine: engine,
depth: depth,
}
}
/// This funtion should be used to execute transaction.
pub fn transact(&mut self, t: &Transaction) -> Result<Executed, Error> {
// TODO: validate transaction signature ?/ sender
let sender = try!(t.sender());
let nonce = self.state.nonce(&sender);
// validate transaction nonce
if t.nonce != nonce {
return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce }));
}
// validate if transaction fits into given block
if self.info.gas_used + t.gas > self.info.gas_limit {
return Err(From::from(ExecutionError::BlockGasLimitReached {
gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used,
gas: t.gas
}));
}
// TODO: we might need bigints here, or at least check overflows.
let balance = self.state.balance(&sender);
let gas_cost = t.gas * t.gas_price;
let total_cost = t.value + gas_cost;
// avoid unaffordable transactions
if balance < total_cost {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: balance }));
}
// NOTE: there can be no invalid transactions from this point.
self.state.inc_nonce(&sender);
let mut substate = Substate::new();
let backup = self.state.clone();
let res = match t.action() {
&Action::Create => {
let params = ActionParams {
address: contract_address(&sender, &nonce),
sender: sender.clone(),
origin: sender.clone(),
gas: t.gas,
gas_price: t.gas_price,
value: t.value,
code: t.data.clone(),
data: vec![],
};
self.create(&params, &mut substate)
},
&Action::Call(ref address) => {
let params = ActionParams {
address: address.clone(),
sender: sender.clone(),
origin: sender.clone(),
gas: t.gas,
gas_price: t.gas_price,
value: t.value,
code: self.state.code(address).unwrap_or(vec![]),
data: t.data.clone(),
};
self.call(&params, &mut substate, &mut [])
}
};
// finalize here!
Ok(try!(self.finalize(t, substate, backup, res)))
}
/// Calls contract function with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`.
fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result {
// at first, transfer value to destination
self.state.transfer_balance(&params.sender, &params.address, &params.value);
if self.engine.is_builtin(&params.address) {
// if destination is builtin, try to execute it
let cost = self.engine.cost_of_builtin(&params.address, &params.data);
match cost <= params.gas {
true => {
self.engine.execute_builtin(&params.address, &params.data, output);
Ok(params.gas - cost)
},
false => Err(evm::Error::OutOfGas)
}
} else if params.code.len() > 0 {
// if destination is a contract, do normal message call
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
let evm = Factory::create();
evm.exec(&params, &mut ext)
} else {
// otherwise, nothing
Ok(params.gas)
}
}
/// Creates contract with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate.
fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result {
// at first create new contract
self.state.new_contract(&params.address);
// then transfer value to it
self.state.transfer_balance(&params.sender, &params.address, &params.value);
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
let evm = Factory::create();
evm.exec(&params, &mut ext)
}
/// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &Transaction, substate: Substate, backup: State, result: evm::Result) -> ExecutionResult {
match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(evm::Error::OutOfGas) => {
*self.state = backup;
Ok(Executed {
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
out_of_gas: true,
})
},
Ok(gas_left) => {
let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero
let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count;
// refunds from contract suicides
let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len());
// real ammount to refund
let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left;
let refund_value = refund * t.gas_price;
self.state.add_balance(&t.sender().unwrap(), &refund_value);
// fees earned by author
let fees = (t.gas - refund) * t.gas_price;
let author = &self.info.author;
self.state.add_balance(author, &fees);
// perform suicides
for address in substate.suicides.iter() {
self.state.kill_account(address);
}
let gas_used = t.gas - gas_left;
Ok(Executed {
gas: t.gas,
gas_used: gas_used,
refunded: refund,
cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs,
out_of_gas: false,
})
}
}
}
}
/// Policy for handling output data on `RETURN` opcode.
enum OutputPolicy<'a> {
/// Return reference to fixed sized output.
/// Used for message calls.
Return(&'a mut [u8]),
/// Init new contract as soon as `RETURN` is called.
InitContract
}
/// Implementation of evm Externalities.
struct Externalities<'a> {
state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
params: &'a ActionParams,
substate: &'a mut Substate,
schedule: Schedule,
output: OutputPolicy<'a>
}
impl<'a> Externalities<'a> {
/// Basic `Externalities` constructor.
fn new(state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
params: &'a ActionParams,
substate: &'a mut Substate,
output: OutputPolicy<'a>) -> Self {
Externalities {
state: state,
info: info,
engine: engine,
depth: depth,
params: params,
substate: substate,
schedule: engine.schedule(info),
output: output
}
}
/// Creates `Externalities` from `Executive`.
fn from_executive(e: &'a mut Executive, params: &'a ActionParams, substate: &'a mut Substate, output: OutputPolicy<'a>) -> Self {
Self::new(e.state, e.info, e.engine, e.depth, params, substate, output)
}
}
impl<'a> Ext for Externalities<'a> {
fn sload(&self, key: &H256) -> H256 {
self.state.storage_at(&self.params.address, key)
}
fn sstore(&mut self, key: H256, value: H256) {
// if SSTORE nonzero -> zero, increment refund count
if value == H256::new() && self.state.storage_at(&self.params.address, &key) != H256::new() {
self.substate.refunds_count = self.substate.refunds_count + U256::one();
}
self.state.set_storage(&self.params.address, key, value)
}
fn balance(&self, address: &Address) -> U256 {
self.state.balance(address)
}
fn blockhash(&self, number: &U256) -> H256 {
match *number < U256::from(self.info.number) {
false => H256::from(&U256::zero()),
true => {
let index = U256::from(self.info.number) - *number - U256::one();
self.info.last_hashes[index.low_u32() as usize].clone()
}
}
}
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 {
return Ok((gas, None));
}
// create new contract address
let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address));
// prepare the params
let params = ActionParams {
address: address.clone(),
sender: self.params.address.clone(),
origin: self.params.origin.clone(),
gas: U256::from(gas),
gas_price: self.params.gas_price.clone(),
value: value.clone(),
code: code.to_vec(),
data: vec![],
};
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.state.inc_nonce(&self.params.address);
ex.create(&params, self.substate).map(|gas_left| (gas_left.low_u64(), Some(address)))
}
fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<u64, evm::Error> {
let mut gas_cost = call_gas;
let mut call_gas = call_gas;
let is_call = receive_address == code_address;
if is_call && self.state.code(&code_address).is_none() {
gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
}
if *value > U256::zero() {
assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible");
gas_cost = gas_cost + self.schedule.call_value_transfer_gas as u64;
call_gas = call_gas + self.schedule.call_stipend as u64;
}
if gas_cost > gas {
return Err(evm::Error::OutOfGas)
}
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 {
return Ok(gas + call_gas)
}
let params = ActionParams {
address: receive_address.clone(),
sender: self.params.address.clone(),
origin: self.params.origin.clone(),
gas: U256::from(call_gas),
gas_price: self.params.gas_price.clone(),
value: value.clone(),
code: self.state.code(code_address).unwrap_or(vec![]),
data: data.to_vec(),
};
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())
}
fn extcode(&self, address: &Address) -> Vec<u8> {
self.state.code(address).unwrap_or(vec![])
}
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, evm::Error> {
match &mut self.output {
&mut OutputPolicy::Return(ref mut slice) => unsafe {
let len = cmp::min(slice.len(), data.len());
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
Ok(gas)
},
&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);
}
let mut code = vec![];
code.reserve(data.len());
unsafe {
ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
code.set_len(data.len());
}
let address = &self.params.address;
self.state.init_code(address, code);
Ok(gas - return_cost)
}
}
}
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
let address = self.params.address.clone();
self.substate.logs.push(LogEntry::new(address, topics, data));
}
fn suicide(&mut self) {
let address = self.params.address.clone();
self.substate.suicides.insert(address);
}
fn schedule(&self) -> &Schedule {
&self.schedule
}
fn env_info(&self) -> &EnvInfo {
&self.info
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::*;
use state::*;
use ethereum;
use null_engine::*;
use super::Substate;
#[test]
fn test_contract_address() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let expected_address = Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap();
assert_eq!(expected_address, contract_address(&address, &U256::from(88)));
}
#[test]
// TODO: replace params with transactions!
fn test_executive() {
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.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 mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, engine.deref());
let _res = ex.create(&params, &mut substate);
}
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));
// TODO: just test state root.
}
#[test]
fn test_create_contract() {
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
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(0x174876e800u64);
params.code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036000f0600055".from_hex().unwrap();
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 mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, engine.deref());
let _res = ex.create(&params, &mut substate);
println!("res: {:?}", _res);
}
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);
}
#[test]
fn test_recursive_bomb1() {
// 60 01 - push 1
// 60 00 - push 0
// 54 - sload
// 01 - add
// 60 00 - push 0
// 55 - sstore
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 30 - load address
// 60 e0 - push e0
// 5a - get gas
// 03 - sub
// f1 - message call (self in this case)
// 60 01 - push 1
// 55 - store
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.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 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);
}
}
}

View File

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

View File

@ -83,25 +83,30 @@ extern crate time;
extern crate env_logger;
#[cfg(feature = "jit" )]
extern crate evmjit;
extern crate ethcore_util as util;
pub mod common;
pub mod basic_types;
pub mod executive;
pub mod error;
pub mod log_entry;
pub mod env_info;
pub mod engine;
pub mod state;
pub mod account;
pub mod action_params;
pub mod header;
pub mod transaction;
pub mod receipt;
pub mod null_engine;
pub mod evm_schedule;
pub mod builtin;
pub mod spec;
pub mod views;
pub mod blockchain;
pub mod extras;
pub mod evm;
pub mod client;
pub mod sync;
pub mod block;

62
src/log_entry.rs Normal file
View File

@ -0,0 +1,62 @@
use util::*;
use basic_types::LogBloom;
/// A single log's entry.
pub struct LogEntry {
pub address: Address,
pub topics: Vec<H256>,
pub data: Bytes,
}
impl RlpStandard for LogEntry {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(3);
s.append(&self.address);
s.append(&self.topics);
s.append(&self.data);
}
}
impl LogEntry {
pub fn bloom(&self) -> LogBloom {
self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3()))
}
/// Create a new log entry.
pub fn new(address: Address, topics: Vec<H256>, data: Bytes) -> LogEntry {
LogEntry {
address: address,
topics: topics,
data: data
}
}
/// Returns reference to address.
pub fn address(&self) -> &Address {
&self.address
}
/// Returns reference to topics.
pub fn topics(&self) -> &Vec<H256> {
&self.topics
}
/// Returns reference to data.
pub fn data(&self) -> &Bytes {
&self.data
}
}
#[cfg(test)]
mod tests {
use util::*;
use super::LogEntry;
#[test]
fn test_empty_log_bloom() {
let bloom = H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let log = LogEntry::new(address, vec![], vec![]);
assert_eq!(log.bloom(), bloom);
}
}

View File

@ -1,6 +1,6 @@
use engine::Engine;
use spec::Spec;
use evm_schedule::EvmSchedule;
use evm::Schedule;
use env_info::EnvInfo;
/// An engine which does not provide any consensus mechanism.
@ -17,5 +17,5 @@ impl NullEngine {
impl Engine for NullEngine {
fn name(&self) -> &str { "NullEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
}

View File

@ -1,27 +1,6 @@
use util::*;
use basic_types::LogBloom;
/// A single log's entry.
pub struct LogEntry {
pub address: Address,
pub topics: Vec<H256>,
pub data: Bytes,
}
impl RlpStandard for LogEntry {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(3);
s.append(&self.address);
s.append(&self.topics);
s.append(&self.data);
}
}
impl LogEntry {
pub fn bloom(&self) -> LogBloom {
self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3()))
}
}
use log_entry::LogEntry;
/// Information describing execution of a transaction.
pub struct Receipt {
@ -31,6 +10,17 @@ pub struct Receipt {
pub logs: Vec<LogEntry>,
}
impl Receipt {
pub fn new(state_root: H256, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
Receipt {
state_root: state_root,
gas_used: gas_used,
log_bloom: logs.iter().fold(LogBloom::new(), |mut b, l| { b |= &l.bloom(); b }),
logs: logs,
}
}
}
impl RlpStandard for Receipt {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(4);

View File

@ -1,12 +1,8 @@
use common::*;
use engine::Engine;
use executive::Executive;
/// Information concerning the result of the `State::apply` operation.
pub struct ApplyInfo {
pub receipt: Receipt,
}
pub type ApplyResult = Result<ApplyInfo, Error>;
pub type ApplyResult = Result<Receipt, Error>;
/// Representation of the entire state of all accounts in the system.
pub struct State {
@ -134,8 +130,10 @@ impl State {
/// Execute a given transaction.
/// This will change the state accordingly.
pub fn apply(&mut self, _env_info: &EnvInfo, _engine: &Engine, _t: &Transaction, _is_permanent: bool) -> ApplyResult {
unimplemented!();
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
let e = try!(Executive::new(self, env_info, engine).transact(t));
self.commit();
Ok(Receipt::new(self.root().clone(), e.gas_used, e.logs))
}
/// Convert into a JSON representation.
@ -221,6 +219,12 @@ impl State {
}
}
impl Clone for State {
fn clone(&self) -> Self {
State::from_existing(self.db.clone(), self.root.clone(), self.account_start_nonce.clone())
}
}
#[cfg(test)]
mod tests {
@ -377,4 +381,4 @@ fn create_empty() {
assert_eq!(s.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
}
}
}

View File

@ -1,40 +1,60 @@
use util::*;
use basic_types::*;
use error::*;
use evm::Schedule;
pub enum Action {
Create,
Call(Address),
}
/// A set of information describing an externally-originating message call
/// or contract creation operation.
pub struct Transaction {
nonce: U256,
gas_price: U256,
gas: U256,
to: Option<Address>,
value: U256,
data: Bytes,
pub nonce: U256,
pub gas_price: U256,
pub gas: U256,
pub action: Action,
pub value: U256,
pub data: Bytes,
// signature
pub v: u8,
pub r: U256,
pub s: U256,
hash: RefCell<Option<H256>>, //TODO: make this private
}
impl Transaction {
/// Is this transaction meant to create a contract?
pub fn is_contract_creation(&self) -> bool {
self.to.is_none()
/// Append object into RLP stream, optionally with or without the signature.
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.gas_price);
s.append(&self.gas);
match self.action {
Action::Create => s.append_empty_data(),
Action::Call(ref to) => s.append(to),
};
s.append(&self.value);
s.append(&self.data);
match with_seal {
Seal::With => { s.append(&(self.v as u16)).append(&self.r).append(&self.s); },
_ => {}
}
}
/// Is this transaction meant to send a message?
pub fn is_message_call(&self) -> bool {
!self.is_contract_creation()
/// 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) {
s.append_list(6);
s.append(&self.nonce);
s.append(&self.gas_price);
s.append(&self.gas);
s.append(&self.to);
s.append(&self.value);
s.append(&self.data);
}
fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_opt(s, Seal::With) }
}
impl Transaction {
@ -54,36 +74,181 @@ impl Transaction {
pub fn note_dirty(&self) {
*self.hash.borrow_mut() = None;
}
/// Returns transaction type.
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.
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 Encodable for Transaction {
fn encode<E>(&self, encoder: &mut E) where E: Encoder {
encoder.emit_list(| e | {
self.nonce.encode(e);
self.gas_price.encode(e);
self.gas.encode(e);
self.to.encode(e);
self.value.encode(e);
self.data.encode(e);
})
impl Decodable for Action {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
if rlp.is_empty() {
Ok(Action::Create)
} else {
Ok(Action::Call(try!(rlp.as_val())))
}
}
}
impl Decodable for Transaction {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = try!(decoder.as_list());
let transaction = Transaction {
if d.len() != 9 {
return Err(DecoderError::RlpIncorrectListLen);
}
Ok(Transaction {
nonce: try!(Decodable::decode(&d[0])),
gas_price: try!(Decodable::decode(&d[1])),
gas: try!(Decodable::decode(&d[2])),
to: try!(Decodable::decode(&d[3])),
action: try!(Decodable::decode(&d[3])),
value: try!(Decodable::decode(&d[4])),
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)
};
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<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<
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: 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();
@ -82,10 +82,10 @@ pub fn verify_block_final<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
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 {
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
@ -121,18 +121,18 @@ pub fn verify_block_final<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<
/// 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::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 {
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 })));
}
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: min_gas_limit, max: BAD_U256, 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 = 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: 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() })));
}
Ok(())
}
@ -143,10 +143,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() })))
}
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 {
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(())
}
@ -339,27 +339,27 @@ mod tests {
header.gas_limit = min_gas_limit - From::from(1);
check_fail(verify_block_basic(&create_test_block(&header), engine.deref()),
InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: BAD_U256, found: header.gas_limit }));
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: BlockNumber::max_value(), min: 0, found: header.number }));
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: header.gas_limit, min: From::from(0), found: header.gas_used }));
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: engine.maximum_extra_data_size(), min: 0, found: header.extra_data.len() }));
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: engine.maximum_extra_data_size(), min: 0, found: header.extra_data.len() }));
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();
@ -382,11 +382,11 @@ mod tests {
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: u64::max_value(), min: parent.timestamp + 1, found: header.timestamp }));
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: BlockNumber::max_value(), min: parent.number + 1, found: header.number }));
InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number }));
}
}