diff --git a/Cargo.toml b/Cargo.toml index 14164a735..a6706e57e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,5 @@ evmjit = { path = "rust-evmjit", optional = true } ethash = { path = "ethash" } [features] -default = ["jit"] jit = ["evmjit"] +evm_debug = [] diff --git a/res/ethereum/frontier.json b/res/ethereum/frontier.json index 6394a9010..eaf0ef4c1 100644 --- a/res/ethereum/frontier.json +++ b/res/ethereum/frontier.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", diff --git a/res/ethereum/frontier_like_test.json b/res/ethereum/frontier_like_test.json new file mode 100644 index 000000000..b5cbec465 --- /dev/null +++ b/res/ethereum/frontier_like_test.json @@ -0,0 +1,34 @@ +{ + "engineName": "Frontier (Test)", + "engineName": "Ethash", + "params": { + "accountStartNonce": "0x00", + "frontierCompatibilityModeLimit": "0x0dbba0", + "maximumExtraDataSize": "0x20", + "tieBreakingGas": false, + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x0400", + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "networkID" : "0x1" + }, + "genesis": { + "nonce": "0x0000000000000042", + "difficulty": "0x400000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } } + } +} diff --git a/res/ethereum/homestead_test.json b/res/ethereum/homestead_test.json index ee73d0ed3..b11ef9740 100644 --- a/res/ethereum/homestead_test.json +++ b/res/ethereum/homestead_test.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xffffffff", + "frontierCompatibilityModeLimit": "0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "tieBreakingGas": false, diff --git a/res/ethereum/morden.json b/res/ethereum/morden.json index 79f9f3d99..32fed0cab 100644 --- a/res/ethereum/morden.json +++ b/res/ethereum/morden.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x0100000", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", diff --git a/res/ethereum/olympic.json b/res/ethereum/olympic.json index 4318d9230..b3dfc1ed8 100644 --- a/res/ethereum/olympic.json +++ b/res/ethereum/olympic.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xffffffff", + "frontierCompatibilityModeLimit": "0xffffffffffffffff", "maximumExtraDataSize": "0x0400", "tieBreakingGas": false, "minGasLimit": "125000", diff --git a/src/blockchain.rs b/src/blockchain.rs index abd6db203..f08d15057 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -248,6 +248,13 @@ impl BlockChain { bc } + /// Ensure that the best block does indeed have a state_root in the state DB. + /// If it doesn't, then rewind down until we find one that does and delete data to ensure that + /// later blocks will be reimported. + pub fn ensure_good(&mut self, _state: &OverlayDB) { + unimplemented!(); + } + /// Returns a tree route between `from` and `to`, which is a tuple of: /// /// - a vector of hashes of all blocks, ordered from `from` to `to`. diff --git a/src/client.rs b/src/client.rs index dd036a858..3ee84ccd7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,6 @@ use util::*; -use rocksdb::{DB}; +use rocksdb::{Options, DB}; +use rocksdb::DBCompactionStyle::DBUniversalCompaction; use blockchain::{BlockChain, BlockProvider}; use views::BlockView; use error::*; @@ -111,19 +112,42 @@ impl Client { /// Create a new client with given spec and DB path. pub fn new(spec: Spec, path: &Path, message_channel: IoChannel ) -> Result { let chain = Arc::new(RwLock::new(BlockChain::new(&spec.genesis_block(), path))); - let engine = Arc::new(try!(spec.to_engine())); + let mut opts = Options::new(); + opts.create_if_missing(true); + opts.set_max_open_files(256); + opts.set_use_fsync(false); + opts.set_bytes_per_sync(8388608); + opts.set_disable_data_sync(false); + opts.set_block_cache_size_mb(1024); + opts.set_table_cache_num_shard_bits(6); + opts.set_max_write_buffer_number(32); + opts.set_write_buffer_size(536870912); + opts.set_target_file_size_base(1073741824); + opts.set_min_write_buffer_number_to_merge(4); + opts.set_level_zero_stop_writes_trigger(2000); + opts.set_level_zero_slowdown_writes_trigger(0); + opts.set_compaction_style(DBUniversalCompaction); + opts.set_max_background_compactions(4); + opts.set_max_background_flushes(4); + opts.set_filter_deletes(false); + opts.set_disable_auto_compactions(true); + let mut state_path = path.to_path_buf(); state_path.push("state"); - let db = DB::open_default(state_path.to_str().unwrap()).unwrap(); + let db = DB::open(&opts, state_path.to_str().unwrap()).unwrap(); let mut state_db = OverlayDB::new(db); + + let engine = Arc::new(try!(spec.to_engine())); engine.spec().ensure_db_good(&mut state_db); state_db.commit().expect("Error commiting genesis state to state DB"); +// chain.write().unwrap().ensure_good(&state_db); + Ok(Client { - chain: chain.clone(), + chain: chain, engine: engine.clone(), state_db: state_db, - queue: BlockQueue::new(engine.clone(), message_channel), + queue: BlockQueue::new(engine, message_channel), }) } diff --git a/src/engine.rs b/src/engine.rs index e76b3b28f..79857a404 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,6 +2,7 @@ use common::*; use block::Block; use spec::Spec; use evm::Schedule; +use evm::Factory; /// 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,6 +23,9 @@ pub trait Engine : Sync + Send { /// Get the general parameters of the chain. fn spec(&self) -> &Spec; + /// Get current EVM factory + fn vm_factory(&self) -> &Factory; + /// Get the EVM schedule for the given `env_info`. fn schedule(&self, env_info: &EnvInfo) -> Schedule; @@ -50,6 +54,7 @@ pub trait Engine : Sync + Send { /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. + fn verify_transaction_basic(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } /// Don't forget to call Super::populateFromParent when subclassing & overriding. diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 9e19959cb..29fea71fe 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -6,23 +6,47 @@ use block::*; use spec::*; use engine::*; use evm::Schedule; +use evm::Factory; /// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum /// mainnet chains in the Olympic, Frontier and Homestead eras. pub struct Ethash { spec: Spec, +<<<<<<< HEAD pow: EthashManager, +======= + factory: Factory, + u64_params: RwLock>, + u256_params: RwLock>, +>>>>>>> a350aae82b00d2bee0e6be8017d38b644121d3e9 } impl Ethash { pub fn new_boxed(spec: Spec) -> Box { +<<<<<<< HEAD Box::new(Ethash { spec: spec, pow: EthashManager::new(), +======= + Box::new(Ethash{ + spec: spec, + // TODO [todr] should this return any specific factory? + factory: Factory::default(), + u64_params: RwLock::new(HashMap::new()), + u256_params: RwLock::new(HashMap::new()) +>>>>>>> a350aae82b00d2bee0e6be8017d38b644121d3e9 }) } - fn u256_param(&self, name: &str) -> U256 { self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(U256::from(0u64)) } + fn u64_param(&self, name: &str) -> u64 { + *self.u64_params.write().unwrap().entry(name.to_string()).or_insert_with(|| + self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(0u64)) + } + + fn u256_param(&self, name: &str) -> U256 { + *self.u256_params.write().unwrap().entry(name.to_string()).or_insert_with(|| + self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(x!(0))) + } } impl Engine for Ethash { @@ -36,7 +60,17 @@ 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 schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() } + + fn vm_factory(&self) -> &Factory { + &self.factory + } + + fn schedule(&self, env_info: &EnvInfo) -> Schedule { + match env_info.number < self.u64_param("frontierCompatibilityModeLimit") { + true => Schedule::new_frontier(), + _ => Schedule::new_homestead(), + } + } fn populate_from_parent(&self, header: &mut Header, parent: &Header) { header.difficulty = self.calculate_difficuty(header, parent); @@ -71,7 +105,7 @@ impl Engine for Ethash { fields.state.commit(); } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::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 }))) @@ -114,7 +148,12 @@ impl Engine for Ethash { Ok(()) } - fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> result::Result<(), Error> { Ok(()) } + fn verify_transaction_basic(&self, t: &Transaction, header: &Header) -> result::Result<(), Error> { + if header.number() >= self.u64_param("frontierCompatibilityModeLimit") { + try!(t.check_low_s()); + } + Ok(()) + } } impl Ethash { @@ -124,10 +163,10 @@ impl Ethash { 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 min_difficulty = self.u256_param("minimumDifficulty"); + let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor"); + let duration_limit = self.u64_param("durationLimit"); + let frontier_limit = self.u64_param("frontierCompatibilityModeLimit"); let mut target = if header.number < frontier_limit { if header.timestamp >= parent.timestamp + duration_limit { parent.difficulty - (parent.difficulty / difficulty_bound_divisor) diff --git a/src/ethereum/mod.rs b/src/ethereum/mod.rs index 832afa2ec..603a64e7d 100644 --- a/src/ethereum/mod.rs +++ b/src/ethereum/mod.rs @@ -23,6 +23,9 @@ pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../ /// 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/ethereum/homestead_test.json")) } +/// Create a new Frontier main net chain spec without genesis accounts. +pub fn new_frontier_like_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_like_test.json")) } + /// Create a new Morden chain spec. pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) } diff --git a/src/evm/evm.rs b/src/evm/evm.rs index fd6e59f6e..696d49474 100644 --- a/src/evm/evm.rs +++ b/src/evm/evm.rs @@ -12,6 +12,28 @@ pub enum Error { /// was invalid. Balance still should be transfered and nonce /// should be increased. OutOfGas, + /// `BadJumpDestination` is returned when execution tried to move + /// to position that wasn't marked with JUMPDEST instruction + BadJumpDestination { + destination: usize + }, + /// `BadInstructions` is returned when given instruction is not supported + BadInstruction { + instruction: u8, + }, + /// `StackUnderflow` when there is not enough stack elements to execute instruction + /// First parameter says how many elements were needed and the second how many were actually on Stack + StackUnderflow { + instruction: &'static str, + wanted: usize, + on_stack: usize + }, + /// When execution would exceed defined Stack Limit + OutOfStack { + instruction: &'static str, + wanted: usize, + limit: usize + }, /// Returned on evm internal error. Should never be ignored during development. /// Likely to cause consensus issues. Internal, @@ -25,5 +47,5 @@ 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; + fn exec(&self, params: ActionParams, ext: &mut Ext) -> Result; } diff --git a/src/evm/ext.rs b/src/evm/ext.rs index d74f847d7..924db7a71 100644 --- a/src/evm/ext.rs +++ b/src/evm/ext.rs @@ -1,17 +1,40 @@ //! Interface for Evm externalities. +use common::Bytes; use util::hash::*; use util::uint::*; -use util::bytes::*; use evm::{Schedule, Error}; use env_info::*; +/// Result of externalities create function. +pub enum ContractCreateResult { + /// Returned when creation was successfull. + /// Contains an address of newly created contract and gas left. + Created(Address, U256), + /// Returned when contract creation failed. + /// VM doesn't have to know the reason. + Failed +} + +/// Result of externalities call function. +pub enum MessageCallResult { + /// Returned when message call was successfull. + /// Contains gas left. + Success(U256), + /// Returned when message call failed. + /// VM doesn't have to know the reason. + Failed +} + pub trait Ext { /// Returns a value for given key. fn storage_at(&self, key: &H256) -> H256; /// Stores a value for given key. - fn set_storage_at(&mut self, key: H256, value: H256); + fn set_storage(&mut self, key: H256, value: H256); + + /// Determine whether an account exists. + fn exists(&self, address: &Address) -> bool; /// Returns address balance. fn balance(&self, address: &Address) -> U256; @@ -22,7 +45,7 @@ pub trait Ext { /// Creates new contract. /// /// Returns gas_left and contract address if contract creation was succesfull. - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
); + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult; /// Message call. /// @@ -31,18 +54,17 @@ pub trait Ext { /// and true if subcall was successfull. fn call(&mut self, gas: &U256, - call_gas: &U256, - receive_address: &Address, + address: &Address, value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), Error>; + output: &mut [u8]) -> MessageCallResult; /// Returns code at given address - fn extcode(&self, address: &Address) -> Vec; + fn extcode(&self, address: &Address) -> Bytes; /// Creates log entry with given topics and data - fn log(&mut self, topics: Vec, data: Bytes); + fn log(&mut self, topics: Vec, data: &[u8]); /// Should be called when transaction calls `RETURN` opcode. /// Returns gas_left if cost of returning the data is not too high. @@ -57,4 +79,13 @@ pub trait Ext { /// Returns environment info. fn env_info(&self) -> &EnvInfo; + + /// Returns current depth of execution. + /// + /// If contract A calls contract B, and contract B calls C, + /// then A depth is 0, B is 1, C is 2 and so on. + fn depth(&self) -> usize; + + /// Increments sstore refunds count by 1. + fn inc_sstore_clears(&mut self); } diff --git a/src/evm/factory.rs b/src/evm/factory.rs index 788140a38..3dde4bb6d 100644 --- a/src/evm/factory.rs +++ b/src/evm/factory.rs @@ -1,25 +1,123 @@ //! Evm factory. - +use std::fmt; use evm::Evm; +#[derive(Clone)] +pub enum VMType { + Jit, + Interpreter +} + +impl fmt::Display for VMType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", match *self { + VMType::Jit => "JIT", + VMType::Interpreter => "INT" + }) + } +} + +impl VMType { + /// Return all possible VMs (JIT, Interpreter) + #[cfg(feature="jit")] + pub fn all() -> Vec { + vec![VMType::Jit, VMType::Interpreter] + } + + /// Return all possible VMs (Interpreter) + #[cfg(not(feature="jit"))] + pub fn all() -> Vec { + vec![VMType::Interpreter] + } +} + /// Evm factory. Creates appropriate Evm. -pub struct Factory; +pub struct Factory { + evm : VMType +} impl Factory { - /// Returns jit vm + /// Create fresh instance of VM + pub fn create(&self) -> Box { + match self.evm { + VMType::Jit => { + Factory::jit() + }, + VMType::Interpreter => { + Box::new(super::interpreter::Interpreter) + } + } + } + + /// Create new instance of specific `VMType` factory + pub fn new(evm: VMType) -> Factory { + Factory { + evm: evm + } + } + #[cfg(feature = "jit")] - pub fn create() -> Box { + fn jit() -> Box { Box::new(super::jit::JitEvm) } - /// Returns native rust evm #[cfg(not(feature = "jit"))] - pub fn create() -> Box { - unimplemented!(); + fn jit() -> Box { + unimplemented!() + } + + /// Returns jitvm factory + #[cfg(feature = "jit")] + pub fn default() -> Factory { + Factory { + evm: VMType::Jit + } + } + + /// Returns native rust evm factory + #[cfg(not(feature = "jit"))] + pub fn default() -> Factory { + Factory { + evm: VMType::Interpreter + } } } #[test] fn test_create_vm() { - let _vm = Factory::create(); + let _vm = Factory::default().create(); } + +/// Create tests by injecting different VM factories +#[macro_export] +macro_rules! evm_test( + ($name_test: ident: $name_jit: ident, $name_int: ident) => { + #[test] + #[cfg(feature = "jit")] + fn $name_jit() { + $name_test(Factory::new(VMType::Jit)); + } + #[test] + fn $name_int() { + $name_test(Factory::new(VMType::Interpreter)); + } + } +); + +/// Create ignored tests by injecting different VM factories +#[macro_export] +macro_rules! evm_test_ignore( + ($name_test: ident: $name_jit: ident, $name_int: ident) => { + #[test] + #[ignore] + #[cfg(feature = "jit")] + fn $name_jit() { + $name_test(Factory::new(VMType::Jit)); + } + #[test] + #[ignore] + fn $name_int() { + $name_test(Factory::new(VMType::Interpreter)); + } + } +); diff --git a/src/evm/instructions.rs b/src/evm/instructions.rs new file mode 100644 index 000000000..976c3c603 --- /dev/null +++ b/src/evm/instructions.rs @@ -0,0 +1,534 @@ +//! VM Instructions list and utility functions + +pub type Instruction = u8; + +/// Returns true if given instruction is `PUSHN` instruction. +pub fn is_push(i: Instruction) -> bool { + i >= PUSH1 && i <= PUSH32 +} + +/// Returns number of bytes to read for `PUSHN` instruction +/// PUSH1 -> 1 +pub fn get_push_bytes(i: Instruction) -> usize { + assert!(is_push(i), "Only for PUSH instructions."); + (i - PUSH1 + 1) as usize +} + +#[test] +fn test_get_push_bytes() { + assert_eq!(get_push_bytes(PUSH1), 1); + assert_eq!(get_push_bytes(PUSH3), 3); + assert_eq!(get_push_bytes(PUSH32), 32); +} + +/// Returns stack position of item to duplicate +/// DUP1 -> 0 +pub fn get_dup_position(i: Instruction) -> usize { + assert!(i >= DUP1 && i <= DUP16); + (i - DUP1) as usize +} + +#[test] +fn test_get_dup_position() { + assert_eq!(get_dup_position(DUP1), 0); + assert_eq!(get_dup_position(DUP5), 4); + assert_eq!(get_dup_position(DUP10), 9); +} + +/// Returns stack position of item to SWAP top with +/// SWAP1 -> 1 +pub fn get_swap_position(i: Instruction) -> usize { + assert!(i >= SWAP1 && i <= SWAP16); + (i - SWAP1 + 1) as usize +} + +#[test] +fn test_get_swap_position() { + assert_eq!(get_swap_position(SWAP1), 1); + assert_eq!(get_swap_position(SWAP5), 5); + assert_eq!(get_swap_position(SWAP10), 10); +} + +/// Returns number of topcis to take from stack +/// LOG0 -> 0 +pub fn get_log_topics (i: Instruction) -> usize { + assert!(i >= LOG0 && i <= LOG4); + (i - LOG0) as usize +} + +#[test] +fn test_get_log_topics() { + assert_eq!(get_log_topics(LOG0), 0); + assert_eq!(get_log_topics(LOG2), 2); + assert_eq!(get_log_topics(LOG4), 4); +} + +#[derive(PartialEq)] +pub enum GasPriceTier { + /// 0 Zero + Zero, + /// 2 Quick + Base, + /// 3 Fastest + VeryLow, + /// 5 Fast + Low, + /// 8 Mid + Mid, + /// 10 Slow + High, + /// 20 Ext + Ext, + /// Multiparam or otherwise special + Special, + /// Invalid + Invalid +} + +/// Returns the index in schedule for specific `GasPriceTier` +pub fn get_tier_idx (tier: GasPriceTier) -> usize { + match tier { + GasPriceTier::Zero => 0, + GasPriceTier::Base => 1, + GasPriceTier::VeryLow => 2, + GasPriceTier::Low => 3, + GasPriceTier::Mid => 4, + GasPriceTier::High => 5, + GasPriceTier::Ext => 6, + GasPriceTier::Special => 7, + GasPriceTier::Invalid => 8 + } +} + +pub struct InstructionInfo { + pub name: &'static str, + pub additional: usize, + pub args: usize, + pub ret: usize, + pub side_effects: bool, + pub tier: GasPriceTier +} +impl InstructionInfo { + pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { + InstructionInfo { + name: name, + additional: additional, + args: args, + ret: ret, + side_effects: side_effects, + tier: tier + } + } +} + +/// Return details about specific instruction +pub fn get_info (instruction: Instruction) -> InstructionInfo { + match instruction { + STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), + ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), + SUB => InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow), + MUL => InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low), + DIV => InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low), + SDIV => InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low), + MOD => InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low), + SMOD => InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low), + EXP => InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special), + NOT => InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow), + LT => InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow), + GT => InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow), + SLT => InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow), + SGT => InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow), + EQ => InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow), + ISZERO => InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow), + AND => InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow), + OR => InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow), + XOR => InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow), + BYTE => InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow), + ADDMOD => InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid), + MULMOD => InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid), + SIGNEXTEND => InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low), + SHA3 => InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special), + ADDRESS => InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base), + BALANCE => InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext), + ORIGIN => InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base), + CALLER => InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base), + CALLVALUE => InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base), + CALLDATALOAD => InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow), + CALLDATASIZE => InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base), + CALLDATACOPY => InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow), + CODESIZE => InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base), + CODECOPY => InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow), + GASPRICE => InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base), + EXTCODESIZE => InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext), + EXTCODECOPY => InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext), + BLOCKHASH => InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext), + COINBASE => InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base), + TIMESTAMP => InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base), + NUMBER => InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base), + DIFFICULTY => InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base), + GASLIMIT => InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base), + POP => InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base), + MLOAD => InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow), + MSTORE => InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow), + MSTORE8 => InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow), + SLOAD => InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special), + SSTORE => InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special), + JUMP => InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid), + JUMPI => InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High), + PC => InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base), + MSIZE => InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base), + GAS => InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base), + JUMPDEST => InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special), + PUSH1 => InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow), + PUSH2 => InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow), + PUSH3 => InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow), + PUSH4 => InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow), + PUSH5 => InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow), + PUSH6 => InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow), + PUSH7 => InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow), + PUSH8 => InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow), + PUSH9 => InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow), + PUSH10 => InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow), + PUSH11 => InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow), + PUSH12 => InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow), + PUSH13 => InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow), + PUSH14 => InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow), + PUSH15 => InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow), + PUSH16 => InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow), + PUSH17 => InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow), + PUSH18 => InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow), + PUSH19 => InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow), + PUSH20 => InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow), + PUSH21 => InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow), + PUSH22 => InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow), + PUSH23 => InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow), + PUSH24 => InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow), + PUSH25 => InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow), + PUSH26 => InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow), + PUSH27 => InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow), + PUSH28 => InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow), + PUSH29 => InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow), + PUSH30 => InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow), + PUSH31 => InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow), + PUSH32 => InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow), + DUP1 => InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow), + DUP2 => InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow), + DUP3 => InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow), + DUP4 => InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow), + DUP5 => InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow), + DUP6 => InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow), + DUP7 => InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow), + DUP8 => InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow), + DUP9 => InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow), + DUP10 => InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow), + DUP11 => InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow), + DUP12 => InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow), + DUP13 => InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow), + DUP14 => InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow), + DUP15 => InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow), + DUP16 => InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow), + SWAP1 => InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow), + SWAP2 => InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow), + SWAP3 => InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow), + SWAP4 => InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow), + SWAP5 => InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow), + SWAP6 => InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow), + SWAP7 => InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow), + SWAP8 => InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow), + SWAP9 => InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow), + SWAP10 => InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow), + SWAP11 => InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow), + SWAP12 => InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow), + SWAP13 => InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow), + SWAP14 => InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow), + SWAP15 => InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow), + SWAP16 => InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow), + LOG0 => InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special), + LOG1 => InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special), + LOG2 => InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special), + LOG3 => InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special), + LOG4 => InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special), + CREATE => InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special), + CALL => InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special), + CALLCODE => InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special), + RETURN => InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero), + DELEGATECALL => InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special), + SUICIDE => InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero), + _ => InstructionInfo::new("INVALID_INSTRUCTION", 0, 0, 0, false, GasPriceTier::Invalid) + } +} + +/// Virtual machine bytecode instruction. +/// halts execution +pub const STOP: Instruction = 0x00; +/// addition operation +pub const ADD: Instruction = 0x01; +/// mulitplication operation +pub const MUL: Instruction = 0x02; +/// subtraction operation +pub const SUB: Instruction = 0x03; +/// integer division operation +pub const DIV: Instruction = 0x04; +/// signed integer division operation +pub const SDIV: Instruction = 0x05; +/// modulo remainder operation +pub const MOD: Instruction = 0x06; +/// signed modulo remainder operation +pub const SMOD: Instruction = 0x07; +/// unsigned modular addition +pub const ADDMOD: Instruction = 0x08; +/// unsigned modular multiplication +pub const MULMOD: Instruction = 0x09; +/// exponential operation +pub const EXP: Instruction = 0x0a; +/// extend length of signed integer +pub const SIGNEXTEND: Instruction = 0x0b; + +/// less-than comparision +pub const LT: Instruction = 0x10; +/// greater-than comparision +pub const GT: Instruction = 0x11; +/// signed less-than comparision +pub const SLT: Instruction = 0x12; +/// signed greater-than comparision +pub const SGT: Instruction = 0x13; +/// equality comparision +pub const EQ: Instruction = 0x14; +/// simple not operator +pub const ISZERO: Instruction = 0x15; +/// bitwise AND operation +pub const AND: Instruction = 0x16; +/// bitwise OR operation +pub const OR: Instruction = 0x17; +/// bitwise XOR operation +pub const XOR: Instruction = 0x18; +/// bitwise NOT opertation +pub const NOT: Instruction = 0x19; +/// retrieve single byte from word +pub const BYTE: Instruction = 0x1a; + +/// compute SHA3-256 hash +pub const SHA3: Instruction = 0x20; + +/// get address of currently executing account +pub const ADDRESS: Instruction = 0x30; +/// get balance of the given account +pub const BALANCE: Instruction = 0x31; +/// get execution origination address +pub const ORIGIN: Instruction = 0x32; +/// get caller address +pub const CALLER: Instruction = 0x33; +/// get deposited value by the instruction/transaction responsible for this execution +pub const CALLVALUE: Instruction = 0x34; +/// get input data of current environment +pub const CALLDATALOAD: Instruction = 0x35; +/// get size of input data in current environment +pub const CALLDATASIZE: Instruction = 0x36; +/// copy input data in current environment to memory +pub const CALLDATACOPY: Instruction = 0x37; +/// get size of code running in current environment +pub const CODESIZE: Instruction = 0x38; +/// copy code running in current environment to memory +pub const CODECOPY: Instruction = 0x39; +/// get price of gas in current environment +pub const GASPRICE: Instruction = 0x3a; +/// get external code size (from another contract) +pub const EXTCODESIZE: Instruction = 0x3b; +/// copy external code (from another contract) +pub const EXTCODECOPY: Instruction = 0x3c; + +/// get hash of most recent complete block +pub const BLOCKHASH: Instruction = 0x40; +/// get the block's coinbase address +pub const COINBASE: Instruction = 0x41; +/// get the block's timestamp +pub const TIMESTAMP: Instruction = 0x42; +/// get the block's number +pub const NUMBER: Instruction = 0x43; +/// get the block's difficulty +pub const DIFFICULTY: Instruction = 0x44; +/// get the block's gas limit +pub const GASLIMIT: Instruction = 0x45; + +/// remove item from stack +pub const POP: Instruction = 0x50; +/// load word from memory +pub const MLOAD: Instruction = 0x51; +/// save word to memory +pub const MSTORE: Instruction = 0x52; +/// save byte to memory +pub const MSTORE8: Instruction = 0x53; +/// load word from storage +pub const SLOAD: Instruction = 0x54; +/// save word to storage +pub const SSTORE: Instruction = 0x55; +/// alter the program counter +pub const JUMP: Instruction = 0x56; +/// conditionally alter the program counter +pub const JUMPI: Instruction = 0x57; +/// get the program counter +pub const PC: Instruction = 0x58; +/// get the size of active memory +pub const MSIZE: Instruction = 0x59; +/// get the amount of available gas +pub const GAS: Instruction = 0x5a; +/// set a potential jump destination +pub const JUMPDEST: Instruction = 0x5b; + +/// place 1 byte item on stack +pub const PUSH1: Instruction = 0x60; +/// place 2 byte item on stack +pub const PUSH2: Instruction = 0x61; +/// place 3 byte item on stack +pub const PUSH3: Instruction = 0x62; +/// place 4 byte item on stack +pub const PUSH4: Instruction = 0x63; +/// place 5 byte item on stack +pub const PUSH5: Instruction = 0x64; +/// place 6 byte item on stack +pub const PUSH6: Instruction = 0x65; +/// place 7 byte item on stack +pub const PUSH7: Instruction = 0x66; +/// place 8 byte item on stack +pub const PUSH8: Instruction = 0x67; +/// place 9 byte item on stack +pub const PUSH9: Instruction = 0x68; +/// place 10 byte item on stack +pub const PUSH10: Instruction = 0x69; +/// place 11 byte item on stack +pub const PUSH11: Instruction = 0x6a; +/// place 12 byte item on stack +pub const PUSH12: Instruction = 0x6b; +/// place 13 byte item on stack +pub const PUSH13: Instruction = 0x6c; +/// place 14 byte item on stack +pub const PUSH14: Instruction = 0x6d; +/// place 15 byte item on stack +pub const PUSH15: Instruction = 0x6e; +/// place 16 byte item on stack +pub const PUSH16: Instruction = 0x6f; +/// place 17 byte item on stack +pub const PUSH17: Instruction = 0x70; +/// place 18 byte item on stack +pub const PUSH18: Instruction = 0x71; +/// place 19 byte item on stack +pub const PUSH19: Instruction = 0x72; +/// place 20 byte item on stack +pub const PUSH20: Instruction = 0x73; +/// place 21 byte item on stack +pub const PUSH21: Instruction = 0x74; +/// place 22 byte item on stack +pub const PUSH22: Instruction = 0x75; +/// place 23 byte item on stack +pub const PUSH23: Instruction = 0x76; +/// place 24 byte item on stack +pub const PUSH24: Instruction = 0x77; +/// place 25 byte item on stack +pub const PUSH25: Instruction = 0x78; +/// place 26 byte item on stack +pub const PUSH26: Instruction = 0x79; +/// place 27 byte item on stack +pub const PUSH27: Instruction = 0x7a; +/// place 28 byte item on stack +pub const PUSH28: Instruction = 0x7b; +/// place 29 byte item on stack +pub const PUSH29: Instruction = 0x7c; +/// place 30 byte item on stack +pub const PUSH30: Instruction = 0x7d; +/// place 31 byte item on stack +pub const PUSH31: Instruction = 0x7e; +/// place 32 byte item on stack +pub const PUSH32: Instruction = 0x7f; + +/// copies the highest item in the stack to the top of the stack +pub const DUP1: Instruction = 0x80; +/// copies the second highest item in the stack to the top of the stack +pub const DUP2: Instruction = 0x81; +/// copies the third highest item in the stack to the top of the stack +pub const DUP3: Instruction = 0x82; +/// copies the 4th highest item in the stack to the top of the stack +pub const DUP4: Instruction = 0x83; +/// copies the 5th highest item in the stack to the top of the stack +pub const DUP5: Instruction = 0x84; +/// copies the 6th highest item in the stack to the top of the stack +pub const DUP6: Instruction = 0x85; +/// copies the 7th highest item in the stack to the top of the stack +pub const DUP7: Instruction = 0x86; +/// copies the 8th highest item in the stack to the top of the stack +pub const DUP8: Instruction = 0x87; +/// copies the 9th highest item in the stack to the top of the stack +pub const DUP9: Instruction = 0x88; +/// copies the 10th highest item in the stack to the top of the stack +pub const DUP10: Instruction = 0x89; +/// copies the 11th highest item in the stack to the top of the stack +pub const DUP11: Instruction = 0x8a; +/// copies the 12th highest item in the stack to the top of the stack +pub const DUP12: Instruction = 0x8b; +/// copies the 13th highest item in the stack to the top of the stack +pub const DUP13: Instruction = 0x8c; +/// copies the 14th highest item in the stack to the top of the stack +pub const DUP14: Instruction = 0x8d; +/// copies the 15th highest item in the stack to the top of the stack +pub const DUP15: Instruction = 0x8e; +/// copies the 16th highest item in the stack to the top of the stack +pub const DUP16: Instruction = 0x8f; + +/// swaps the highest and second highest value on the stack +pub const SWAP1: Instruction = 0x90; +/// swaps the highest and third highest value on the stack +pub const SWAP2: Instruction = 0x91; +/// swaps the highest and 4th highest value on the stack +pub const SWAP3: Instruction = 0x92; +/// swaps the highest and 5th highest value on the stack +pub const SWAP4: Instruction = 0x93; +/// swaps the highest and 6th highest value on the stack +pub const SWAP5: Instruction = 0x94; +/// swaps the highest and 7th highest value on the stack +pub const SWAP6: Instruction = 0x95; +/// swaps the highest and 8th highest value on the stack +pub const SWAP7: Instruction = 0x96; +/// swaps the highest and 9th highest value on the stack +pub const SWAP8: Instruction = 0x97; +/// swaps the highest and 10th highest value on the stack +pub const SWAP9: Instruction = 0x98; +/// swaps the highest and 11th highest value on the stack +pub const SWAP10: Instruction = 0x99; +/// swaps the highest and 12th highest value on the stack +pub const SWAP11: Instruction = 0x9a; +/// swaps the highest and 13th highest value on the stack +pub const SWAP12: Instruction = 0x9b; +/// swaps the highest and 14th highest value on the stack +pub const SWAP13: Instruction = 0x9c; +/// swaps the highest and 15th highest value on the stack +pub const SWAP14: Instruction = 0x9d; +/// swaps the highest and 16th highest value on the stack +pub const SWAP15: Instruction = 0x9e; +/// swaps the highest and 17th highest value on the stack +pub const SWAP16: Instruction = 0x9f; + +/// Makes a log entry; no topics. +pub const LOG0: Instruction = 0xa0; +/// Makes a log entry; 1 topic. +pub const LOG1: Instruction = 0xa1; +/// Makes a log entry; 2 topics. +pub const LOG2: Instruction = 0xa2; +/// Makes a log entry; 3 topics. +pub const LOG3: Instruction = 0xa3; +/// Makes a log entry; 4 topics. +pub const LOG4: Instruction = 0xa4; +/// Maximal number of topics for log instructions +pub const MAX_NO_OF_TOPICS : usize = 4; + +/// create a new account with associated code +pub const CREATE: Instruction = 0xf0; +/// message-call into an account +pub const CALL: Instruction = 0xf1; +/// message-call with another account's code only +pub const CALLCODE: Instruction = 0xf2; +/// halt execution returning output data +pub const RETURN: Instruction = 0xf3; +/// like CALLCODE but keeps caller's value and sender +pub const DELEGATECALL: Instruction = 0xf4; +/// halt execution and register account for later deletion +pub const SUICIDE: Instruction = 0xff; + diff --git a/src/evm/interpreter.rs b/src/evm/interpreter.rs new file mode 100644 index 000000000..e6499b022 --- /dev/null +++ b/src/evm/interpreter.rs @@ -0,0 +1,1210 @@ +///! Rust VM implementation + +use common::*; +use evm; +use super::instructions as instructions; +use super::instructions::Instruction; +use std::num::wrapping::OverflowingOps; +use std::marker::Copy; +use evm::{MessageCallResult, ContractCreateResult}; + +#[cfg(not(feature = "evm_debug"))] +macro_rules! evm_debug { + ($x: expr) => {} +} + +#[cfg(feature = "evm_debug")] +macro_rules! evm_debug { + ($x: expr) => { + $x + } +} + +#[cfg(feature = "evm_debug")] +fn color(instruction: Instruction, name: &'static str) -> String { + let c = instruction as usize % 6; + let colors = [31, 34, 33, 32, 35, 36]; + format!("\x1B[1;{}m{}\x1B[0m", colors[c], name) +} + +type CodePosition = usize; +type Gas = U256; +type ProgramCounter = usize; + +/// Stack trait with VM-friendly API +trait Stack { + /// Returns `Stack[len(Stack) - no_from_top]` + fn peek(&self, no_from_top: usize) -> &T; + /// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top] + fn swap_with_top(&mut self, no_from_top: usize); + /// Returns true if Stack has at least `no_of_elems` elements + fn has(&self, no_of_elems: usize) -> bool; + /// Get element from top and remove it from Stack. Panics if stack is empty. + fn pop_back(&mut self) -> T; + /// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty. + fn pop_n(&mut self, no_of_elems: usize) -> &[T]; + /// Add element on top of the Stack + fn push(&mut self, elem: T); + /// Get number of elements on Stack + fn size(&self) -> usize; +} + +struct VecStack { + stack: Vec, + logs: [S; instructions::MAX_NO_OF_TOPICS] +} + +impl VecStack { + fn with_capacity(capacity: usize, zero: S) -> Self { + VecStack { + stack: Vec::with_capacity(capacity), + logs: [zero; instructions::MAX_NO_OF_TOPICS] + } + } +} + +impl Stack for VecStack { + fn peek(&self, no_from_top: usize) -> &S { + return &self.stack[self.stack.len() - no_from_top - 1]; + } + + fn swap_with_top(&mut self, no_from_top: usize) { + let len = self.stack.len(); + self.stack.swap(len - no_from_top - 1, len - 1); + } + + fn has(&self, no_of_elems: usize) -> bool { + self.stack.len() >= no_of_elems + } + + fn pop_back(&mut self) -> S { + let val = self.stack.pop(); + match val { + Some(x) => { + evm_debug!({ + println!(" POP: {}", x) + }); + x + }, + None => panic!("Tried to pop from empty stack.") + } + } + + fn pop_n(&mut self, no_of_elems: usize) -> &[S] { + assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS); + + for i in 0..no_of_elems { + self.logs[i] = self.pop_back(); + } + &self.logs[0..no_of_elems] + } + + fn push(&mut self, elem: S) { + evm_debug!({ + println!(" PUSH: {}", elem) + }); + self.stack.push(elem); + } + + fn size(&self) -> usize { + self.stack.len() + } +} + +trait Memory { + /// Retrieve current size of the memory + fn size(&self) -> usize; + /// Resize (shrink or expand) the memory to specified size (fills 0) + fn resize(&mut self, new_size: usize); + /// Resize the memory only if its smaller + fn expand(&mut self, new_size: usize); + /// Write single byte to memory + fn write_byte(&mut self, offset: U256, value: U256); + /// Write a word to memory. Does not resize memory! + fn write(&mut self, offset: U256, value: U256); + /// Read a word from memory + fn read(&self, offset: U256) -> U256; + /// Write slice of bytes to memory. Does not resize memory! + fn write_slice(&mut self, offset: U256, &[u8]); + /// Retrieve part of the memory between offset and offset + size + fn read_slice(&self, offset: U256, size: U256) -> &[u8]; + /// Retrieve writeable part of memory + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8]; + fn dump(&self); +} + +/// Checks whether offset and size is valid memory range +fn is_valid_range(off: usize, size: usize) -> bool { + // When size is zero we haven't actually expanded the memory + let (_a, overflow) = off.overflowing_add(size); + size > 0 && !overflow +} + +impl Memory for Vec { + fn dump(&self) { + println!("MemoryDump:"); + for i in self.iter() { + println!("{:02x} ", i); + } + println!(""); + } + + fn size(&self) -> usize { + return self.len() + } + + fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] { + let off = init_off_u.low_u64() as usize; + let size = init_size_u.low_u64() as usize; + if !is_valid_range(off, size) { + &self[0..0] + } else { + &self[off..off+size] + } + } + + fn read(&self, offset: U256) -> U256 { + let off = offset.low_u64() as usize; + U256::from(&self[off..off+32]) + } + + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { + let off = offset.low_u64() as usize; + let s = size.low_u64() as usize; + if !is_valid_range(off, s) { + &mut self[0..0] + } else { + &mut self[off..off+s] + } + } + + fn write_slice(&mut self, offset: U256, slice: &[u8]) { + let off = offset.low_u64() as usize; + + // TODO [todr] Optimize? + for pos in off..off+slice.len() { + self[pos] = slice[pos - off]; + } + } + + fn write(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + let mut val = value; + + let end = off + 32; + for pos in 0..32 { + self[end - pos - 1] = val.low_u64() as u8; + val = val >> 8; + } + } + + fn write_byte(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + let val = value.low_u64() as u64; + self[off] = val as u8; + } + + fn resize(&mut self, new_size: usize) { + self.resize(new_size, 0); + } + + fn expand(&mut self, size: usize) { + if size > self.len() { + Memory::resize(self, size) + } + } +} + +/// Abstraction over raw vector of Bytes. Easier state management of PC. +struct CodeReader<'a> { + position: ProgramCounter, + code: &'a Bytes +} + +impl<'a> CodeReader<'a> { + /// Get `no_of_bytes` from code and convert to U256. Move PC + fn read(&mut self, no_of_bytes: usize) -> U256 { + let pos = self.position; + self.position += no_of_bytes; + let max = cmp::min(pos + no_of_bytes, self.code.len()); + U256::from(&self.code[pos..max]) + } + + fn len (&self) -> usize { + self.code.len() + } +} + +enum RequiredMem { + Mem(U256), + OutOfMemory +} + +enum InstructionCost { + Gas(U256), + GasMem(U256, RequiredMem), + GasMemCopy(U256, RequiredMem, U256) +} + +enum InstructionResult { + Ok, + UseAllGas, + GasLeft(U256), + UnusedGas(U256), + JumpToPosition(U256), + StopExecutionWithGasLeft(U256), + StopExecution +} + +/// Intepreter EVM implementation +pub struct Interpreter; + +impl evm::Evm for Interpreter { + fn exec(&self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result { + let code = ¶ms.code.clone().unwrap(); + let valid_jump_destinations = self.find_jump_destinations(&code); + + let mut current_gas = params.gas.clone(); + let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); + let mut mem = vec![]; + let mut reader = CodeReader { + position: 0, + code: &code + }; + + while reader.position < code.len() { + let instruction = code[reader.position]; + reader.position += 1; + + // Calculate gas cost + let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); + try!(self.verify_gas(¤t_gas, &gas_cost)); + mem.expand(mem_size); + current_gas -= gas_cost; + + evm_debug!({ + println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}", + reader.position, + color(instruction, instructions::get_info(instruction).name), + instruction, + gas_cost, + current_gas + gas_cost + ); + }); + + // Execute instruction + let result = try!(self.exec_instruction( + current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack + )); + + // Advance + match result { + InstructionResult::Ok => {}, + InstructionResult::UnusedGas(gas) => { + current_gas += gas; + }, + InstructionResult::UseAllGas => { + current_gas = U256::zero(); + }, + InstructionResult::GasLeft(gas_left) => { + current_gas = gas_left; + }, + InstructionResult::JumpToPosition(position) => { + let pos = try!(self.verify_jump(position, &valid_jump_destinations)); + reader.position = pos; + }, + InstructionResult::StopExecutionWithGasLeft(gas_left) => { + current_gas = gas_left; + reader.position = code.len(); + }, + InstructionResult::StopExecution => { + reader.position = code.len(); + } + } + } + + Ok(current_gas) + } +} + +impl Interpreter { + fn get_gas_cost_mem(&self, + ext: &evm::Ext, + instruction: Instruction, + mem: &mut Memory, + stack: &Stack + ) -> Result<(U256, usize), evm::Error> { + let schedule = ext.schedule(); + let info = instructions::get_info(instruction); + + if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL { + return Err(evm::Error::BadInstruction { + instruction: instruction + }); + } + if info.tier == instructions::GasPriceTier::Invalid { + return Err(evm::Error::BadInstruction { + instruction: instruction + }); + } + + try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack)); + + let tier = instructions::get_tier_idx(info.tier); + let default_gas = U256::from(schedule.tier_step_gas[tier]); + + let cost = match instruction { + instructions::SSTORE => { + let address = H256::from(stack.peek(0)); + let newval = stack.peek(1); + let val = U256::from(ext.storage_at(&address).as_slice()); + + let gas = if self.is_zero(&val) && !self.is_zero(newval) { + schedule.sstore_set_gas + } else if !self.is_zero(&val) && self.is_zero(newval) { + // Refund is added when actually executing sstore + schedule.sstore_reset_gas + } else { + schedule.sstore_reset_gas + }; + InstructionCost::Gas(U256::from(gas)) + }, + instructions::SLOAD => { + InstructionCost::Gas(U256::from(schedule.sload_gas)) + }, + instructions::MSTORE => { + InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32)) + }, + instructions::MLOAD => { + InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 32)) + }, + instructions::MSTORE8 => { + InstructionCost::GasMem(default_gas, self.mem_needed_const(stack.peek(0), 1)) + }, + instructions::RETURN => { + InstructionCost::GasMem(default_gas, self.mem_needed(stack.peek(0), stack.peek(1))) + }, + instructions::SHA3 => { + match add_u256_usize(stack.peek(1), 31) { + (_w, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory), + (w, false) => { + let words = w >> 5; + let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words); + InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1))) + } + } + }, + instructions::CALLDATACOPY => { + InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(0), stack.peek(2)), stack.peek(2).clone()) + }, + instructions::CODECOPY => { + InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(0), stack.peek(2)), stack.peek(2).clone()) + }, + instructions::EXTCODECOPY => { + InstructionCost::GasMemCopy(default_gas, self.mem_needed(stack.peek(1), stack.peek(3)), stack.peek(3).clone()) + }, + instructions::JUMPDEST => { + InstructionCost::Gas(U256::one()) + }, + instructions::LOG0...instructions::LOG4 => { + let no_of_topics = instructions::get_log_topics(instruction); + let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; + // TODO [todr] potential overflow of datagass + let data_gas = stack.peek(1).clone() * U256::from(schedule.log_data_gas); + let gas = try!(self.gas_add(data_gas, U256::from(log_gas))); + InstructionCost::GasMem(gas, self.mem_needed(stack.peek(0), stack.peek(1))) + }, + instructions::CALL | instructions::CALLCODE => { + match add_u256_usize(stack.peek(0), schedule.call_gas) { + (_gas, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory), + (mut gas, false) => { + let mem = self.mem_max( + self.mem_needed(stack.peek(5), stack.peek(6)), + self.mem_needed(stack.peek(3), stack.peek(4)) + ); + + let address = u256_to_address(stack.peek(1)); + + // TODO [todr] Potential overflows + if instruction == instructions::CALL && !ext.exists(&address) { + gas = gas + U256::from(schedule.call_new_account_gas); + }; + + if stack.peek(2).clone() > U256::zero() { + gas = gas + U256::from(schedule.call_value_transfer_gas) + }; + + InstructionCost::GasMem(gas,mem) + } + } + }, + instructions::DELEGATECALL => { + match add_u256_usize(stack.peek(0), schedule.call_gas) { + (_gas, true) => InstructionCost::GasMem(U256::zero(), RequiredMem::OutOfMemory), + (gas, false) => { + let mem = self.mem_max( + self.mem_needed(stack.peek(4), stack.peek(5)), + self.mem_needed(stack.peek(2), stack.peek(3)) + ); + InstructionCost::GasMem(gas, mem) + } + } + }, + instructions::CREATE => { + let gas = U256::from(schedule.create_gas); + let mem = self.mem_needed(stack.peek(1), stack.peek(2)); + InstructionCost::GasMem(gas, mem) + }, + instructions::EXP => { + let expon = stack.peek(1); + let bytes = ((expon.bits() + 7) / 8) as usize; + let gas = U256::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); + InstructionCost::Gas(gas) + }, + _ => InstructionCost::Gas(default_gas) + }; + + match cost { + InstructionCost::Gas(gas) => { + Ok((gas, 0)) + }, + InstructionCost::GasMem(gas, mem_size) => match mem_size { + RequiredMem::Mem(mem_size) => { + let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size); + let gas = try!(self.gas_add(gas, mem_gas)); + Ok((gas, new_mem_size)) + }, + RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas) + }, + InstructionCost::GasMemCopy(gas, mem_size, copy) => match mem_size { + RequiredMem::Mem(mem_size) => { + let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size); + match add_u256_usize(©, 31) { + (_c, true) => Err(evm::Error::OutOfGas), + (copy, false) => { + let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32)); + let gas = try!(self.gas_add(try!(self.gas_add(gas, copy_gas)), mem_gas)); + Ok((gas, new_mem_size)) + } + } + }, + RequiredMem::OutOfMemory => Err(evm::Error::OutOfGas) + } + } + } + + fn gas_add(&self, a: U256, b: U256) -> Result { + match a.overflowing_add(b) { + (_val, true) => Err(evm::Error::OutOfGas), + (val, false) => Ok(val) + } + } + + fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> (U256, usize) { + let gas_for_mem = |mem_size: U256| { + let s = mem_size >> 5; + s * U256::from(schedule.memory_gas) + s * s / U256::from(schedule.quad_coeff_div) + }; + let current_mem_size = U256::from(current_mem_size); + let req_mem_size_rounded = ((mem_size.clone() + U256::from(31)) >> 5) << 5; + let new_mem_gas = gas_for_mem(U256::from(req_mem_size_rounded)); + let current_mem_gas = gas_for_mem(current_mem_size); + + (if req_mem_size_rounded > current_mem_size { + new_mem_gas - current_mem_gas + } else { + U256::zero() + }, req_mem_size_rounded.low_u64() as usize) + } + + fn mem_max(&self, m_a: RequiredMem, m_b: RequiredMem) -> RequiredMem { + match (m_a, m_b) { + (RequiredMem::Mem(a), RequiredMem::Mem(b)) => { + RequiredMem::Mem(cmp::max(a, b)) + }, + (RequiredMem::OutOfMemory, _) | (_, RequiredMem::OutOfMemory) => { + RequiredMem::OutOfMemory + } + } + } + + fn mem_needed_const(&self, mem: &U256, add: usize) -> RequiredMem { + match mem.overflowing_add(U256::from(add)) { + (_, true) => RequiredMem::OutOfMemory, + (mem, false) => RequiredMem::Mem(mem) + } + } + + fn mem_needed(&self, offset: &U256, size: &U256) -> RequiredMem { + if self.is_zero(size) { + return RequiredMem::Mem(U256::zero()); + } + + match offset.clone().overflowing_add(size.clone()) { + (_result, true) => RequiredMem::OutOfMemory, + (result, false) => { + RequiredMem::Mem(result) + } + } + } + + fn exec_instruction(&self, + gas: Gas, + params: &ActionParams, + ext: &mut evm::Ext, + instruction: Instruction, + code: &mut CodeReader, + mem: &mut Memory, + stack: &mut Stack + ) -> Result { + match instruction { + instructions::JUMP => { + let jump = stack.pop_back(); + return Ok(InstructionResult::JumpToPosition( + jump + )); + }, + instructions::JUMPI => { + let jump = stack.pop_back(); + let condition = stack.pop_back(); + if !self.is_zero(&condition) { + return Ok(InstructionResult::JumpToPosition( + jump + )); + } + }, + instructions::JUMPDEST => { + // ignore + }, + instructions::CREATE => { + let endowment = stack.pop_back(); + let init_off = stack.pop_back(); + let init_size = stack.pop_back(); + + let contract_code = mem.read_slice(init_off, init_size); + let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth; + + if !can_create { + stack.push(U256::zero()); + return Ok(InstructionResult::Ok); + } + + let create_result = ext.create(&gas, &endowment, &contract_code); + return match create_result { + ContractCreateResult::Created(address, gas_left) => { + stack.push(address_to_u256(address)); + Ok(InstructionResult::GasLeft(gas_left)) + }, + ContractCreateResult::Failed => { + stack.push(U256::zero()); + // TODO [todr] Should we just StopExecution here? + Ok(InstructionResult::UseAllGas) + } + }; + }, + instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => { + assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible"); + let call_gas = stack.pop_back(); + let code_address = stack.pop_back(); + let code_address = u256_to_address(&code_address); + let is_delegatecall = instruction == instructions::DELEGATECALL; + + let value = match is_delegatecall { + true => params.value, + false => stack.pop_back() + }; + + let address = match instruction == instructions::CALL { + true => &code_address, + false => ¶ms.address + }; + + let in_off = stack.pop_back(); + let in_size = stack.pop_back(); + let out_off = stack.pop_back(); + let out_size = stack.pop_back(); + + let call_gas = call_gas + match !is_delegatecall && value > U256::zero() { + true => U256::from(ext.schedule().call_stipend), + false => U256::zero() + }; + + let can_call = (is_delegatecall || ext.balance(¶ms.address) >= value) && ext.depth() < ext.schedule().max_depth; + + if !can_call { + stack.push(U256::zero()); + return Ok(InstructionResult::UnusedGas(call_gas)); + } + + let call_result = { + // we need to write and read from memory in the same time + // and we don't want to copy + let input = unsafe { ::std::mem::transmute(mem.read_slice(in_off, in_size)) }; + let output = mem.writeable_slice(out_off, out_size); + ext.call(&call_gas, address, &value, input, &code_address, output) + }; + + return match call_result { + MessageCallResult::Success(gas_left) => { + println!("Unused: {}", gas_left); + stack.push(U256::one()); + Ok(InstructionResult::UnusedGas(gas_left)) + }, + MessageCallResult::Failed => { + stack.push(U256::zero()); + Ok(InstructionResult::Ok) + } + }; + }, + instructions::RETURN => { + let init_off = stack.pop_back(); + let init_size = stack.pop_back(); + let return_code = mem.read_slice(init_off, init_size); + let gas_left = try!(ext.ret(&gas, &return_code)); + return Ok(InstructionResult::StopExecutionWithGasLeft( + gas_left + )); + }, + instructions::STOP => { + return Ok(InstructionResult::StopExecution); + }, + instructions::SUICIDE => { + let address = stack.pop_back(); + ext.suicide(&u256_to_address(&address)); + return Ok(InstructionResult::StopExecution); + }, + instructions::LOG0...instructions::LOG4 => { + let no_of_topics = instructions::get_log_topics(instruction); + + let offset = stack.pop_back(); + let size = stack.pop_back(); + let topics = stack.pop_n(no_of_topics) + .iter() + .map(H256::from) + .collect(); + ext.log(topics, mem.read_slice(offset, size)); + }, + instructions::PUSH1...instructions::PUSH32 => { + let bytes = instructions::get_push_bytes(instruction); + let val = code.read(bytes); + stack.push(val); + }, + instructions::MLOAD => { + let word = mem.read(stack.pop_back()); + stack.push(U256::from(word)); + }, + instructions::MSTORE => { + let offset = stack.pop_back(); + let word = stack.pop_back(); + mem.write(offset, word); + }, + instructions::MSTORE8 => { + let offset = stack.pop_back(); + let byte = stack.pop_back(); + mem.write_byte(offset, byte); + }, + instructions::MSIZE => { + stack.push(U256::from(mem.size())); + }, + instructions::SHA3 => { + let offset = stack.pop_back(); + let size = stack.pop_back(); + let sha3 = mem.read_slice(offset, size).sha3(); + stack.push(U256::from(sha3.as_slice())); + }, + instructions::SLOAD => { + let key = H256::from(&stack.pop_back()); + let word = U256::from(ext.storage_at(&key).as_slice()); + stack.push(word); + }, + instructions::SSTORE => { + let address = H256::from(&stack.pop_back()); + let val = stack.pop_back(); + + let current_val = U256::from(ext.storage_at(&address).as_slice()); + // Increase refund for clear + if !self.is_zero(¤t_val) && self.is_zero(&val) { + ext.inc_sstore_clears(); + } + ext.set_storage(address, H256::from(&val)); + }, + instructions::PC => { + stack.push(U256::from(code.position - 1)); + }, + instructions::GAS => { + stack.push(gas.clone()); + }, + instructions::ADDRESS => { + stack.push(address_to_u256(params.address.clone())); + }, + instructions::ORIGIN => { + stack.push(address_to_u256(params.origin.clone())); + }, + instructions::BALANCE => { + let address = u256_to_address(&stack.pop_back()); + let balance = ext.balance(&address); + stack.push(balance); + }, + instructions::CALLER => { + stack.push(address_to_u256(params.sender.clone())); + }, + instructions::CALLVALUE => { + stack.push(params.value.clone()); + }, + instructions::CALLDATALOAD => { + let big_id = stack.pop_back(); + let id = big_id.low_u64() as usize; + let max = id.wrapping_add(32); + let data = params.data.clone().unwrap_or(vec![]); + let bound = cmp::min(data.len(), max); + if id < bound && big_id < U256::from(data.len()) { + let mut v = data[id..bound].to_vec(); + v.resize(32, 0); + stack.push(U256::from(&v[..])) + } else { + stack.push(U256::zero()) + } + }, + instructions::CALLDATASIZE => { + stack.push(U256::from(params.data.clone().unwrap_or(vec![]).len())); + }, + instructions::CODESIZE => { + stack.push(U256::from(code.len())); + }, + instructions::EXTCODESIZE => { + let address = u256_to_address(&stack.pop_back()); + let len = ext.extcode(&address).len(); + stack.push(U256::from(len)); + }, + instructions::CALLDATACOPY => { + self.copy_data_to_memory(mem, stack, ¶ms.data.clone().unwrap_or(vec![])); + }, + instructions::CODECOPY => { + self.copy_data_to_memory(mem, stack, ¶ms.code.clone().unwrap_or(vec![])); + }, + instructions::EXTCODECOPY => { + let address = u256_to_address(&stack.pop_back()); + let code = ext.extcode(&address); + self.copy_data_to_memory(mem, stack, &code); + }, + instructions::GASPRICE => { + stack.push(params.gas_price.clone()); + }, + instructions::BLOCKHASH => { + let block_number = stack.pop_back(); + let block_hash = ext.blockhash(&block_number); + stack.push(U256::from(block_hash.as_slice())); + }, + instructions::COINBASE => { + stack.push(address_to_u256(ext.env_info().author.clone())); + }, + instructions::TIMESTAMP => { + stack.push(U256::from(ext.env_info().timestamp)); + }, + instructions::NUMBER => { + stack.push(U256::from(ext.env_info().number)); + }, + instructions::DIFFICULTY => { + stack.push(ext.env_info().difficulty.clone()); + }, + instructions::GASLIMIT => { + stack.push(ext.env_info().gas_limit.clone()); + }, + _ => { + try!(self.exec_stack_instruction(instruction, stack)); + } + }; + Ok(InstructionResult::Ok) + } + + fn copy_data_to_memory(&self, + mem: &mut Memory, + stack: &mut Stack, + data: &Bytes) { + let offset = stack.pop_back(); + let index = stack.pop_back(); + let size = stack.pop_back(); + let data_size = data.len(); + + if index < U256::from(data_size) { + let u_index = index.low_u64() as usize; + let bound_size = match size + index > U256::from(data_size) { + true => data_size, + false => size.low_u64() as usize + u_index + }; + + mem.write_slice(offset, &data[u_index..bound_size]); + } + } + + fn verify_instructions_requirements(&self, + info: &instructions::InstructionInfo, + stack_limit: usize, + stack: &Stack) -> Result<(), evm::Error> { + if !stack.has(info.args) { + Err(evm::Error::StackUnderflow { + instruction: info.name, + wanted: info.args, + on_stack: stack.size() + }) + } else if stack.size() - info.args + info.ret > stack_limit { + Err(evm::Error::OutOfStack { + instruction: info.name, + wanted: info.ret - info.args, + limit: stack_limit + }) + } else { + Ok(()) + } + } + + fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> Result<(), evm::Error> { + match current_gas < gas_cost { + true => Err(evm::Error::OutOfGas), + false => Ok(()) + } + } + + fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet) -> Result { + let jump = jump_u.low_u64() as usize; + + if valid_jump_destinations.contains(&jump) && jump_u < U256::from(!0 as usize) { + Ok(jump) + } else { + Err(evm::Error::BadJumpDestination { + destination: jump + }) + } + } + + fn is_zero(&self, val: &U256) -> bool { + &U256::zero() == val + } + + fn bool_to_u256(&self, val: bool) -> U256 { + if val { + U256::one() + } else { + U256::zero() + } + } + + fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack) -> Result<(), evm::Error> { + match instruction { + instructions::DUP1...instructions::DUP16 => { + let position = instructions::get_dup_position(instruction); + let val = stack.peek(position).clone(); + stack.push(val); + }, + instructions::SWAP1...instructions::SWAP16 => { + let position = instructions::get_swap_position(instruction); + stack.swap_with_top(position) + }, + instructions::POP => { + stack.pop_back(); + }, + instructions::ADD => { + let a = stack.pop_back(); + let b = stack.pop_back(); + let (c, _overflow) = a.overflowing_add(b); + stack.push(c); + }, + instructions::MUL => { + let a = stack.pop_back(); + let b = stack.pop_back(); + let (c, _overflow) = a.overflowing_mul(b); + stack.push(c); + }, + instructions::SUB => { + let a = stack.pop_back(); + let b = stack.pop_back(); + let (c, _overflow) = a.overflowing_sub(b); + stack.push(c); + }, + instructions::DIV => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(match !self.is_zero(&b) { + true => { + let (c, _overflow) = a.overflowing_div(b); + c + }, + false => U256::zero() + }); + }, + instructions::MOD => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(match !self.is_zero(&b) { + true => { + let (c, _overflow) = a.overflowing_rem(b); + c + }, + false => U256::zero() + }); + }, + instructions::SDIV => { + let (a, sign_a) = get_and_reset_sign(stack.pop_back()); + let (b, sign_b) = get_and_reset_sign(stack.pop_back()); + + // -2^255 + let min = (U256::one() << 255) - U256::one(); + stack.push(if self.is_zero(&b) { + U256::zero() + } else if a == min && b == !U256::zero() { + min + } else { + let (c, _overflow) = a.overflowing_div(b); + set_sign(c, sign_a ^ sign_b) + }); + }, + instructions::SMOD => { + let ua = stack.pop_back(); + let ub = stack.pop_back(); + let (a, sign_a) = get_and_reset_sign(ua); + let (b, _sign_b) = get_and_reset_sign(ub); + + stack.push(if !self.is_zero(&b) { + let (c, _overflow) = a.overflowing_rem(b); + set_sign(c, sign_a) + } else { + U256::zero() + }); + }, + instructions::EXP => { + let base = stack.pop_back(); + let expon = stack.pop_back(); + let (res, _overflow) = base.overflowing_pow(expon); + stack.push(res); + }, + instructions::NOT => { + let a = stack.pop_back(); + stack.push(!a); + }, + instructions::LT => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(self.bool_to_u256(a < b)); + }, + instructions::SLT => { + let (a, neg_a) = get_and_reset_sign(stack.pop_back()); + let (b, neg_b) = get_and_reset_sign(stack.pop_back()); + + let is_positive_lt = a < b && (neg_a | neg_b) == false; + let is_negative_lt = a > b && (neg_a & neg_b) == true; + let has_different_signs = neg_a == true && neg_b == false; + + stack.push(self.bool_to_u256(is_positive_lt | is_negative_lt | has_different_signs)); + }, + instructions::GT => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(self.bool_to_u256(a > b)); + }, + instructions::SGT => { + let (a, neg_a) = get_and_reset_sign(stack.pop_back()); + let (b, neg_b) = get_and_reset_sign(stack.pop_back()); + + let is_positive_gt = a > b && (neg_a | neg_b) == false; + let is_negative_gt = a < b && (neg_a & neg_b) == true; + let has_different_signs = neg_a == false && neg_b == true; + + stack.push(self.bool_to_u256(is_positive_gt | is_negative_gt | has_different_signs)); + }, + instructions::EQ => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(self.bool_to_u256(a == b)); + }, + instructions::ISZERO => { + let a = stack.pop_back(); + stack.push(self.bool_to_u256(self.is_zero(&a))); + }, + instructions::AND => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(a & b); + }, + instructions::OR => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(a | b); + }, + instructions::XOR => { + let a = stack.pop_back(); + let b = stack.pop_back(); + stack.push(a ^ b); + }, + instructions::BYTE => { + let word = stack.pop_back(); + let val = stack.pop_back(); + let byte = match word < U256::from(32) { + true => (val >> (8 * (31 - word.low_u64() as usize))) & U256::from(0xff), + false => U256::zero() + }; + stack.push(byte); + }, + instructions::ADDMOD => { + let a = stack.pop_back(); + let b = stack.pop_back(); + let c = stack.pop_back(); + + stack.push(if !self.is_zero(&c) { + // upcast to 512 + let a5 = U512::from(a); + let (res, _overflow) = a5.overflowing_add(U512::from(b)); + let (x, _overflow) = res.overflowing_rem(U512::from(c)); + U256::from(x) + } else { + U256::zero() + }); + }, + instructions::MULMOD => { + let a = stack.pop_back(); + let b = stack.pop_back(); + let c = stack.pop_back(); + + stack.push(if !self.is_zero(&c) { + let a5 = U512::from(a); + let (res, _overflow) = a5.overflowing_mul(U512::from(b)); + let (x, _overflow) = res.overflowing_rem(U512::from(c)); + U256::from(x) + } else { + U256::zero() + }); + }, + instructions::SIGNEXTEND => { + let bit = stack.pop_back(); + if bit < U256::from(32) { + let number = stack.pop_back(); + let bit_position = (bit.low_u64() * 8 + 7) as usize; + + let bit = number.bit(bit_position); + let mask = (U256::one() << bit_position) - U256::one(); + stack.push(if bit { + number | !mask + } else { + number & mask + }); + } + }, + _ => { + return Err(evm::Error::BadInstruction { + instruction: instruction + }); + } + } + Ok(()) + } + + fn find_jump_destinations(&self, code: &Bytes) -> HashSet { + let mut jump_dests = HashSet::new(); + let mut position = 0; + + while position < code.len() { + let instruction = code[position]; + + if instruction == instructions::JUMPDEST { + jump_dests.insert(position); + } else if instructions::is_push(instruction) { + position += instructions::get_push_bytes(instruction); + } + position += 1; + } + + return jump_dests; + } +} + +fn get_and_reset_sign(value: U256) -> (U256, bool) { + let sign = (value >> 255).low_u64() == 1; + (set_sign(value, sign), sign) +} + +fn set_sign(value: U256, sign: bool) -> U256 { + if sign { + let (val, _overflow) = (!U256::zero() ^ value).overflowing_add(U256::one()); + val + } else { + value + } +} + +#[inline] +fn add_u256_usize(value: &U256, num: usize) -> (U256, bool) { + value.clone().overflowing_add(U256::from(num)) +} + +#[inline] +fn u256_to_address(value: &U256) -> Address { + Address::from(H256::from(value)) +} + +#[inline] +fn address_to_u256(value: Address) -> U256 { + U256::from(H256::from(value).as_slice()) +} + +#[cfg(test)] +mod tests { + use common::*; + use super::*; + use evm; + + #[test] + fn test_find_jump_destinations() { + // given + let interpreter = Interpreter; + let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); + + // when + let valid_jump_destinations = interpreter.find_jump_destinations(&code); + + // then + assert!(valid_jump_destinations.contains(&66)); + } + + #[test] + fn test_calculate_mem_cost() { + // given + let interpreter = Interpreter; + let schedule = evm::Schedule::default(); + let current_mem_size = 0; + let mem_size = U256::from(5); + + // when + let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size); + + // then + assert_eq!(mem_cost, U256::from(3)); + assert_eq!(mem_size, 32); + } + + #[test] + fn test_memory_read_and_write() { + // given + let mem: &mut super::Memory = &mut vec![]; + mem.resize(0x80 + 32); + + // when + mem.write(U256::from(0x80), U256::from(0xabcdef)); + + // then + assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef)); + } + + #[test] + fn test_memory_read_and_write_byte() { + // given + let mem: &mut super::Memory = &mut vec![]; + mem.resize(32); + + // when + mem.write_byte(U256::from(0x1d), U256::from(0xab)); + mem.write_byte(U256::from(0x1e), U256::from(0xcd)); + mem.write_byte(U256::from(0x1f), U256::from(0xef)); + + // then + assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef)); + } +} diff --git a/src/evm/jit.rs b/src/evm/jit.rs index 122947d32..9f990155d 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -3,43 +3,6 @@ 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; @@ -126,64 +89,42 @@ impl IntoJit for Address { } } -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 + address: Address } impl<'a> ExtAdapter<'a> { - fn new(ext: &'a mut evm::Ext, err: &'a mut Option) -> Self { + fn new(ext: &'a mut evm::Ext, address: Address) -> Self { ExtAdapter { ext: ext, - err: err + address: address } } } impl<'a> evmjit::Ext for ExtAdapter<'a> { - fn sload(&self, index: *const evmjit::I256, out_value: *mut evmjit::I256) { + fn sload(&self, key: *const evmjit::I256, out_value: *mut evmjit::I256) { unsafe { - let i = H256::from_jit(&*index); + let i = H256::from_jit(&*key); let o = self.ext.storage_at(&i); *out_value = o.into_jit(); } } - fn sstore(&mut self, index: *const evmjit::I256, value: *const evmjit::I256) { - unsafe { - self.ext.set_storage_at(H256::from_jit(&*index), H256::from_jit(&*value)); + fn sstore(&mut self, key: *const evmjit::I256, value: *const evmjit::I256) { + let key = unsafe { H256::from_jit(&*key) }; + let value = unsafe { H256::from_jit(&*value) }; + let old_value = self.ext.storage_at(&key); + // if SSTORE nonzero -> zero, increment refund count + if !old_value.is_zero() && value.is_zero() { + self.ext.inc_sstore_clears(); } + self.ext.set_storage(key, value); } fn balance(&self, address: *const evmjit::H256, out_value: *mut evmjit::I256) { @@ -204,17 +145,29 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { fn create(&mut self, io_gas: *mut u64, - endowment: *const evmjit::I256, + value: *const evmjit::I256, init_beg: *const u8, init_size: u64, address: *mut evmjit::H256) { - unsafe { - let (gas_left, opt_addr) = self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)); - *io_gas = gas_left.low_u64(); - *address = match opt_addr { - Some(addr) => addr.into_jit(), - _ => Address::new().into_jit() - }; + + let gas = unsafe { U256::from(*io_gas) }; + let value = unsafe { U256::from_jit(&*value) }; + let code = unsafe { slice::from_raw_parts(init_beg, init_size as usize) }; + + // check if balance is sufficient and we are not too deep + if self.ext.balance(&self.address) >= value && self.ext.depth() < self.ext.schedule().max_depth { + match self.ext.create(&gas, &value, code) { + evm::ContractCreateResult::Created(new_address, gas_left) => unsafe { + *address = new_address.into_jit(); + *io_gas = gas_left.low_u64(); + }, + evm::ContractCreateResult::Failed => unsafe { + *address = Address::new().into_jit(); + *io_gas = 0; + } + } + } else { + unsafe { *address = Address::new().into_jit(); } } } @@ -228,31 +181,56 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { out_beg: *mut u8, out_size: u64, code_address: *const evmjit::H256) -> bool { - unsafe { - let res = self.ext.call(&U256::from(*io_gas), - &U256::from(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, ok)) => { - *io_gas = gas_left.low_u64(); - ok - } - Err(evm::Error::OutOfGas) => { - // hack to propagate out_of_gas to evmjit. - // must be negative - *io_gas = -1i64 as u64; - false - }, - Err(err) => { - // internal error. - *self.err = Some(err); - *io_gas = -1i64 as u64; - false - } + + let mut gas = unsafe { U256::from(*io_gas) }; + let mut call_gas = U256::from(call_gas); + let mut gas_cost = call_gas; + let receive_address = unsafe { Address::from_jit(&*receive_address) }; + let code_address = unsafe { Address::from_jit(&*code_address) }; + let value = unsafe { U256::from_jit(&*value) }; + + // receive address and code address are the same in normal calls + let is_callcode = receive_address != code_address; + if !is_callcode && !self.ext.exists(&code_address) { + gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas); + } + + if value > U256::zero() { + assert!(self.ext.schedule().call_value_transfer_gas > self.ext.schedule().call_stipend, "overflow possible"); + gas_cost = gas_cost + U256::from(self.ext.schedule().call_value_transfer_gas); + call_gas = call_gas + U256::from(self.ext.schedule().call_stipend); + } + + if gas_cost > gas { + unsafe { + *io_gas = -1i64 as u64; + return false; + } + } + + gas = gas - gas_cost; + + // check if balance is sufficient and we are not too deep + if self.ext.balance(&self.address) < value || self.ext.depth() >= self.ext.schedule().max_depth { + unsafe { + *io_gas = (gas + call_gas).low_u64(); + return false; + } + } + + match self.ext.call(&call_gas, + &receive_address, + &value, + unsafe { slice::from_raw_parts(in_beg, in_size as usize) }, + &code_address, + unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) { + evm::MessageCallResult::Success(gas_left) => unsafe { + *io_gas = (gas + gas_left).low_u64(); + true + }, + evm::MessageCallResult::Failed => unsafe { + *io_gas = gas.low_u64(); + false } } } @@ -302,35 +280,41 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { pub struct JitEvm; impl evm::Evm for JitEvm { - fn exec(&self, params: &ActionParams, ext: &mut evm::Ext) -> evm::Result { - let mut optional_err = None; + fn exec(&self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result { // 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 ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) }; 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().unwrap_or(vec![]); - 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().unwrap_or(vec![]); + assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); + assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); - data.author = ext.env_info().author.clone(); - data.difficulty = ext.env_info().difficulty; - data.gas_limit = ext.env_info().gas_limit; + let call_data = params.data.unwrap_or(vec![]); + let code = params.code.unwrap_or(vec![]); + + let mut data = evmjit::RuntimeDataHandle::new(); + data.gas = params.gas.low_u64() as i64; + data.gas_price = params.gas_price.low_u64() as i64; + data.call_data = call_data.as_ptr(); + data.call_data_size = call_data.len() as u64; + mem::forget(call_data); + data.code = code.as_ptr(); + data.code_size = code.len() as u64; + data.code_hash = code.sha3().into_jit(); + mem::forget(code); + data.address = params.address.into_jit(); + data.caller = params.sender.into_jit(); + data.origin = params.origin.into_jit(); + data.call_value = params.value.into_jit(); + + data.author = ext.env_info().author.clone().into_jit(); + data.difficulty = ext.env_info().difficulty.into_jit(); + data.gas_limit = ext.env_info().gas_limit.into_jit(); 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) }; + // don't really know why jit timestamp is int.. + data.timestamp = ext.env_info().timestamp as i64; + + let mut context = unsafe { evmjit::ContextHandle::new(data, &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(&U256::from(context.gas_left()), context.output_data()), diff --git a/src/evm/mod.rs b/src/evm/mod.rs index e84e133c5..2ed9a1146 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -2,8 +2,11 @@ pub mod ext; pub mod evm; +pub mod interpreter; +#[macro_use] pub mod factory; pub mod schedule; +mod instructions; #[cfg(feature = "jit" )] mod jit; @@ -11,6 +14,7 @@ mod jit; mod tests; pub use self::evm::{Evm, Error, Result}; -pub use self::ext::{Ext}; +pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::Factory; pub use self::schedule::Schedule; +pub use self::factory::VMType; diff --git a/src/evm/tests.rs b/src/evm/tests.rs index f43881424..8e1b5eff4 100644 --- a/src/evm/tests.rs +++ b/src/evm/tests.rs @@ -1,6 +1,6 @@ use common::*; use evm; -use evm::{Ext, Schedule, Factory}; +use evm::{Ext, Schedule, Factory, VMType, ContractCreateResult, MessageCallResult}; struct FakeLogEntry { topics: Vec, @@ -18,11 +18,20 @@ struct FakeExt { codes: HashMap, logs: Vec, _suicides: HashSet
, - info: EnvInfo + info: EnvInfo, + _schedule: Schedule } impl FakeExt { - fn new() -> Self { FakeExt::default() } + fn new() -> Self { + FakeExt::default() + } +} + +impl Default for Schedule { + fn default() -> Self { + Schedule::new_frontier() + } } impl Ext for FakeExt { @@ -30,10 +39,14 @@ impl Ext for FakeExt { self.store.get(key).unwrap_or(&H256::new()).clone() } - fn set_storage_at(&mut self, key: H256, value: H256) { + fn set_storage(&mut self, key: H256, value: H256) { self.store.insert(key, value); } + fn exists(&self, _address: &Address) -> bool { + unimplemented!(); + } + fn balance(&self, _address: &Address) -> U256 { unimplemented!(); } @@ -42,29 +55,28 @@ impl Ext for FakeExt { self.blockhashes.get(number).unwrap_or(&H256::new()).clone() } - fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> (U256, Option
) { + fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> ContractCreateResult { unimplemented!(); } fn call(&mut self, _gas: &U256, - _call_gas: &U256, - _receive_address: &Address, + _address: &Address, _value: &U256, _data: &[u8], _code_address: &Address, - _output: &mut [u8]) -> result::Result<(U256, bool), evm::Error> { + _output: &mut [u8]) -> MessageCallResult { unimplemented!(); } - fn extcode(&self, address: &Address) -> Vec { + fn extcode(&self, address: &Address) -> Bytes { self.codes.get(address).unwrap_or(&Bytes::new()).clone() } - fn log(&mut self, topics: Vec, data: Bytes) { + fn log(&mut self, topics: Vec, data: &[u8]) { self.logs.push(FakeLogEntry { topics: topics, - data: data + data: data.to_vec() }); } @@ -77,17 +89,52 @@ impl Ext for FakeExt { } fn schedule(&self) -> &Schedule { - unimplemented!(); + &self._schedule } fn env_info(&self) -> &EnvInfo { &self.info } + + fn depth(&self) -> usize { + unimplemented!(); + } + + fn inc_sstore_clears(&mut self) { + unimplemented!(); + } } #[test] -fn test_add() { +fn test_stack_underflow() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = "01600055".from_hex().unwrap(); + + let mut params = ActionParams::new(); + params.address = address.clone(); + params.gas = U256::from(100_000); + params.code = Some(code); + let mut ext = FakeExt::new(); + + let err = { + let vm : Box = Box::new(super::interpreter::Interpreter); + vm.exec(params, &mut ext).unwrap_err() + }; + + match err { + evm::Error::StackUnderflow {instruction: _, wanted, on_stack} => { + assert_eq!(wanted, 2); + assert_eq!(on_stack, 0); + } + _ => { + assert!(false, "Expected StackUndeflow") + } + }; +} + +evm_test!{test_add: test_add_jit, test_add_int} +fn test_add(factory: super::Factory) { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); let mut params = ActionParams::new(); @@ -97,16 +144,16 @@ fn test_add() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_988)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap()); } -#[test] -fn test_sha3() { +evm_test!{test_sha3: test_sha3_jit, test_sha3_int} +fn test_sha3(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "6000600020600055".from_hex().unwrap(); @@ -117,16 +164,16 @@ fn test_sha3() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_961)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap()); } -#[test] -fn test_address() { +evm_test!{test_address: test_address_jit, test_address_int} +fn test_address(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "30600055".from_hex().unwrap(); @@ -137,16 +184,16 @@ fn test_address() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()); } -#[test] -fn test_origin() { +evm_test!{test_origin: test_origin_jit, test_origin_int} +fn test_origin(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); let code = "32600055".from_hex().unwrap(); @@ -159,16 +206,16 @@ fn test_origin() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()); } -#[test] -fn test_sender() { +evm_test!{test_sender: test_sender_jit, test_sender_int} +fn test_sender(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); let code = "33600055".from_hex().unwrap(); @@ -181,16 +228,16 @@ fn test_sender() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()); } -#[test] -fn test_extcodecopy() { +evm_test!{test_extcodecopy: test_extcodecopy_jit, test_extcodecopy_int} +fn test_extcodecopy(factory: super::Factory) { // 33 - sender // 3b - extcodesize // 60 00 - push 0 @@ -216,16 +263,16 @@ fn test_extcodecopy() { ext.codes.insert(sender, sender_code); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_935)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("6005600055000000000000000000000000000000000000000000000000000000").unwrap()); } -#[test] -fn test_log_empty() { +evm_test!{test_log_empty: test_log_empty_jit, test_log_empty_int} +fn test_log_empty(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "60006000a0".from_hex().unwrap(); @@ -236,8 +283,8 @@ fn test_log_empty() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(99_619)); @@ -246,8 +293,8 @@ fn test_log_empty() { assert_eq!(ext.logs[0].data, vec![]); } -#[test] -fn test_log_sender() { +evm_test!{test_log_sender: test_log_sender_jit, test_log_sender_int} +fn test_log_sender(factory: super::Factory) { // 60 ff - push ff // 60 00 - push 00 // 53 - mstore @@ -268,8 +315,8 @@ fn test_log_sender() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(98_974)); @@ -279,8 +326,8 @@ fn test_log_sender() { assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); } -#[test] -fn test_blockhash() { +evm_test!{test_blockhash: test_blockhash_jit, test_blockhash_int} +fn test_blockhash(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "600040600055".from_hex().unwrap(); let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); @@ -293,16 +340,16 @@ fn test_blockhash() { ext.blockhashes.insert(U256::zero(), blockhash.clone()); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_974)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash); } -#[test] -fn test_calldataload() { +evm_test!{test_calldataload: test_calldataload_jit, test_calldataload_int} +fn test_calldataload(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "600135600055".from_hex().unwrap(); let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap(); @@ -315,8 +362,8 @@ fn test_calldataload() { let mut ext = FakeExt::new(); let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_991)); @@ -324,8 +371,8 @@ fn test_calldataload() { } -#[test] -fn test_author() { +evm_test!{test_author: test_author_jit, test_author_int} +fn test_author(factory: super::Factory) { let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let code = "41600055".from_hex().unwrap(); @@ -336,16 +383,16 @@ fn test_author() { ext.info.author = author; let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()); } -#[test] -fn test_timestamp() { +evm_test!{test_timestamp: test_timestamp_jit, test_timestamp_int} +fn test_timestamp(factory: super::Factory) { let timestamp = 0x1234; let code = "42600055".from_hex().unwrap(); @@ -356,16 +403,16 @@ fn test_timestamp() { ext.info.timestamp = timestamp; let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap()); } -#[test] -fn test_number() { +evm_test!{test_number: test_number_jit, test_number_int} +fn test_number(factory: super::Factory) { let number = 0x1234; let code = "43600055".from_hex().unwrap(); @@ -376,16 +423,16 @@ fn test_number() { ext.info.number = number; let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap()); } -#[test] -fn test_difficulty() { +evm_test!{test_difficulty: test_difficulty_jit, test_difficulty_int} +fn test_difficulty(factory: super::Factory) { let difficulty = U256::from(0x1234); let code = "44600055".from_hex().unwrap(); @@ -396,16 +443,16 @@ fn test_difficulty() { ext.info.difficulty = difficulty; let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap()); } -#[test] -fn test_gas_limit() { +evm_test!{test_gas_limit: test_gas_limit_jit, test_gas_limit_int} +fn test_gas_limit(factory: super::Factory) { let gas_limit = U256::from(0x1234); let code = "45600055".from_hex().unwrap(); @@ -416,10 +463,11 @@ fn test_gas_limit() { ext.info.gas_limit = gas_limit; let gas_left = { - let vm = Factory::create(); - vm.exec(¶ms, &mut ext).unwrap() + let vm = factory.create(); + vm.exec(params, &mut ext).unwrap() }; assert_eq!(gas_left, U256::from(79_995)); assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap()); } + diff --git a/src/executive.rs b/src/executive.rs index 9c3a545a5..2a26dd3f0 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -2,7 +2,7 @@ use common::*; use state::*; use engine::*; -use evm::{self, Factory, Ext}; +use evm::{self, Ext}; use externalities::*; use substate::*; @@ -75,8 +75,8 @@ impl<'a> Executive<'a> { } /// Creates `Externalities` from `Executive`. - pub fn to_externalities<'_>(&'_ mut self, params: &'_ ActionParams, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities { - Externalities::new(self.state, self.info, self.engine, self.depth, params, substate, output) + pub fn to_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities { + Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output) } /// This funtion should be used to execute transaction. @@ -137,7 +137,7 @@ impl<'a> Executive<'a> { code: Some(t.data.clone()), data: None, }; - self.create(¶ms, &mut substate) + self.create(params, &mut substate) }, &Action::Call(ref address) => { let params = ActionParams { @@ -153,7 +153,7 @@ impl<'a> Executive<'a> { }; // TODO: move output upstream let mut out = vec![]; - self.call(¶ms, &mut substate, BytesRef::Flexible(&mut out)) + self.call(params, &mut substate, BytesRef::Flexible(&mut out)) } }; @@ -165,7 +165,7 @@ impl<'a> Executive<'a> { /// 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`. - pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { + pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { // backup used in case of running out of gas let backup = self.state.clone(); @@ -198,12 +198,11 @@ impl<'a> Executive<'a> { let mut unconfirmed_substate = Substate::new(); let res = { - let mut ext = self.to_externalities(params, &mut unconfirmed_substate, OutputPolicy::Return(output)); - let evm = Factory::create(); - evm.exec(¶ms, &mut ext) + let mut ext = self.to_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::Return(output)); + self.engine.vm_factory().create().exec(params, &mut ext) }; - trace!("exec: sstore-clears={}\n", unconfirmed_substate.refunds_count); + trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count); trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); self.enact_result(&res, substate, unconfirmed_substate, backup); trace!("exec: new substate={:?}\n", substate); @@ -217,7 +216,7 @@ impl<'a> Executive<'a> { /// Creates contract with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate. - pub fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result { + pub fn create(&mut self, params: ActionParams, substate: &mut Substate) -> evm::Result { // backup used in case of running out of gas let backup = self.state.clone(); @@ -231,9 +230,8 @@ impl<'a> Executive<'a> { self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value); let res = { - let mut ext = self.to_externalities(params, &mut unconfirmed_substate, OutputPolicy::InitContract); - let evm = Factory::create(); - evm.exec(¶ms, &mut ext) + let mut ext = self.to_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::InitContract); + self.engine.vm_factory().create().exec(params, &mut ext) }; self.enact_result(&res, substate, unconfirmed_substate, backup); res @@ -244,7 +242,7 @@ impl<'a> Executive<'a> { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero - let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count; + let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_clears_count; // refunds from contract suicides let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); let refunds_bound = sstore_refunds + suicide_refunds; @@ -274,6 +272,21 @@ impl<'a> Executive<'a> { match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), + // TODO [ToDr] BadJumpDestination @debris - how to handle that? + Err(evm::Error::OutOfGas) + | Err(evm::Error::BadJumpDestination { destination: _ }) + | Err(evm::Error::BadInstruction { instruction: _ }) + | Err(evm::Error::StackUnderflow {instruction: _, wanted: _, on_stack: _}) + | Err(evm::Error::OutOfStack {instruction: _, wanted: _, limit: _}) => { + Ok(Executed { + gas: t.gas, + gas_used: t.gas, + refunded: U256::zero(), + cumulative_gas_used: self.info.gas_used + t.gas, + logs: vec![], + contracts_created: vec![] + }) + }, _ => { Ok(Executed { gas: t.gas, @@ -290,7 +303,11 @@ impl<'a> Executive<'a> { fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) { // TODO: handle other evm::Errors same as OutOfGas once they are implemented match result { - &Err(evm::Error::OutOfGas) => { + &Err(evm::Error::OutOfGas) + | &Err(evm::Error::BadJumpDestination { destination: _ }) + | &Err(evm::Error::BadInstruction { instruction: _ }) + | &Err(evm::Error::StackUnderflow {instruction: _, wanted: _, on_stack: _}) + | &Err(evm::Error::OutOfStack {instruction: _, wanted: _, limit: _}) => { self.state.revert(backup); }, &Ok(_) | &Err(evm::Error::Internal) => substate.accrue(un_substate) @@ -306,17 +323,19 @@ mod tests { use ethereum; use engine::*; use spec::*; - use evm::Schedule; + use evm::{Schedule, Factory, VMType}; use substate::*; struct TestEngine { + factory: Factory, spec: Spec, max_depth: usize } impl TestEngine { - fn new(max_depth: usize) -> TestEngine { + fn new(max_depth: usize, factory: Factory) -> TestEngine { TestEngine { + factory: factory, spec: ethereum::new_frontier_test(), max_depth: max_depth } @@ -326,6 +345,9 @@ mod tests { impl Engine for TestEngine { fn name(&self) -> &str { "TestEngine" } fn spec(&self) -> &Spec { &self.spec } + fn vm_factory(&self) -> &Factory { + &self.factory + } fn schedule(&self, _env_info: &EnvInfo) -> Schedule { let mut schedule = Schedule::new_frontier(); schedule.max_depth = self.max_depth; @@ -340,9 +362,9 @@ mod tests { assert_eq!(expected_address, contract_address(&address, &U256::from(88))); } - #[test] // TODO: replace params with transactions! - fn test_sender_balance() { + evm_test!{test_sender_balance: test_sender_balance_jit, test_sender_balance_int} + fn test_sender_balance(factory: Factory) { let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = contract_address(&sender, &U256::zero()); let mut params = ActionParams::new(); @@ -354,12 +376,12 @@ mod tests { let mut state = State::new_temp(); state.add_balance(&sender, &U256::from(0x100u64)); let info = EnvInfo::new(); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let mut substate = Substate::new(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(¶ms, &mut substate).unwrap() + ex.create(params, &mut substate).unwrap() }; assert_eq!(gas_left, U256::from(79_975)); @@ -372,8 +394,8 @@ mod tests { // TODO: just test state root. } - #[test] - fn test_create_contract() { + evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} + fn test_create_contract(factory: Factory) { // code: // // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? @@ -412,12 +434,12 @@ mod tests { let mut state = State::new_temp(); state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::new(); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let mut substate = Substate::new(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(¶ms, &mut substate).unwrap() + ex.create(params, &mut substate).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -425,8 +447,8 @@ mod tests { assert_eq!(substate.contracts_created.len(), 0); } - #[test] - fn test_create_contract_value_too_high() { + evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} + fn test_create_contract_value_too_high(factory: Factory) { // code: // // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? @@ -465,20 +487,20 @@ mod tests { let mut state = State::new_temp(); state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::new(); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let mut substate = Substate::new(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(¶ms, &mut substate).unwrap() + ex.create(params, &mut substate).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); assert_eq!(substate.contracts_created.len(), 0); } - #[test] - fn test_create_contract_without_max_depth() { + evm_test!{test_create_contract_without_max_depth: test_create_contract_without_max_depth_jit, test_create_contract_without_max_depth_int} + fn test_create_contract_without_max_depth(factory: Factory) { // code: // // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? @@ -501,7 +523,7 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(&sender, &U256::zero()); @@ -516,20 +538,21 @@ mod tests { let mut state = State::new_temp(); state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::new(); - let engine = TestEngine::new(1024); + let engine = TestEngine::new(1024, factory); let mut substate = Substate::new(); { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(¶ms, &mut substate).unwrap(); + ex.create(params, &mut substate).unwrap(); } assert_eq!(substate.contracts_created.len(), 1); assert_eq!(substate.contracts_created[0], next_address); } - #[test] - fn test_aba_calls() { + // test is incorrect, mk + evm_test_ignore!{test_aba_calls: test_aba_calls_jit, test_aba_calls_int} + fn test_aba_calls(factory: Factory) { // 60 00 - push 0 // 60 00 - push 0 // 60 00 - push 0 @@ -574,20 +597,21 @@ mod tests { state.add_balance(&sender, &U256::from(100_000)); let info = EnvInfo::new(); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let mut substate = Substate::new(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut [])).unwrap() }; assert_eq!(gas_left, U256::from(73_237)); assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))), H256::from(&U256::from(1))); } - #[test] - fn test_recursive_bomb1() { + // test is incorrect, mk + evm_test_ignore!{test_recursive_bomb1: test_recursive_bomb1_jit, test_recursive_bomb1_int} + fn test_recursive_bomb1(factory: Factory) { // 60 01 - push 1 // 60 00 - push 0 // 54 - sload @@ -616,12 +640,12 @@ mod tests { let mut state = State::new_temp(); state.init_code(&address, code.clone()); let info = EnvInfo::new(); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let mut substate = Substate::new(); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap() + ex.call(params, &mut substate, BytesRef::Fixed(&mut [])).unwrap() }; assert_eq!(gas_left, U256::from(59_870)); @@ -629,8 +653,9 @@ mod tests { assert_eq!(state.storage_at(&address, &H256::from(&U256::one())), H256::from(&U256::from(1))); } - #[test] - fn test_transact_simple() { + // test is incorrect, mk + evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int} + fn test_transact_simple(factory: Factory) { let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero()); let keypair = KeyPair::create().unwrap(); t.sign(&keypair.secret()); @@ -641,7 +666,7 @@ mod tests { state.add_balance(&sender, &U256::from(18)); let mut info = EnvInfo::new(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let executed = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -660,14 +685,14 @@ mod tests { assert_eq!(state.storage_at(&contract, &H256::new()), H256::from(&U256::from(1))); } - #[test] - fn test_transact_invalid_sender() { + evm_test!{test_transact_invalid_sender: test_transact_invalid_sender_jit, test_transact_invalid_sender_int} + fn test_transact_invalid_sender(factory: Factory) { let t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero()); let mut state = State::new_temp(); let mut info = EnvInfo::new(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let res = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -680,8 +705,8 @@ mod tests { } } - #[test] - fn test_transact_invalid_nonce() { + evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int} + fn test_transact_invalid_nonce(factory: Factory) { let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::one()); let keypair = KeyPair::create().unwrap(); t.sign(&keypair.secret()); @@ -691,7 +716,7 @@ mod tests { state.add_balance(&sender, &U256::from(17)); let mut info = EnvInfo::new(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let res = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -705,8 +730,8 @@ mod tests { } } - #[test] - fn test_transact_gas_limit_reached() { + evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int} + fn test_transact_gas_limit_reached(factory: Factory) { let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(80_001), U256::zero(), U256::zero()); let keypair = KeyPair::create().unwrap(); t.sign(&keypair.secret()); @@ -717,7 +742,7 @@ mod tests { let mut info = EnvInfo::new(); info.gas_used = U256::from(20_000); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let res = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -731,8 +756,8 @@ mod tests { } } - #[test] - fn test_not_enough_cash() { + evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int} + fn test_not_enough_cash(factory: Factory) { let mut t = Transaction::new_create(U256::from(18), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::one(), U256::zero()); let keypair = KeyPair::create().unwrap(); t.sign(&keypair.secret()); @@ -742,7 +767,7 @@ mod tests { state.add_balance(&sender, &U256::from(100_017)); let mut info = EnvInfo::new(); info.gas_limit = U256::from(100_000); - let engine = TestEngine::new(0); + let engine = TestEngine::new(0, factory); let res = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -755,4 +780,40 @@ mod tests { _ => assert!(false, "Expected not enough cash error. {:?}", res) } } + + evm_test!{test_sha3: test_sha3_jit, test_sha3_int} + fn test_sha3(factory: Factory) { + let code = "6064640fffffffff20600055".from_hex().unwrap(); + + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address = contract_address(&sender, &U256::zero()); + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(0x0186a0); + params.code = Some(code.clone()); + params.value = U256::from_str("0de0b6b3a7640000").unwrap(); + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); + let info = EnvInfo::new(); + let engine = TestEngine::new(0, factory); + let mut substate = Substate::new(); + + let result = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(params, &mut substate) + }; + + match result { + Err(_) => { + }, + _ => { + panic!("Expected OutOfGas"); + } + } + } + } diff --git a/src/externalities.rs b/src/externalities.rs index 8c75a71f4..8b16cc72b 100644 --- a/src/externalities.rs +++ b/src/externalities.rs @@ -3,7 +3,7 @@ use common::*; use state::*; use engine::*; use executive::*; -use evm::{self, Schedule, Ext}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult}; use substate::*; /// Policy for handling output data on `RETURN` opcode. @@ -15,23 +15,31 @@ pub enum OutputPolicy<'a> { InitContract } +/// Transaction properties that externalities need to know about. +pub struct OriginInfo { + address: Address, + origin: Address, + gas_price: U256 +} + +impl OriginInfo { + /// Populates origin info from action params. + pub fn from(params: &ActionParams) -> Self { + OriginInfo { + address: params.address.clone(), + origin: params.origin.clone(), + gas_price: params.gas_price.clone() + } + } +} + /// Implementation of evm Externalities. pub struct Externalities<'a> { - - #[cfg(test)] - pub state: &'a mut State, - #[cfg(not(test))] state: &'a mut State, - - info: &'a EnvInfo, + env_info: &'a EnvInfo, engine: &'a Engine, depth: usize, - - #[cfg(test)] - pub params: &'a ActionParams, - #[cfg(not(test))] - params: &'a ActionParams, - + origin_info: OriginInfo, substate: &'a mut Substate, schedule: Schedule, output: OutputPolicy<'a> @@ -40,20 +48,20 @@ pub struct Externalities<'a> { impl<'a> Externalities<'a> { /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, - info: &'a EnvInfo, + env_info: &'a EnvInfo, engine: &'a Engine, depth: usize, - params: &'a ActionParams, + origin_info: OriginInfo, substate: &'a mut Substate, output: OutputPolicy<'a>) -> Self { Externalities { state: state, - info: info, + env_info: env_info, engine: engine, depth: depth, - params: params, + origin_info: origin_info, substate: substate, - schedule: engine.schedule(info), + schedule: engine.schedule(env_info), output: output } } @@ -61,19 +69,15 @@ impl<'a> Externalities<'a> { impl<'a> Ext for Externalities<'a> { fn storage_at(&self, key: &H256) -> H256 { - trace!("ext: storage_at({}, {}) == {}\n", self.params.address, key, U256::from(self.state.storage_at(&self.params.address, key).as_slice())); - self.state.storage_at(&self.params.address, key) + self.state.storage_at(&self.origin_info.address, key) } - fn set_storage_at(&mut self, key: H256, value: H256) { - let old = self.state.storage_at(&self.params.address, &key); - // if SSTORE nonzero -> zero, increment refund count - if value.is_zero() && !old.is_zero() { - trace!("ext: additional refund. {} -> {}\n", self.substate.refunds_count, self.substate.refunds_count + x!(1)); - self.substate.refunds_count = self.substate.refunds_count + U256::one(); - } - trace!("ext: set_storage_at({}, {}): {} -> {}\n", self.params.address, key, U256::from(old.as_slice()), U256::from(value.as_slice())); - self.state.set_storage(&self.params.address, key, value) + fn set_storage(&mut self, key: H256, value: H256) { + self.state.set_storage(&self.origin_info.address, key, value) + } + + fn exists(&self, address: &Address) -> bool { + self.state.exists(address) } fn balance(&self, address: &Address) -> U256 { @@ -81,113 +85,79 @@ impl<'a> Ext for Externalities<'a> { } fn blockhash(&self, number: &U256) -> H256 { - match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 { + match *number < U256::from(self.env_info.number) && number.low_u64() >= cmp::max(256, self.env_info.number) - 256 { true => { - let index = self.info.number - number.low_u64() - 1; - let r = self.info.last_hashes[index as usize].clone(); - trace!("ext: blockhash({}) -> {} self.info.number={}\n", number, r, self.info.number); + let index = self.env_info.number - number.low_u64() - 1; + let r = self.env_info.last_hashes[index as usize].clone(); + trace!("ext: blockhash({}) -> {} self.env_info.number={}\n", number, r, self.env_info.number); r }, false => { - trace!("ext: blockhash({}) -> null self.info.number={}\n", number, self.info.number); + trace!("ext: blockhash({}) -> null self.env_info.number={}\n", number, self.env_info.number); H256::from(&U256::zero()) }, } } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
) { - // if balance is insufficient or we are to deep, return - if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { - return (*gas, None); - } - + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { // create new contract address - let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address)); + let address = contract_address(&self.origin_info.address, &self.state.nonce(&self.origin_info.address)); // prepare the params let params = ActionParams { code_address: address.clone(), address: address.clone(), - sender: self.params.address.clone(), - origin: self.params.origin.clone(), + sender: self.origin_info.address.clone(), + origin: self.origin_info.origin.clone(), gas: *gas, - gas_price: self.params.gas_price.clone(), + gas_price: self.origin_info.gas_price.clone(), value: value.clone(), code: Some(code.to_vec()), data: None, }; - self.state.inc_nonce(&self.params.address); - let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - match ex.create(¶ms, self.substate) { - Ok(gas_left) => (gas_left, Some(address)), - _ => (U256::zero(), None) + self.state.inc_nonce(&self.origin_info.address); + let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth); + + // TODO: handle internal error separately + match ex.create(params, self.substate) { + Ok(gas_left) => { + self.substate.contracts_created.push(address.clone()); + ContractCreateResult::Created(address, gas_left) + }, + _ => ContractCreateResult::Failed } } fn call(&mut self, gas: &U256, - call_gas: &U256, - receive_address: &Address, + address: &Address, value: &U256, data: &[u8], code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), evm::Error> { - - let mut gas_cost = *call_gas; - let mut call_gas = *call_gas; - - let is_call = receive_address == code_address; - if is_call && !self.state.exists(&code_address) { - gas_cost = gas_cost + U256::from(self.schedule.call_new_account_gas); - } - - if *value > U256::zero() { - assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible"); - gas_cost = gas_cost + U256::from(self.schedule.call_value_transfer_gas); - call_gas = call_gas + U256::from(self.schedule.call_stipend); - } - - debug!("Externalities::call(gas={}, call_gas={}, recv={}, value={}, data={}, code={})\n", gas, call_gas, receive_address, value, data.pretty(), code_address); - - if gas_cost > *gas { - debug!("Externalities::call: OutOfGas gas_cost={}, gas={}", gas_cost, gas); - return Err(evm::Error::OutOfGas); - } - - let gas = *gas - gas_cost; - - // if balance is insufficient or we are too deep, return - if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { - debug!("Externalities::call: OutOfCash bal({})={}, value={}", self.params.address, self.state.balance(&self.params.address), value); - return Ok((gas + call_gas, false)); - } + output: &mut [u8]) -> MessageCallResult { let params = ActionParams { code_address: code_address.clone(), - address: receive_address.clone(), - sender: self.params.address.clone(), - origin: self.params.origin.clone(), - gas: call_gas, - gas_price: self.params.gas_price.clone(), + address: address.clone(), + sender: self.origin_info.address.clone(), + origin: self.origin_info.origin.clone(), + gas: *gas, + gas_price: self.origin_info.gas_price.clone(), value: value.clone(), code: self.state.code(code_address), data: Some(data.to_vec()), }; + let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth); - trace!("Externalities::call: BEFORE: bal({})={}, bal({})={}\n", params.sender, self.state.balance(¶ms.sender), params.address, self.state.balance(¶ms.address)); - trace!("Externalities::call: CALLING: params={:?}\n", params); - let r = Executive::from_parent(self.state, self.info, self.engine, self.depth).call(¶ms, self.substate, BytesRef::Fixed(output)); - trace!("Externalities::call: AFTER: bal({})={}, bal({})={}\n", params.sender, self.state.balance(¶ms.sender), params.address, self.state.balance(¶ms.address)); - - match r { - Ok(gas_left) => Ok((gas + gas_left, true)), - _ => Ok((gas, false)) + match ex.call(params, self.substate, BytesRef::Fixed(output)) { + Ok(gas_left) => MessageCallResult::Success(gas_left), + _ => MessageCallResult::Failed } } - fn extcode(&self, address: &Address) -> Vec { + fn extcode(&self, address: &Address) -> Bytes { self.state.code(address).unwrap_or(vec![]) } @@ -219,21 +189,20 @@ impl<'a> Ext for Externalities<'a> { ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); code.set_len(data.len()); } - let address = &self.params.address; + let address = &self.origin_info.address; self.state.init_code(address, code); - self.substate.contracts_created.push(address.clone()); 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 log(&mut self, topics: Vec, data: &[u8]) { + let address = self.origin_info.address.clone(); + self.substate.logs.push(LogEntry::new(address, topics, data.to_vec())); } fn suicide(&mut self, refund_address: &Address) { - let address = self.params.address.clone(); + let address = self.origin_info.address.clone(); let balance = self.balance(&address); self.state.transfer_balance(&address, refund_address, &balance); self.substate.suicides.insert(address); @@ -244,6 +213,14 @@ impl<'a> Ext for Externalities<'a> { } fn env_info(&self) -> &EnvInfo { - &self.info + &self.env_info + } + + fn depth(&self) -> usize { + self.depth + } + + fn inc_sstore_clears(&mut self) { + self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); } } diff --git a/src/lib.rs b/src/lib.rs index c3abe6ddf..7e4fdf33e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(cell_extras)] #![feature(augmented_assignments)] +#![feature(wrapping)] //#![feature(plugin)] //#![plugin(interpolate_idents)] //! Ethcore's ethereum implementation @@ -89,6 +90,8 @@ extern crate ethcore_util as util; pub mod common; pub mod basic_types; +#[macro_use] +pub mod evm; pub mod error; pub mod log_entry; pub mod env_info; @@ -110,7 +113,6 @@ pub mod views; pub mod blockchain; pub mod extras; pub mod substate; -pub mod evm; pub mod service; pub mod executive; pub mod externalities; diff --git a/src/null_engine.rs b/src/null_engine.rs index bd745365c..e673563b3 100644 --- a/src/null_engine.rs +++ b/src/null_engine.rs @@ -1,20 +1,29 @@ use engine::Engine; use spec::Spec; use evm::Schedule; +use evm::Factory; use env_info::EnvInfo; /// An engine which does not provide any consensus mechanism. pub struct NullEngine { spec: Spec, + factory: Factory } impl NullEngine { pub fn new_boxed(spec: Spec) -> Box { - Box::new(NullEngine{spec: spec}) + Box::new(NullEngine{ + spec: spec, + // TODO [todr] should this return any specific factory? + factory: Factory::default() + }) } } impl Engine for NullEngine { + fn vm_factory(&self) -> &Factory { + &self.factory + } fn name(&self) -> &str { "NullEngine" } fn spec(&self) -> &Spec { &self.spec } fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() } diff --git a/src/service.rs b/src/service.rs index 363cd455e..3bc137c9c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -48,7 +48,8 @@ struct ClientIoHandler { } impl IoHandler for ClientIoHandler { - fn initialize<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>) { } + fn initialize<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>) { + } fn message<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>, net_message: &'s mut NetSyncMessage) { match net_message { diff --git a/src/state.rs b/src/state.rs index 88280c2da..fc4d6f817 100644 --- a/src/state.rs +++ b/src/state.rs @@ -101,7 +101,7 @@ impl State { } /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn code(&self, a: &Address) -> Option> { + pub fn code(&self, a: &Address) -> Option { self.get(a, true).as_ref().map(|a|a.code().map(|x|x.to_vec())).unwrap_or(None) } diff --git a/src/substate.rs b/src/substate.rs index b227afacc..9a1d6741e 100644 --- a/src/substate.rs +++ b/src/substate.rs @@ -8,8 +8,8 @@ pub struct Substate { pub suicides: HashSet
, /// Any logs. pub logs: Vec, - /// Refund counter of SSTORE nonzero->zero. - pub refunds_count: U256, + /// Refund counter of SSTORE nonzero -> zero. + pub sstore_clears_count: U256, /// Created contracts. pub contracts_created: Vec
} @@ -20,7 +20,7 @@ impl Substate { Substate { suicides: HashSet::new(), logs: vec![], - refunds_count: U256::zero(), + sstore_clears_count: U256::zero(), contracts_created: vec![] } } @@ -28,7 +28,7 @@ impl Substate { pub fn accrue(&mut self, s: Substate) { self.suicides.extend(s.suicides.into_iter()); self.logs.extend(s.logs.into_iter()); - self.refunds_count = self.refunds_count + s.refunds_count; + self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.contracts_created.extend(s.contracts_created.into_iter()); } } diff --git a/src/tests/executive.rs b/src/tests/executive.rs index 8b53e65bb..fe428e199 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -4,19 +4,21 @@ use executive::*; use spec::*; use engine::*; use evm; -use evm::{Schedule, Ext, Factory}; +use evm::{Schedule, Ext, Factory, VMType, ContractCreateResult, MessageCallResult}; use ethereum; use externalities::*; use substate::*; struct TestEngine { + vm_factory: Factory, spec: Spec, max_depth: usize } impl TestEngine { - fn new(max_depth: usize) -> TestEngine { + fn new(max_depth: usize, vm_type: VMType) -> TestEngine { TestEngine { + vm_factory: Factory::new(vm_type), spec: ethereum::new_frontier_test(), max_depth: max_depth } @@ -26,6 +28,7 @@ impl TestEngine { impl Engine for TestEngine { fn name(&self) -> &str { "TestEngine" } fn spec(&self) -> &Spec { &self.spec } + fn vm_factory(&self) -> &Factory { &self.vm_factory } fn schedule(&self, _env_info: &EnvInfo) -> Schedule { let mut schedule = Schedule::new_frontier(); schedule.max_depth = self.max_depth; @@ -36,7 +39,7 @@ impl Engine for TestEngine { struct CallCreate { data: Bytes, destination: Option
, - _gas_limit: U256, + gas_limit: U256, value: U256 } @@ -44,13 +47,22 @@ struct CallCreate { /// Stores callcreates. struct TestExt<'a> { ext: Externalities<'a>, - callcreates: Vec + callcreates: Vec, + contract_address: Address } impl<'a> TestExt<'a> { - fn new(ext: Externalities<'a>) -> TestExt { + fn new(state: &'a mut State, + info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a>, + address: Address) -> Self { TestExt { - ext: ext, + contract_address: contract_address(&address, &state.nonce(&address)), + ext: Externalities::new(state, info, engine, depth, origin_info, substate, output), callcreates: vec![] } } @@ -61,8 +73,12 @@ impl<'a> Ext for TestExt<'a> { self.ext.storage_at(key) } - fn set_storage_at(&mut self, key: H256, value: H256) { - self.ext.set_storage_at(key, value) + fn set_storage(&mut self, key: H256, value: H256) { + self.ext.set_storage(key, value) + } + + fn exists(&self, address: &Address) -> bool { + self.ext.exists(address) } fn balance(&self, address: &Address) -> U256 { @@ -73,66 +89,37 @@ impl<'a> Ext for TestExt<'a> { self.ext.blockhash(number) } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option
) { - // in call and create we need to check if we exited with insufficient balance or max limit reached. - // in case of reaching max depth, we should store callcreates. Otherwise, ignore. - let res = self.ext.create(gas, value, code); - let ext = &self.ext; - match res { - // just record call create - (gas_left, Some(address)) => { - self.callcreates.push(CallCreate { - data: code.to_vec(), - destination: Some(address.clone()), - _gas_limit: *gas, - value: *value - }); - (gas_left, Some(address)) - }, - // creation failed only due to reaching max_depth - (gas_left, None) if ext.state.balance(&ext.params.address) >= *value => { - self.callcreates.push(CallCreate { - data: code.to_vec(), - // callcreate test does not need an address - destination: None, - _gas_limit: *gas, - value: *value - }); - let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); - (gas_left, Some(address)) - }, - other => other - } + fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + self.callcreates.push(CallCreate { + data: code.to_vec(), + destination: None, + gas_limit: *gas, + value: *value + }); + ContractCreateResult::Created(self.contract_address.clone(), *gas) } fn call(&mut self, gas: &U256, - call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], - code_address: &Address, - output: &mut [u8]) -> Result<(U256, bool), evm::Error> { - let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output); - let ext = &self.ext; - if let &Ok(_some) = &res { - if ext.state.balance(&ext.params.address) >= *value { - self.callcreates.push(CallCreate { - data: data.to_vec(), - destination: Some(receive_address.clone()), - _gas_limit: *call_gas, - value: *value - }); - } - } - res + _code_address: &Address, + _output: &mut [u8]) -> MessageCallResult { + self.callcreates.push(CallCreate { + data: data.to_vec(), + destination: Some(receive_address.clone()), + gas_limit: *gas, + value: *value + }); + MessageCallResult::Success(*gas) } - fn extcode(&self, address: &Address) -> Vec { + fn extcode(&self, address: &Address) -> Bytes { self.ext.extcode(address) } - fn log(&mut self, topics: Vec, data: Bytes) { + fn log(&mut self, topics: Vec, data: &[u8]) { self.ext.log(topics, data) } @@ -151,20 +138,39 @@ impl<'a> Ext for TestExt<'a> { fn env_info(&self) -> &EnvInfo { self.ext.env_info() } + + fn depth(&self) -> usize { + 0 + } + + fn inc_sstore_clears(&mut self) { + self.ext.inc_sstore_clears() + } } fn do_json_test(json_data: &[u8]) -> Vec { + let vms = VMType::all(); + vms + .iter() + .flat_map(|vm| do_json_test_for(vm, json_data)) + .collect() +} + +fn do_json_test_for(vm: &VMType, 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(); for (name, test) in json.as_object().unwrap() { println!("name: {:?}", name); // sync io is usefull when something crashes in jit - //::std::io::stdout().write(&name.as_bytes()); - //::std::io::stdout().write(b"\n"); - //::std::io::stdout().flush(); + // ::std::io::stdout().write(&name.as_bytes()); + // ::std::io::stdout().write(b"\n"); + // ::std::io::stdout().flush(); let mut fail = false; //let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true }; - let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { failed.push(name.to_string() + ": "+ s); fail = true }; + let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { + failed.push(format!("[{}] {}: {}", vm, name.to_string(), s)); + fail = true + }; // test env let mut state = State::new_temp(); @@ -191,7 +197,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { info.timestamp = xjson!(&env["currentTimestamp"]); }); - let engine = TestEngine::new(0); + let engine = TestEngine::new(1, vm.clone()); // params let mut params = ActionParams::new(); @@ -214,18 +220,24 @@ fn do_json_test(json_data: &[u8]) -> Vec { // execute let (res, callcreates) = { - let ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output))); - let mut test_ext = TestExt::new(ex); - let evm = Factory::create(); - let res = evm.exec(¶ms, &mut test_ext); - (res, test_ext.callcreates) + let mut ex = TestExt::new(&mut state, + &info, + &engine, + 0, + OriginInfo::from(¶ms), + &mut substate, + OutputPolicy::Return(BytesRef::Flexible(&mut output)), + params.address.clone()); + let evm = engine.vm_factory().create(); + let res = evm.exec(params, &mut ex); + (res, ex.callcreates) }; // then validate match res { Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), Ok(gas_left) => { - //println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, U256::from(&test["gas"])); + // println!("name: {}, gas_left : {:?}", name, gas_left); fail_unless(!out_of_gas, "expected to run out of gas."); fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect"); fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect"); @@ -247,11 +259,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { fail_unless(callcreate.data == Bytes::from_json(&expected["data"]), "callcreates data is incorrect"); fail_unless(callcreate.destination == xjson!(&expected["destination"]), "callcreates destination is incorrect"); fail_unless(callcreate.value == xjson!(&expected["value"]), "callcreates value is incorrect"); - - // TODO: call_gas is calculated in externalities and is not exposed to TestExt. - // maybe move it to it's own function to simplify calculation? - //println!("name: {:?}, callcreate {:?}, expected: {:?}", name, callcreate.gas_limit, U256::from(&expected["gasLimit"])); - //fail_unless(callcreate.gas_limit == U256::from(&expected["gasLimit"]), "callcreates gas_limit is incorrect"); + fail_unless(callcreate.gas_limit == xjson!(&expected["gasLimit"]), "callcreates gas_limit is incorrect"); } } } diff --git a/src/tests/state.rs b/src/tests/state.rs index d4a921add..119e7037a 100644 --- a/src/tests/state.rs +++ b/src/tests/state.rs @@ -8,8 +8,7 @@ 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 engine = ethereum::new_frontier_test().to_engine().unwrap(); - + let engine = ethereum::new_frontier_like_test().to_engine().unwrap(); flush(format!("\n")); for (name, test) in json.as_object().unwrap() { diff --git a/src/transaction.rs b/src/transaction.rs index 56acf5ab7..4f547a243 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -215,6 +215,15 @@ impl Transaction { Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule) } + /// Checks whether the signature has a low 's' value. + pub fn check_low_s(&self) -> Result<(), Error> { + if !ec::is_low_s(&self.s) { + Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) + } else { + Ok(()) + } + } + /// Do basic validation, checking for valid signature and minimum gas, pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result { if require_low && !ec::is_low_s(&self.s) { diff --git a/src/verification.rs b/src/verification.rs index bb7b912aa..aecee2734 100644 --- a/src/verification.rs +++ b/src/verification.rs @@ -18,6 +18,12 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res try!(verify_header(&u, engine)); try!(engine.verify_block_basic(&u, None)); } + // Verify transactions. + // TODO: either use transaction views or cache the decoded transactions. + let v = BlockView::new(bytes); + for t in v.transactions() { + try!(engine.verify_transaction_basic(&t, &header)); + } Ok(()) } @@ -29,6 +35,12 @@ pub fn verify_block_unordered(header: &Header, bytes: &[u8], engine: &Engine) -> for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { try!(engine.verify_block_unordered(&u, None)); } + // Verify transactions. + // TODO: pass in pre-recovered transactions - maybe verify_transaction wants to call `sender()`. + let v = BlockView::new(bytes); + for t in v.transactions() { + try!(engine.verify_transaction(&t, &header)); + } Ok(()) } diff --git a/src/views.rs b/src/views.rs index 7a1a20e9f..6c616774d 100644 --- a/src/views.rs +++ b/src/views.rs @@ -3,6 +3,64 @@ use util::*; use header::*; use transaction::*; +/// View onto transaction rlp. +pub struct TransactionView<'a> { + rlp: Rlp<'a> +} + +impl<'a> TransactionView<'a> { + /// Creates new view onto block from raw bytes. + pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { + TransactionView { + rlp: Rlp::new(bytes) + } + } + + /// Creates new view onto block from rlp. + pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> { + TransactionView { + rlp: rlp + } + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &Rlp<'a> { + &self.rlp + } + + /// Get the nonce field of the transaction. + pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } + + /// Get the gas_price field of the transaction. + pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } + + /// Get the gas field of the transaction. + pub fn gas(&self) -> U256 { self.rlp.val_at(2) } + + /// Get the value field of the transaction. + pub fn value(&self) -> U256 { self.rlp.val_at(4) } + + /// Get the data field of the transaction. + pub fn data(&self) -> Bytes { self.rlp.val_at(5) } + + /// Get the v field of the transaction. + pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } + + /// Get the r field of the transaction. + pub fn r(&self) -> U256 { self.rlp.val_at(7) } + + /// Get the s field of the transaction. + pub fn s(&self) -> U256 { self.rlp.val_at(8) } + + // TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) } +} + +impl<'a> Hashable for TransactionView<'a> { + fn sha3(&self) -> H256 { + self.rlp.as_raw().sha3() + } +} + /// View onto block rlp. pub struct BlockView<'a> { rlp: Rlp<'a> @@ -38,6 +96,11 @@ impl<'a> BlockView<'a> { HeaderView::new_from_rlp(self.rlp.at(0)) } + /// Return List of transactions in given block. + pub fn transaction_views(&self) -> Vec { + self.rlp.at(1).iter().map(|rlp| TransactionView::new_from_rlp(rlp)).collect() + } + /// Return List of transactions in given block. pub fn transactions(&self) -> Vec { self.rlp.val_at(1)