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/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..41270177f 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,17 +238,50 @@ 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, @@ -199,88 +292,88 @@ pub mod ffi { 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/block.rs b/src/block.rs index b1207ce53..b683ab159 100644 --- a/src/block.rs +++ b/src/block.rs @@ -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); + return Err(BlockError::TooManyUncles(OutOfBounds{min: 0, max: self.engine.maximum_uncle_count(), found: self.block.uncles.len()})); } // TODO: check number // TODO: check not a direct ancestor (use last_hashes for that) @@ -170,12 +170,12 @@ impl<'x, 'y> OpenBlock<'x, 'y> { 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) { - Ok(x) => { + 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/client.rs b/src/client.rs index b914dba19..018f99b6f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -5,6 +5,7 @@ use error::*; use header::BlockNumber; use spec::Spec; use engine::Engine; +use queue::BlockQueue; /// General block status pub enum BlockStatus { @@ -18,9 +19,6 @@ pub enum BlockStatus { Unknown, } -/// Result of import block operation. -pub type ImportResult = Result<(), ImportError>; - /// Information about the blockchain gthered together. pub struct BlockChainInfo { /// Blockchain difficulty. @@ -95,28 +93,30 @@ pub trait BlockChainClient : Sync { /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. pub struct Client { - chain: Arc, + chain: Arc>, _engine: Arc>, + queue: BlockQueue, } impl Client { pub fn new(spec: Spec, path: &Path) -> Result { - let chain = Arc::new(BlockChain::new(&spec.genesis_block(), path)); + let chain = Arc::new(RwLock::new(BlockChain::new(&spec.genesis_block(), path))); let engine = Arc::new(try!(spec.to_engine())); Ok(Client { chain: chain.clone(), - _engine: engine, + _engine: engine.clone(), + queue: BlockQueue::new(chain.clone(), engine.clone()), }) } } impl BlockChainClient for Client { fn block_header(&self, hash: &H256) -> Option { - self.chain.block(hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()) + self.chain.read().unwrap().block(hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()) } fn block_body(&self, hash: &H256) -> Option { - self.chain.block(hash).map(|bytes| { + self.chain.read().unwrap().block(hash).map(|bytes| { let rlp = Rlp::new(&bytes); let mut body = RlpStream::new(); body.append_raw(rlp.at(1).as_raw(), 1); @@ -126,34 +126,34 @@ impl BlockChainClient for Client { } fn block(&self, hash: &H256) -> Option { - self.chain.block(hash) + self.chain.read().unwrap().block(hash) } fn block_status(&self, hash: &H256) -> BlockStatus { - if self.chain.is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown } + if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown } } fn block_header_at(&self, n: BlockNumber) -> Option { - self.chain.block_hash(n).and_then(|h| self.block_header(&h)) + self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h)) } fn block_body_at(&self, n: BlockNumber) -> Option { - self.chain.block_hash(n).and_then(|h| self.block_body(&h)) + self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_body(&h)) } fn block_at(&self, n: BlockNumber) -> Option { - self.chain.block_hash(n).and_then(|h| self.block(&h)) + self.chain.read().unwrap().block_hash(n).and_then(|h| self.block(&h)) } fn block_status_at(&self, n: BlockNumber) -> BlockStatus { - match self.chain.block_hash(n) { + match self.chain.read().unwrap().block_hash(n) { Some(h) => self.block_status(&h), None => BlockStatus::Unknown } } fn tree_route(&self, from: &H256, to: &H256) -> Option { - self.chain.tree_route(from.clone(), to.clone()) + self.chain.read().unwrap().tree_route(from.clone(), to.clone()) } fn state_data(&self, _hash: &H256) -> Option { @@ -165,17 +165,7 @@ impl BlockChainClient for Client { } fn import_block(&mut self, bytes: &[u8]) -> ImportResult { - //TODO: verify block - { - let block = BlockView::new(bytes); - let header = block.header_view(); - let hash = header.sha3(); - if self.chain.is_known(&hash) { - return Err(ImportError::AlreadyInChain); - } - } - self.chain.insert_block(bytes); - Ok(()) + self.queue.import_block(bytes) } fn queue_status(&self) -> BlockQueueStatus { @@ -188,12 +178,13 @@ impl BlockChainClient for Client { } fn chain_info(&self) -> BlockChainInfo { + let chain = self.chain.read().unwrap(); BlockChainInfo { - total_difficulty: self.chain.best_block_total_difficulty(), - pending_total_difficulty: self.chain.best_block_total_difficulty(), - genesis_hash: self.chain.genesis_hash(), - best_block_hash: self.chain.best_block_hash(), - best_block_number: From::from(self.chain.best_block_number()) + total_difficulty: chain.best_block_total_difficulty(), + pending_total_difficulty: chain.best_block_total_difficulty(), + genesis_hash: chain.genesis_hash(), + best_block_hash: chain.best_block_hash(), + best_block_number: From::from(chain.best_block_number()) } } } diff --git a/src/common.rs b/src/common.rs index 061d0748a..b699bd4c6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,11 +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 log_entry::*; -pub use receipt::*; \ No newline at end of file +pub use receipt::*; +pub use action_params::*; \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index 64f85e079..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()) } @@ -33,11 +34,18 @@ pub trait Engine : Sync + Send { fn on_new_block(&self, _block: &mut Block) {} fn on_close_block(&self, _block: &mut Block) {} - /// Verify that `header` is valid. - /// `parent` (the parent header) and `block` (the header's full block) may be provided for additional - /// checks. Returns either a null `Ok` or a general error detailing the problem with import. - // TODO: consider including State in the params. - fn verify_block(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + // TODO: consider including State in the params for verification functions. + /// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block) + /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + + /// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block) + /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + + /// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block) + /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify_block_final(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. diff --git a/src/env_info.rs b/src/env_info.rs index 47b247f3c..5df877970 100644 --- a/src/env_info.rs +++ b/src/env_info.rs @@ -22,3 +22,17 @@ 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() + } + } +} diff --git a/src/error.rs b/src/error.rs index d975f15b6..4b5b1b6f1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ //! General error types for use in ethcore. use util::*; +use header::BlockNumber; #[derive(Debug)] pub struct Mismatch { @@ -15,27 +16,73 @@ pub struct OutOfBounds { 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 BlockError { - TooManyUncles, + TooManyUncles(OutOfBounds), UncleWrongGeneration, ExtraDataOutOfBounds(OutOfBounds), InvalidSealArity(Mismatch), + TooMuchGasUsed(OutOfBounds), + InvalidUnclesHash(Mismatch), + UncleTooOld(OutOfBounds), + UncleIsBrother(OutOfBounds), + UncleInChain(H256), + UncleParentNotInChain(H256), + InvalidStateRoot, + InvalidGasUsed, + InvalidTransactionsRoot(Mismatch), + InvalidDifficulty(Mismatch), + InvalidGasLimit(OutOfBounds), + InvalidReceiptsStateRoot, + InvalidTimestamp(OutOfBounds), + InvalidLogBloom, + InvalidBlockNonce, + InvalidParentHash(Mismatch), + InvalidNumber(OutOfBounds), + UnknownParent(H256), + UnknownUncleParent(H256), } #[derive(Debug)] pub enum ImportError { - Bad(BlockError), + Bad(Error), AlreadyInChain, AlreadyQueued, } +impl From for ImportError { + fn from(err: Error) -> ImportError { + ImportError::Bad(err) + } +} + +/// Result of import block operation. +pub type ImportResult = Result<(), ImportError>; + #[derive(Debug)] /// General error type which should be capable of representing all errors in ethcore. pub enum Error { Util(UtilError), Block(BlockError), UnknownEngineName(String), + Execution(ExecutionError), } impl From for Error { @@ -44,6 +91,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: ExecutionError) -> Error { + Error::Execution(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 828441fd6..d3a64d723 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). @@ -43,6 +44,83 @@ impl Engine for Ethash { fields.state.add_balance(u.author(), &(reward * U256::from((8 + u.number() - current_number) / 8))); } } + + + 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 }))) + } + let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap()); + if header.gas_limit < min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: From::from(0), found: header.gas_limit }))); + } + let maximum_extra_data_size = self.maximum_extra_data_size(); + if header.number != 0 && header.extra_data.len() > maximum_extra_data_size { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: 0, max: maximum_extra_data_size, found: header.extra_data.len() }))); + } + // TODO: Verify seal (quick) + Ok(()) + } + + 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::Result<(), Error> { + // Check difficulty is correct given the two timestamps. + let expected_difficulty = self.calculate_difficuty(header, parent); + if header.difficulty != expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty }))) + } + let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap()); + 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 }))); + } + Ok(()) + } + + fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> result::Result<(), Error> { Ok(()) } +} + +impl Ethash { + fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 { + const EXP_DIFF_PERIOD: u64 = 100000; + if header.number == 0 { + panic!("Can't calculate genesis block difficulty"); + } + + let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap()); + let difficulty_bound_divisor = decode(self.spec().engine_params.get("difficultyBoundDivisor").unwrap()); + let duration_limit: u64 = decode(self.spec().engine_params.get("durationLimit").unwrap()); + let frontier_limit = decode(self.spec().engine_params.get("frontierCompatibilityModeLimit").unwrap()); + let mut target = if header.number < frontier_limit { + if header.timestamp >= parent.timestamp + duration_limit { + parent.difficulty - (parent.difficulty / difficulty_bound_divisor) + } + else { + parent.difficulty + (parent.difficulty / difficulty_bound_divisor) + } + } + else { + let diff_inc = (header.timestamp - parent.timestamp) / 10; + if diff_inc <= 1 { + parent.difficulty + parent.difficulty / From::from(2048) * From::from(1 - diff_inc) + } + else { + parent.difficulty - parent.difficulty / From::from(2048) * From::from(max(diff_inc - 1, 99)) + } + }; + target = max(min_difficulty, target); + let period = ((parent.number + 1) / EXP_DIFF_PERIOD) as usize; + if period > 1 { + target = max(min_difficulty, target + (U256::from(1) << (period - 2))); + } + target + } } #[test] @@ -57,3 +135,5 @@ fn on_close_block() { let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } + +// TODO: difficulty test 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..f5ddaaaa6 --- /dev/null +++ b/src/evm/ext.rs @@ -0,0 +1,58 @@ +//! Interface for Evm externalities. + +use util::hash::*; +use util::uint::*; +use util::bytes::*; +use evm::Schedule; +use evm::Error; + +// 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; +} 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..4b99bc655 --- /dev/null +++ b/src/evm/jit.rs @@ -0,0 +1,715 @@ +//! Just in time compiler execution environment. +use common::*; +use evmjit; +use evm; + +/// Ethcore representation of evmjit runtime data. +pub struct RuntimeData { + pub gas: U256, + pub gas_price: U256, + pub call_data: Vec, + pub address: Address, + pub caller: Address, + pub origin: Address, + pub call_value: U256, + pub coinbase: Address, + pub difficulty: U256, + pub gas_limit: U256, + pub number: u64, + pub timestamp: u64, + pub code: Vec +} + +impl RuntimeData { + pub 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(), + coinbase: 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.coinbase = self.coinbase.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(); + + // TODO: + data.coinbase = Address::new(); + data.difficulty = U256::zero(); + data.gas_limit = U256::zero(); + data.number = 0; + data.timestamp = 0; + + 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) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use common::*; + use evm::jit::{FromJit, IntoJit}; + use evm::{Evm,Schedule}; + use executive::*; + use state::*; + use engine::*; + use spec::*; + + struct TestEngine; + + impl TestEngine { + fn new() -> Self { TestEngine } + } + + impl Engine for TestEngine { + fn name(&self) -> &str { "TestEngine" } + fn spec(&self) -> &Spec { unimplemented!() } + fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() } + } + + #[test] + fn test_to_and_from_u256() { + use std::str::FromStr; + + 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() { + use std::str::FromStr; + + 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); + } + + #[test] + fn test_to_and_from_address() { + use std::str::FromStr; + + 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); + } + + #[test] + fn test_ext_add() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop); + } + + assert_eq!(state.storage_at(&address, &H256::new()), + H256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap()); + } + + #[test] + fn test_ext_sha3_0() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "6000600020600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), + H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap()); + } + + #[test] + fn test_ext_sha3_1() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "6005600420600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), + H256::from_str("c41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec").unwrap()); + } + + #[test] + fn test_origin() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.origin = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "32600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(Address::from(state.storage_at(&address, &H256::new())), address.clone()); + } + + #[test] + fn test_sender() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "32600055".from_hex().unwrap(); + params.code = "33600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(Address::from(state.storage_at(&address, &H256::new())), address.clone()); + } + + #[test] + fn test_extcode_copy0() { + // 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("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address_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.origin = sender.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = address_code.clone(); + + let mut state = State::new_temp(); + state.init_code(&address, address_code); + state.init_code(&sender, sender_code); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), + H256::from_str("6005600055000000000000000000000000000000000000000000000000000000").unwrap()); + } + + #[test] + fn test_balance() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "3331600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + state.add_balance(&address, &U256::from(0x10)); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0x10))); + } + + #[test] + fn test_empty_log() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "60006000a0".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + let logs = substate.logs(); + assert_eq!(logs.len(), 1); + let log = &logs[0]; + assert_eq!(log.address(), &address); + assert_eq!(log.topics().len(), 0); + assert_eq!(log.bloom(), H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()); + } + + #[test] + fn test_log_with_one_topic() { + // 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 mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "60ff6000533360206000a1".from_hex().unwrap(); + + let mut state = State::new_temp(); + let info = EnvInfo::new(); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + let logs = substate.logs(); + assert_eq!(logs.len(), 1); + let log = &logs[0]; + assert_eq!(log.address(), &address); + assert_eq!(log.topics().len(), 1); + let topic = &log.topics()[0]; + assert_eq!(topic, &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()); + assert_eq!(topic, &H256::from(address.clone())); + assert_eq!(log.data(), &"ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + } + + #[test] + fn test_blockhash() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "600040600055".from_hex().unwrap(); + + let mut state = State::new_temp(); + let mut info = EnvInfo::new(); + info.number = 1; + info.last_hashes.push(H256::from(address.clone())); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), H256::from(address.clone())); + } + + #[test] + fn test_calldataload() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(0x174876e800u64); + params.code = "600135600055".from_hex().unwrap(); + params.data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap(); + + let mut state = State::new_temp(); + let mut info = EnvInfo::new(); + info.number = 1; + info.last_hashes.push(H256::from(address.clone())); + let engine = TestEngine::new(); + let mut substate = Substate::new(); + + { + let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract); + let evm = JitEvm; + let _res = evm.exec(¶ms, &mut ext); + //assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {}); + } + + assert_eq!(state.storage_at(&address, &H256::new()), H256::from_str("23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").unwrap()); + } +} diff --git a/src/evm/mod.rs b/src/evm/mod.rs new file mode 100644 index 000000000..9f5789dda --- /dev/null +++ b/src/evm/mod.rs @@ -0,0 +1,13 @@ +//! Ethereum virtual machine. + +pub mod ext; +pub mod evm; +pub mod factory; +pub mod schedule; +#[cfg(feature = "jit" )] +mod jit; + +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 90% rename from src/evm_schedule.rs rename to src/evm/schedule.rs index dbe2fc670..552c2a576 100644 --- a/src/evm_schedule.rs +++ b/src/evm/schedule.rs @@ -1,5 +1,5 @@ /// 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 +32,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, diff --git a/src/executive.rs b/src/executive.rs new file mode 100644 index 000000000..6ea0be1cc --- /dev/null +++ b/src/executive.rs @@ -0,0 +1,563 @@ +//! 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. +pub 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. + pub fn new() -> Self { + Substate { + suicides: HashSet::new(), + logs: vec![], + refunds_count: U256::zero(), + } + } + + // TODO: remove + pub fn logs(&self) -> &[LogEntry] { + &self.logs + } +} + +/// Transaction execution result. +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, +} + +pub type ExecutionResult = Result; + +/// Message-call/contract-creation executor; useful for executing transactions. +pub struct Executive<'a> { + state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, +} + +impl<'a> Executive<'a> { + /// Creates new executive with depth equal 0. + 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) -> ExecutionResult { + // TODO: validate transaction signature ?/ sender + + let sender = t.sender(); + let nonce = self.state.nonce(&sender); + + // validate transaction nonce + if t.nonce != nonce { + return Err(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(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(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! + 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(), &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. +pub 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. +pub 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. + pub 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`. + pub 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 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use common::*; + use state::*; + use ethereum; + use null_engine::*; + + #[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/lib.rs b/src/lib.rs index 1b9c055df..1e7da98dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,27 +83,33 @@ 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; +pub mod verification; +pub mod queue; pub mod ethereum; 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/queue.rs b/src/queue.rs index ea212aaf1..721960259 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,16 +1,24 @@ -use std::sync::Arc; use util::*; use blockchain::BlockChain; -use client::{QueueStatus, ImportResult}; use views::{BlockView}; +use verification::*; +use error::*; +use engine::Engine; /// A queue of blocks. Sits between network or other I/O and the BlockChain. /// Sorts them ready for blockchain insertion. -pub struct BlockQueue; +pub struct BlockQueue { + bc: Arc>, + engine: Arc>, +} impl BlockQueue { /// Creates a new queue instance. - pub fn new() -> BlockQueue { + pub fn new(bc: Arc>, engine: Arc>) -> BlockQueue { + BlockQueue { + bc: bc, + engine: engine, + } } /// Clear the queue and stop verification activity. @@ -18,18 +26,16 @@ impl BlockQueue { } /// Add a block to the queue. - pub fn import_block(&mut self, bytes: &[u8], bc: &mut BlockChain) -> ImportResult { - //TODO: verify block - { - let block = BlockView::new(bytes); - let header = block.header_view(); - let hash = header.sha3(); - if self.chain.is_known(&hash) { - return ImportResult::Bad; - } + pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult { + let header = BlockView::new(bytes).header(); + if self.bc.read().unwrap().is_known(&header.hash()) { + return Err(ImportError::AlreadyInChain); } - bc.insert_block(bytes); - ImportResult::Queued(QueueStatus::Known) + try!(verify_block_basic(bytes, self.engine.deref().deref())); + try!(verify_block_unordered(bytes, self.engine.deref().deref())); + try!(verify_block_final(bytes, self.engine.deref().deref(), self.bc.read().unwrap().deref())); + self.bc.write().unwrap().insert_block(bytes); + Ok(()) } } diff --git a/src/receipt.rs b/src/receipt.rs index ef46e0f48..ef7e03cc8 100644 --- a/src/receipt.rs +++ b/src/receipt.rs @@ -10,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 03978fc4e..cdd861b33 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) -> 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/sync/tests.rs b/src/sync/tests.rs index 6a045fa1e..bc0e171d2 100644 --- a/src/sync/tests.rs +++ b/src/sync/tests.rs @@ -1,13 +1,7 @@ -use std::collections::{HashMap, VecDeque}; -use util::bytes::Bytes; -use util::hash::{H256, FixedHash}; -use util::uint::{U256}; -use util::sha3::Hashable; -use util::rlp::{self, Rlp, RlpStream, View, Stream}; -use util::network::{PeerId, PacketId}; -use util::error::UtilError; -use client::{BlockChainClient, BlockStatus, TreeRoute, BlockQueueStatus, BlockChainInfo, ImportResult}; +use util::*; +use client::{BlockChainClient, BlockStatus, TreeRoute, BlockQueueStatus, BlockChainInfo}; use header::{Header as BlockHeader, BlockNumber}; +use error::*; use sync::io::SyncIo; use sync::chain::ChainSync; diff --git a/src/transaction.rs b/src/transaction.rs index f162bb2c5..3c3301dd9 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -8,12 +8,12 @@ pub enum Action { /// A set of information describing an externally-originating message call /// or contract creation operation. pub struct Transaction { - nonce: U256, - gas_price: U256, - gas: U256, - action: Action, - value: U256, - data: Bytes, + pub nonce: U256, + pub gas_price: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub data: Bytes, hash: RefCell>, //TODO: make this private } @@ -53,6 +53,9 @@ impl Transaction { /// Returns transaction type. pub fn action(&self) -> &Action { &self.action } + + /// Returns transaction sender. + pub fn sender(&self) -> Address { Address::new() } } impl Decodable for Action { diff --git a/src/verification.rs b/src/verification.rs new file mode 100644 index 000000000..be885162a --- /dev/null +++ b/src/verification.rs @@ -0,0 +1,154 @@ +/// Block and transaction verification functions +/// +/// Block verification is done in 3 steps +/// 1. Quick verification upon adding to the block queue +/// 2. Signatures verification done in the queue. +/// 3. Final verification against the blockchain done before enactment. + +use common::*; +use engine::Engine; +use blockchain::BlockChain; + +/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block +pub fn verify_block_basic(bytes: &[u8], engine: &Engine) -> Result<(), Error> { + let block = BlockView::new(bytes); + let header = block.header(); + try!(verify_header(&header)); + try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash)); + try!(engine.verify_block_basic(&header, Some(bytes))); + for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + try!(verify_header(&u)); + try!(engine.verify_block_basic(&u, None)); + } + Ok(()) +} + +/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. +/// Still operates on a individual block +/// TODO: return cached transactions, header hash. +pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error> { + let block = BlockView::new(bytes); + let header = block.header(); + try!(engine.verify_block_unordered(&header, Some(bytes))); + for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + try!(engine.verify_block_unordered(&u, None)); + } + Ok(()) +} + +/// Phase 3 verification. Check block information against parent and uncles. +pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Result<(), Error> { + let block = BlockView::new(bytes); + let header = block.header(); + let parent = try!(bc.block_header(&header.parent_hash).ok_or::(From::from(BlockError::UnknownParent(header.parent_hash.clone())))); + try!(verify_parent(&header, &parent)); + try!(engine.verify_block_final(&header, &parent, Some(bytes))); + + 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 }))); + } + + let mut excluded = HashSet::new(); + excluded.insert(header.hash()); + let mut hash = header.parent_hash.clone(); + excluded.insert(hash.clone()); + for _ in 0..6 { + match bc.block_details(&hash) { + Some(details) => { + excluded.insert(details.parent.clone()); + let b = bc.block(&hash).unwrap(); + excluded.extend(BlockView::new(&b).uncle_hashes()); + hash = details.parent; + } + None => break + } + } + + for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone())))); + if excluded.contains(&uncle_parent.hash()) { + return Err(From::from(BlockError::UncleInChain(uncle_parent.hash()))) + } + + // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() + // 1 2 + // 2 + // 3 + // 4 + // 5 + // 6 7 + // (8 Invalid) + + let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 }; + if depth > 6 { + return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: header.number - depth, max: 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 }))); + } + + // cB + // cB.p^1 1 depth, valid uncle + // cB.p^2 ---/ 2 + // cB.p^3 -----/ 3 + // cB.p^4 -------/ 4 + // cB.p^5 ---------/ 5 + // cB.p^6 -----------/ 6 + // cB.p^7 -------------/ + // cB.p^8 + let mut expected_uncle_parent = header.parent_hash.clone(); + for _ in 0..depth { + expected_uncle_parent = bc.block_details(&expected_uncle_parent).unwrap().parent; + } + if expected_uncle_parent != uncle_parent.hash() { + return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); + } + + try!(engine.verify_block_final(&uncle, &uncle_parent, Some(bytes))); + } + } + Ok(()) +} + +/// Check basic header parameters. +fn verify_header(header: &Header) -> 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 }))) + } + 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 }))); + } + Ok(()) +} + +/// Check header parameters agains parent header. +fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> { + if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash { + 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 }))) + } + if header.number <= parent.number { + return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: BlockNumber::max_value(), min: parent.number + 1, found: header.number }))); + } + Ok(()) +} + +/// Verify block data against header: transactions root and uncles hash. +fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> { + let block = Rlp::new(block); + let tx = block.at(1); + let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here + if expected_root != transactions_root { + return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() }))) + } + let expected_uncles = &block.at(2).as_raw().sha3(); + if expected_uncles != uncles_hash { + return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() }))) + } + Ok(()) +} +