diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..84843f000 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "res/ethereum/tests"] + path = res/ethereum/tests + url = git@github.com:ethereum/tests diff --git a/Cargo.toml b/Cargo.toml index f5e0fd420..1c7085b03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/ethereum/res/frontier.json b/res/ethereum/frontier.json similarity index 100% rename from src/ethereum/res/frontier.json rename to res/ethereum/frontier.json diff --git a/src/ethereum/res/frontier_test.json b/res/ethereum/frontier_test.json similarity index 100% rename from src/ethereum/res/frontier_test.json rename to res/ethereum/frontier_test.json diff --git a/src/ethereum/res/homestead_test.json b/res/ethereum/homestead_test.json similarity index 100% rename from src/ethereum/res/homestead_test.json rename to res/ethereum/homestead_test.json diff --git a/src/ethereum/res/morden.json b/res/ethereum/morden.json similarity index 100% rename from src/ethereum/res/morden.json rename to res/ethereum/morden.json diff --git a/src/ethereum/res/olympic.json b/res/ethereum/olympic.json similarity index 100% rename from src/ethereum/res/olympic.json rename to res/ethereum/olympic.json diff --git a/res/ethereum/tests b/res/ethereum/tests new file mode 160000 index 000000000..dc86e6359 --- /dev/null +++ b/res/ethereum/tests @@ -0,0 +1 @@ +Subproject commit dc86e6359675440aea59ddb48648a01c799925d8 diff --git a/rust-evmjit/Cargo.toml b/rust-evmjit/Cargo.toml index 4fabe4f97..aeebe60cf 100644 --- a/rust-evmjit/Cargo.toml +++ b/rust-evmjit/Cargo.toml @@ -3,6 +3,8 @@ name = "evmjit" version = "0.1.0" authors = ["debris "] +[lib] +crate-type = ["dylib"] + [dependencies] -libc = "0.2.2" tiny-keccak = "1.0" diff --git a/rust-evmjit/src/lib.rs b/rust-evmjit/src/lib.rs index 8958f2b2f..670509d87 100644 --- a/rust-evmjit/src/lib.rs +++ b/rust-evmjit/src/lib.rs @@ -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> +/// C abi compatible wrapper for jit ext implementers. +pub struct ExtHandle { + ext_impl: Option> } -impl EnvHandle { - /// Creates new environment wrapper for given implementation - pub fn new(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(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; +impl Deref for ExtHandle { + type Target = Box; 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 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 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); } diff --git a/src/action_params.rs b/src/action_params.rs new file mode 100644 index 000000000..c92fe4c80 --- /dev/null +++ b/src/action_params.rs @@ -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![], + } + } +} diff --git a/src/basic_types.rs b/src/basic_types.rs index 2c18c59d2..2466d8813 100644 --- a/src/basic_types.rs +++ b/src/basic_types.rs @@ -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, +} diff --git a/src/block.rs b/src/block.rs index ac95e8eea..ce125b8df 100644 --- a/src/block.rs +++ b/src/block.rs @@ -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) -> 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)) } } diff --git a/src/common.rs b/src/common.rs index 468a722d7..b699bd4c6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -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::*; \ No newline at end of file +pub use log_entry::*; +pub use receipt::*; +pub use action_params::*; \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index aed9fe2f3..59888d12a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -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()) } diff --git a/src/env_info.rs b/src/env_info.rs index 47b247f3c..8b9fe3349 100644 --- a/src/env_info.rs +++ b/src/env_info.rs @@ -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() + } +} diff --git a/src/error.rs b/src/error.rs index 73a9e6029..3e7d3eb6d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,11 +11,33 @@ pub struct Mismatch { #[derive(Debug, PartialEq, Eq)] pub struct OutOfBounds { - pub min: T, - pub max: T, + pub min: Option, + pub max: Option, 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), +} + #[derive(Debug, PartialEq, Eq)] pub enum BlockError { TooManyUncles(OutOfBounds), @@ -65,6 +87,14 @@ pub enum Error { Util(UtilError), Block(BlockError), UnknownEngineName(String), + Execution(ExecutionError), + Transaction(TransactionError), +} + +impl From for Error { + fn from(err: TransactionError) -> Error { + Error::Transaction(err) + } } impl From for Error { @@ -73,6 +103,24 @@ impl From for Error { } } +impl From for Error { + fn from(err: ExecutionError) -> Error { + Error::Execution(err) + } +} + +impl From for Error { + fn from(err: CryptoError) -> Error { + Error::Util(UtilError::Crypto(err)) + } +} + +impl From 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 { diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 0f1f4ed27..f2f5eeafd 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -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 { 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 { diff --git a/src/ethereum/mod.rs b/src/ethereum/mod.rs index b3efe4a3d..832afa2ec 100644 --- a/src/ethereum/mod.rs +++ b/src/ethereum/mod.rs @@ -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 { diff --git a/src/evm/evm.rs b/src/evm/evm.rs new file mode 100644 index 000000000..fd6e59f6e --- /dev/null +++ b/src/evm/evm.rs @@ -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; + +/// Evm interface. +pub trait Evm { + /// This function should be used to execute transaction. + fn exec(&self, params: &ActionParams, ext: &mut Ext) -> Result; +} diff --git a/src/evm/ext.rs b/src/evm/ext.rs new file mode 100644 index 000000000..b878c5819 --- /dev/null +++ b/src/evm/ext.rs @@ -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
), 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; + + /// Returns code at given address + fn extcode(&self, address: &Address) -> Vec; + + /// Creates log entry with given topics and data + fn log(&mut self, topics: Vec, 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; + + /// 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; +} diff --git a/src/evm/factory.rs b/src/evm/factory.rs new file mode 100644 index 000000000..788140a38 --- /dev/null +++ b/src/evm/factory.rs @@ -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 { + Box::new(super::jit::JitEvm) + } + + /// Returns native rust evm + #[cfg(not(feature = "jit"))] + pub fn create() -> Box { + unimplemented!(); + } +} + +#[test] +fn test_create_vm() { + let _vm = Factory::create(); +} diff --git a/src/evm/jit.rs b/src/evm/jit.rs new file mode 100644 index 000000000..fb71f3363 --- /dev/null +++ b/src/evm/jit.rs @@ -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, + address: Address, + caller: Address, + origin: Address, + call_value: U256, + author: Address, + difficulty: U256, + gas_limit: U256, + number: u64, + timestamp: u64, + code: Vec +} + +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: Sized { + fn from_jit(input: T) -> Self; +} + +/// Should be used to covert ethcore types to jit +trait IntoJit { + 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 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 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 for H256 { + fn into_jit(self) -> evmjit::H256 { + let i: evmjit::I256 = self.into_jit(); + From::from(i) + } +} + +impl IntoJit for Address { + fn into_jit(self) -> evmjit::I256 { + H256::from(self).into_jit() + } +} + +impl IntoJit for Address { + fn into_jit(self) -> evmjit::H256 { + H256::from(self).into_jit() + } +} + +impl IntoJit 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 +} + +impl<'a> ExtAdapter<'a> { + fn new(ext: &'a mut evm::Ext, err: &'a mut Option) -> 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); +} diff --git a/src/evm/mod.rs b/src/evm/mod.rs new file mode 100644 index 000000000..ee590c934 --- /dev/null +++ b/src/evm/mod.rs @@ -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; diff --git a/src/evm_schedule.rs b/src/evm/schedule.rs similarity index 88% rename from src/evm_schedule.rs rename to src/evm/schedule.rs index dbe2fc670..0f46bba12 100644 --- a/src/evm_schedule.rs +++ b/src/evm/schedule.rs @@ -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, } } -} \ No newline at end of file +} diff --git a/src/evm/tests.rs b/src/evm/tests.rs new file mode 100644 index 000000000..e3f81dd93 --- /dev/null +++ b/src/evm/tests.rs @@ -0,0 +1,425 @@ +use common::*; +use evm; +use evm::{Ext, Schedule, Factory}; + +struct FakeLogEntry { + topics: Vec, + data: Bytes +} + +/// Fake externalities test structure. +/// +/// Can't do recursive calls. +#[derive(Default)] +struct FakeExt { + store: HashMap, + _balances: HashMap, + blockhashes: HashMap, + codes: HashMap, + logs: Vec, + _suicides: HashSet
, + 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
), 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 { + unimplemented!(); + } + + fn extcode(&self, address: &Address) -> Vec { + self.codes.get(address).unwrap_or(&Bytes::new()).clone() + } + + fn log(&mut self, topics: Vec, data: Bytes) { + self.logs.push(FakeLogEntry { + topics: topics, + data: data + }); + } + + fn ret(&mut self, _gas: u64, _data: &[u8]) -> result::Result { + 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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &mut ext).unwrap() + }; + + assert_eq!(gas_left, U256::from(79_995)); + assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap()); +} diff --git a/src/executive.rs b/src/executive.rs new file mode 100644 index 000000000..da0948d9e --- /dev/null +++ b/src/executive.rs @@ -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
, + /// Any logs. + logs: Vec, + /// 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, + + /// Execution ended running out of gas. + pub out_of_gas: bool, +} + +/// Transaction execution result. +pub type ExecutionResult = Result; + +/// 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 { + // 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(¶ms, &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(¶ms, &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(¶ms.sender, ¶ms.address, ¶ms.value); + + if self.engine.is_builtin(¶ms.address) { + // if destination is builtin, try to execute it + let cost = self.engine.cost_of_builtin(¶ms.address, ¶ms.data); + match cost <= params.gas { + true => { + self.engine.execute_builtin(¶ms.address, ¶ms.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(¶ms, &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(¶ms.address); + // then transfer value to it + self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); + + let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract); + let evm = Factory::create(); + evm.exec(¶ms, &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
), 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(¶ms, 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 { + 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(¶ms, self.substate, output).map(|gas_left| gas + gas_left.low_u64()) + } + + fn extcode(&self, address: &Address) -> Vec { + self.state.code(address).unwrap_or(vec![]) + } + + fn ret(&mut self, gas: u64, data: &[u8]) -> Result { + 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, 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(¶ms, &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(¶ms, &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(¶ms, &mut substate, &mut []); + println!("res: {:?}", _res); + } + } +} diff --git a/src/header.rs b/src/header.rs index dac456c5f..45190332d 100644 --- a/src/header.rs +++ b/src/header.rs @@ -34,11 +34,6 @@ pub struct Header { pub hash: RefCell>, } -pub enum Seal { - With, - Without, -} - impl Header { /// Create a new, default-valued, header. pub fn new() -> Header { diff --git a/src/lib.rs b/src/lib.rs index 6aab2587a..1e7da98dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/log_entry.rs b/src/log_entry.rs new file mode 100644 index 000000000..939d60276 --- /dev/null +++ b/src/log_entry.rs @@ -0,0 +1,62 @@ +use util::*; +use basic_types::LogBloom; + +/// A single log's entry. +pub struct LogEntry { + pub address: Address, + pub topics: Vec, + 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, 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 { + &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); + } +} \ No newline at end of file diff --git a/src/null_engine.rs b/src/null_engine.rs index da6334839..bd745365c 100644 --- a/src/null_engine.rs +++ b/src/null_engine.rs @@ -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() } } diff --git a/src/receipt.rs b/src/receipt.rs index 5a188f93d..ef7e03cc8 100644 --- a/src/receipt.rs +++ b/src/receipt.rs @@ -1,27 +1,6 @@ use util::*; use basic_types::LogBloom; - -/// A single log's entry. -pub struct LogEntry { - pub address: Address, - pub topics: Vec, - 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, } +impl Receipt { + pub fn new(state_root: H256, gas_used: U256, logs: Vec) -> 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); diff --git a/src/state.rs b/src/state.rs index 569de13bd..42813ca76 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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; +pub type ApplyResult = Result; /// 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"); } -} \ No newline at end of file +} diff --git a/src/transaction.rs b/src/transaction.rs index d13a728bb..64f5b5806 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -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
, - 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>, //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 { 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 { + 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(&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(decoder: &D) -> Result 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(decoder: &D) -> Result 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 { + 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} +} \ No newline at end of file diff --git a/src/verification.rs b/src/verification.rs index 98ed722c3..dcf705126 100644 --- a/src/verification.rs +++ b/src/verification.rs @@ -47,7 +47,7 @@ pub fn verify_block_final(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(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(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 })); } }