diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 40ab23b8e..271e95785 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -14,8 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::mem; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::BTreeMap; use util::{U256, Address, H256, H2048, Bytes, Itertools}; use blockchain::TreeRoute; use block_queue::BlockQueueInfo; diff --git a/ethcore/src/common.rs b/ethcore/src/common.rs index 2c66710c0..41fdd5397 100644 --- a/ethcore/src/common.rs +++ b/ethcore/src/common.rs @@ -21,7 +21,6 @@ pub use env_info::*; pub use views::*; pub use builtin::*; pub use header::*; -pub use account::*; pub use transaction::*; pub use log_entry::*; pub use receipt::*; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 748582eba..4cceb137b 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -16,12 +16,11 @@ //! Transaction Execution environment. use common::*; -use state::*; +use state::{State, Substate}; use engines::Engine; use types::executed::CallType; use evm::{self, Ext, Factory, Finalize}; use externalities::*; -use substate::*; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; pub use types::executed::{Executed, ExecutionResult}; @@ -487,7 +486,7 @@ mod tests { use super::*; use common::*; use evm::{Factory, VMType}; - use substate::*; + use state::Substate; use tests::helpers::*; use trace::trace; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index bfaa15d38..09c4b4e11 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -16,11 +16,10 @@ //! Transaction Execution environment. use common::*; -use state::*; +use state::{State, Substate}; use engines::Engine; use executive::*; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; -use substate::*; use types::executed::CallType; use trace::{Tracer, VMTracer}; @@ -299,10 +298,9 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT #[cfg(test)] mod tests { use common::*; - use state::*; use engines::Engine; - use evm::{Ext}; - use substate::*; + use evm::Ext; + use state::{State, Substate}; use tests::helpers::*; use devtools::GuardedTempResult; use super::*; diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index adba16703..7304f5931 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -15,13 +15,12 @@ // along with Parity. If not, see . use super::test_common::*; -use state::*; +use state::{State, Substate}; use executive::*; use engines::Engine; use evm; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use externalities::*; -use substate::*; use types::executed::CallType; use tests::helpers::*; use ethjson; diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 5c9d2c5fe..d74139d8a 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -132,10 +132,8 @@ mod basic_types; mod env_info; mod pod_account; mod state; -mod account; mod account_db; mod builtin; -mod substate; mod executive; mod externalities; mod verification; diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 80332b3e8..797d1a265 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -15,8 +15,8 @@ // along with Parity. If not, see . use util::*; -use account::*; -use account_db::*; +use state::Account; +use account_db::AccountDBMut; use ethjson; use types::account_diff::*; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index aa0fc3ae5..e6a92642e 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -79,7 +79,7 @@ impl StateProducer { let address_hash = H256::random(); let balance: usize = rng.gen(); let nonce: usize = rng.gen(); - let acc = ::account::Account::new_basic(balance.into(), nonce.into()).rlp(); + let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); trie.insert(&address_hash[..], &acc).unwrap(); } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs deleted file mode 100644 index ec67c50ed..000000000 --- a/ethcore/src/state.rs +++ /dev/null @@ -1,1496 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::cell::{RefCell, RefMut}; - -use common::*; -use engines::Engine; -use executive::{Executive, TransactOptions}; -use evm::Factory as EvmFactory; -use account_db::*; -use trace::FlatTrace; -use pod_account::*; -use pod_state::{self, PodState}; -use types::state_diff::StateDiff; - -/// Used to return information about an `State::apply` operation. -pub struct ApplyOutcome { - /// The receipt for the applied transaction. - pub receipt: Receipt, - /// The trace for the applied transaction, if None if tracing is disabled. - pub trace: Vec, -} - -/// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result; - -/// Representation of the entire state of all accounts in the system. -pub struct State { - db: Box, - root: H256, - cache: RefCell>>, - snapshots: RefCell>>>>, - account_start_nonce: U256, - trie_factory: TrieFactory, -} - -const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ - Therefore creating a SecTrieDB with this state's root will not fail."; - -impl State { - /// Creates new state with empty state root - #[cfg(test)] - pub fn new(mut db: Box, account_start_nonce: U256, trie_factory: TrieFactory) -> State { - let mut root = H256::new(); - { - // init trie and reset root too null - let _ = trie_factory.create(db.as_hashdb_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - snapshots: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - trie_factory: trie_factory, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: Box, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result { - if !db.as_hashdb().contains(&root) { - return Err(TrieError::InvalidStateRoot(root)); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - snapshots: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - trie_factory: trie_factory, - }; - - Ok(state) - } - - /// Create a recoverable snaphot of this state - pub fn snapshot(&mut self) { - self.snapshots.borrow_mut().push(HashMap::new()); - } - - /// Merge last snapshot with previous - pub fn clear_snapshot(&mut self) { - // merge with previous snapshot - let last = self.snapshots.borrow_mut().pop(); - if let Some(mut snapshot) = last { - if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() { - for (k, v) in snapshot.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - - /// Revert to snapshot - pub fn revert_snapshot(&mut self) { - if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() { - for (k, v) in snapshot.drain() { - match v { - Some(v) => { - self.cache.borrow_mut().insert(k, v); - }, - None => { - self.cache.borrow_mut().remove(&k); - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: Option) { - if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { - if !snapshot.contains_key(address) { - snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account)); - return; - } - } - self.cache.borrow_mut().insert(address.clone(), account); - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { - if !snapshot.contains_key(address) { - snapshot.insert(address.clone(), self.cache.borrow().get(address).cloned()); - } - } - } - - /// Destroy the current object and return root and database. - pub fn drop(self) -> (H256, Box) { - (self.root, self.db) - } - - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256) { - self.insert_cache(contract, Some(Account::new_contract(balance, self.account_start_nonce))); - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, None); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> bool { - self.ensure_cached(a, false, |a| a.is_some()) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> U256 { - self.ensure_cached(a, false, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> U256 { - self.ensure_cached(a, false, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { - self.ensure_cached(address, false, - |a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn code(&self, a: &Address) -> Option { - self.ensure_cached(a, true, - |a| a.as_ref().map_or(None, |a|a.code().map(|x|x.to_vec()))) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256) { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); - self.require(a, false).add_balance(incr); - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256) { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)); - self.require(a, false).sub_balance(decr); - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) { - self.sub_balance(from, by); - self.add_balance(to, by); - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) { - self.require(a, false).inc_nonce() - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) { - self.require(a, false).set_storage(key, value) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code); - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code); - } - - /// Execute a given transaction. - /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { -// let old = self.to_pod(); - - let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; - let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); - - // TODO uncomment once to_pod() works correctly. -// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); - try!(self.commit()); - let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); - trace!(target: "state", "Transaction receipt: {:?}", receipt); - Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) - } - - /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. - /// `accounts` is mutable because we may need to commit the code or storage and record that. - #[cfg_attr(feature="dev", allow(match_ref_pats))] - pub fn commit_into( - trie_factory: &TrieFactory, - db: &mut HashDB, - root: &mut H256, - accounts: &mut HashMap> - ) -> Result<(), Error> { - // first, commit the sub trees. - // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? - for (address, ref mut a) in accounts.iter_mut() { - match a { - &mut&mut Some(ref mut account) if account.is_dirty() => { - let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address)); - account.commit_storage(trie_factory, &mut account_db); - account.commit_code(&mut account_db); - } - _ => {} - } - } - - { - let mut trie = trie_factory.from_existing(db, root).unwrap(); - for (address, ref mut a) in accounts.iter_mut() { - match **a { - Some(ref mut account) if account.is_dirty() => { - account.set_clean(); - try!(trie.insert(address, &account.rlp())) - }, - None => try!(trie.remove(address)), - _ => (), - } - } - } - - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.snapshots.borrow().is_empty()); - Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut()) - } - - /// Clear state cache - pub fn clear(&mut self) { - self.cache.borrow_mut().clear(); - } - - #[cfg(test)] - #[cfg(feature = "json-tests")] - /// Populate the state from `accounts`. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.snapshots.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc))); - } - } - - /// Populate a PodAccount map from this state. - pub fn to_pod(&self) -> PodState { - assert!(self.snapshots.borrow().is_empty()); - // TODO: handle database rather than just the cache. - // will need fat db. - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = *opt { - m.insert(add.clone(), PodAccount::from_account(acc)); - } - m - })) - } - - fn query_pod(&mut self, query: &PodState) { - for (address, pod_account) in query.get() { - self.ensure_cached(address, true, |a| { - if a.is_some() { - for key in pod_account.storage.keys() { - self.storage_at(address, key); - } - } - }); - } - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, orig: State) -> StateDiff { - let pod_state_post = self.to_pod(); - let mut state_pre = orig; - state_pre.query_pod(&pod_state_post); - pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post) - } - - /// Ensure account `a` is in our cache of the trie DB and return a handle for getting it. - /// `require_code` requires that the code be cached, too. - fn ensure_cached<'a, F, U>(&'a self, a: &'a Address, require_code: bool, f: F) -> U - where F: FnOnce(&Option) -> U { - let have_key = self.cache.borrow().contains_key(a); - if !have_key { - let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let maybe_acc = match db.get(a) { - Ok(acc) => acc.map(Account::from_rlp), - Err(e) => panic!("Potential DB corruption encountered: {}", e), - }; - self.insert_cache(a, maybe_acc); - } - if require_code { - if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { - let addr_hash = account.address_hash(a); - account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); - } - } - - f(self.cache.borrow().get(a).unwrap()) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> RefMut<'a, Account> { - self.require_or_from(a, require_code, || Account::new_basic(U256::from(0u8), self.account_start_nonce), |_|{}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) - -> RefMut<'a, Account> - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let maybe_acc = match db.get(a) { - Ok(acc) => acc.map(Account::from_rlp), - Err(e) => panic!("Potential DB corruption encountered: {}", e), - }; - - self.insert_cache(a, maybe_acc); - } else { - self.note_cache(a); - } - - match self.cache.borrow_mut().get_mut(a).unwrap() { - &mut Some(ref mut acc) => not_default(acc), - slot @ &mut None => *slot = Some(default()), - } - - RefMut::map(self.cache.borrow_mut(), |c| { - let account = c.get_mut(a).unwrap().as_mut().unwrap(); - if require_code { - let addr_hash = account.address_hash(a); - account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); - } - account - }) - } -} - -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } -} - -impl Clone for State { - fn clone(&self) -> State { - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(self.cache.borrow().clone()), - snapshots: RefCell::new(self.snapshots.borrow().clone()), - account_start_nonce: self.account_start_nonce.clone(), - trie_factory: self.trie_factory.clone(), - } - } -} - -#[cfg(test)] -mod tests { - -use std::str::FromStr; -use rustc_serialize::hex::FromHex; -use super::*; -use util::{U256, H256, FixedHash, Address, Hashable}; -use account::*; -use tests::helpers::*; -use devtools::*; -use env_info::*; -use spec::*; -use transaction::*; -use util::log::init_log; -use trace::trace; -use trace::FlatTrace; -use types::executed::CallType; - -#[test] -fn should_apply_create_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&"".sha3()); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 77412.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_work_when_cloned() { - init_log(); - - let a = Address::zero(); - - let temp = RandomTempPath::new(); - let mut state = { - let mut state = get_temp_state_in(temp.as_path()); - assert_eq!(state.exists(&a), false); - state.inc_nonce(&a); - state.commit().unwrap(); - state.clone() - }; - - state.inc_nonce(&a); - state.commit().unwrap(); -} - -#[test] -fn should_trace_failed_create_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&"".sha3()); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 78792.into(), - init: vec![91, 96, 0, 86], - }), - result: trace::Res::FailedCreate, - subtraces: 0 - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_basic_call_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(0), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_transaction_to_builtin() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0x1.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3()); - - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: "0000000000000000000000000000000000000001".into(), - value: 0.into(), - gas: 79_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3000), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_subcall_transaction_to_builtin() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(28_061), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_callcode() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 64.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xa.into(), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_delegatecall() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - info.number = 0x789b0; - let engine = &*Spec::new_test().engine; - - println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(61), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_call_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall, - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_basic_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_call_with_invalid_basic_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_subcall_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(135), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_subcall_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }) - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall, - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - call_type: CallType::Call, - input: vec![], - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_suicide() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3()); - - state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); - state.add_balance(&0xa.into(), &50.into()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Suicide(trace::Suicide { - address: 0xa.into(), - refund_address: 0xb.into(), - balance: 150.into(), - }), - result: trace::Res::None, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn code_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); - state.init_code(&a, vec![1, 2, 3]); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); - state.commit().unwrap(); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); -} - -#[test] -fn storage_at_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64))); - state.commit().unwrap(); - state.drop() - }; - - let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64))); -} - -#[test] -fn get_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.inc_nonce(&a); - state.add_balance(&a, &U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.balance(&a), U256::from(69u64)); - assert_eq!(state.nonce(&a), U256::from(1u64)); -} - -#[test] -fn remove() { - let a = Address::zero(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - assert_eq!(state.exists(&a), false); - state.inc_nonce(&a); - assert_eq!(state.exists(&a), true); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.kill_account(&a); - assert_eq!(state.exists(&a), false); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} - -#[test] -fn remove_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.inc_nonce(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a), true); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.drop() - }; - - let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a), true); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.kill_account(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a), false); - assert_eq!(state.nonce(&a), U256::from(0u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a), false); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} - -#[test] -fn alter_balance() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - let b = 1u64.into(); - state.add_balance(&a, &U256::from(69u64)); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.sub_balance(&a, &U256::from(42u64)); - assert_eq!(state.balance(&a), U256::from(27u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64)); - assert_eq!(state.balance(&a), U256::from(9u64)); - assert_eq!(state.balance(&b), U256::from(18u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(9u64)); - assert_eq!(state.balance(&b), U256::from(18u64)); -} - -#[test] -fn alter_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(2u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a), U256::from(2u64)); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(3u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a), U256::from(3u64)); -} - -#[test] -fn balance_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - assert_eq!(state.balance(&a), U256::from(0u64)); - assert_eq!(state.nonce(&a), U256::from(0u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(0u64)); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} - -#[test] -fn ensure_cached() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.require(&a, false); - state.commit().unwrap(); - assert_eq!(state.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785"); -} - -#[test] -fn snapshot_basic() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.snapshot(); - state.add_balance(&a, &U256::from(69u64)); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.clear_snapshot(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.snapshot(); - state.add_balance(&a, &U256::from(1u64)); - assert_eq!(state.balance(&a), U256::from(70u64)); - state.revert_snapshot(); - assert_eq!(state.balance(&a), U256::from(69u64)); -} - -#[test] -fn snapshot_nested() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.snapshot(); - state.snapshot(); - state.add_balance(&a, &U256::from(69u64)); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.clear_snapshot(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.revert_snapshot(); - assert_eq!(state.balance(&a), U256::from(0)); -} - -#[test] -fn create_empty() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - state.commit().unwrap(); - assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); -} - -} diff --git a/ethcore/src/account.rs b/ethcore/src/state/account.rs similarity index 100% rename from ethcore/src/account.rs rename to ethcore/src/state/account.rs diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs new file mode 100644 index 000000000..09a807d70 --- /dev/null +++ b/ethcore/src/state/mod.rs @@ -0,0 +1,1499 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::cell::{RefCell, RefMut}; +use common::*; +use engines::Engine; +use executive::{Executive, TransactOptions}; +use evm::Factory as EvmFactory; +use account_db::*; +use trace::FlatTrace; +use pod_account::*; +use pod_state::{self, PodState}; +use types::state_diff::StateDiff; + +mod account; +mod substate; + +pub use self::account::Account; +pub use self::substate::Substate; + +/// Used to return information about an `State::apply` operation. +pub struct ApplyOutcome { + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The trace for the applied transaction, if None if tracing is disabled. + pub trace: Vec, +} + +/// Result type for the execution ("application") of a transaction. +pub type ApplyResult = Result; + +/// Representation of the entire state of all accounts in the system. +pub struct State { + db: Box, + root: H256, + cache: RefCell>>, + snapshots: RefCell>>>>, + account_start_nonce: U256, + trie_factory: TrieFactory, +} + +const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ + Therefore creating a SecTrieDB with this state's root will not fail."; + +impl State { + /// Creates new state with empty state root + #[cfg(test)] + pub fn new(mut db: Box, account_start_nonce: U256, trie_factory: TrieFactory) -> State { + let mut root = H256::new(); + { + // init trie and reset root too null + let _ = trie_factory.create(db.as_hashdb_mut(), &mut root); + } + + State { + db: db, + root: root, + cache: RefCell::new(HashMap::new()), + snapshots: RefCell::new(Vec::new()), + account_start_nonce: account_start_nonce, + trie_factory: trie_factory, + } + } + + /// Creates new state with existing state root + pub fn from_existing(db: Box, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result { + if !db.as_hashdb().contains(&root) { + return Err(TrieError::InvalidStateRoot(root)); + } + + let state = State { + db: db, + root: root, + cache: RefCell::new(HashMap::new()), + snapshots: RefCell::new(Vec::new()), + account_start_nonce: account_start_nonce, + trie_factory: trie_factory, + }; + + Ok(state) + } + + /// Create a recoverable snaphot of this state + pub fn snapshot(&mut self) { + self.snapshots.borrow_mut().push(HashMap::new()); + } + + /// Merge last snapshot with previous + pub fn clear_snapshot(&mut self) { + // merge with previous snapshot + let last = self.snapshots.borrow_mut().pop(); + if let Some(mut snapshot) = last { + if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() { + for (k, v) in snapshot.drain() { + prev.entry(k).or_insert(v); + } + } + } + } + + /// Revert to snapshot + pub fn revert_snapshot(&mut self) { + if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() { + for (k, v) in snapshot.drain() { + match v { + Some(v) => { + self.cache.borrow_mut().insert(k, v); + }, + None => { + self.cache.borrow_mut().remove(&k); + } + } + } + } + } + + fn insert_cache(&self, address: &Address, account: Option) { + if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { + if !snapshot.contains_key(address) { + snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account)); + return; + } + } + self.cache.borrow_mut().insert(address.clone(), account); + } + + fn note_cache(&self, address: &Address) { + if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { + if !snapshot.contains_key(address) { + snapshot.insert(address.clone(), self.cache.borrow().get(address).cloned()); + } + } + } + + /// Destroy the current object and return root and database. + pub fn drop(self) -> (H256, Box) { + (self.root, self.db) + } + + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + + /// Create a new contract at address `contract`. If there is already an account at the address + /// it will have its code reset, ready for `init_code()`. + pub fn new_contract(&mut self, contract: &Address, balance: U256) { + self.insert_cache(contract, Some(Account::new_contract(balance, self.account_start_nonce))); + } + + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.insert_cache(account, None); + } + + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> bool { + self.ensure_cached(a, false, |a| a.is_some()) + } + + /// Get the balance of account `a`. + pub fn balance(&self, a: &Address) -> U256 { + self.ensure_cached(a, false, + |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) + } + + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> U256 { + self.ensure_cached(a, false, + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + } + + /// Mutate storage of account `address` so that it is `value` for `key`. + pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { + self.ensure_cached(address, false, + |a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn code(&self, a: &Address) -> Option { + self.ensure_cached(a, true, + |a| a.as_ref().map_or(None, |a|a.code().map(|x|x.to_vec()))) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance(&mut self, a: &Address, incr: &U256) { + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); + self.require(a, false).add_balance(incr); + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256) { + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)); + self.require(a, false).sub_balance(decr); + } + + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) { + self.sub_balance(from, by); + self.add_balance(to, by); + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) { + self.require(a, false).inc_nonce() + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) { + self.require(a, false).set_storage(key, value) + } + + /// Initialise the code of account `a` so that it is `code`. + /// NOTE: Account should have been created with `new_contract`. + pub fn init_code(&mut self, a: &Address, code: Bytes) { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code); + } + + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code); + } + + /// Execute a given transaction. + /// This will change the state accordingly. + pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { +// let old = self.to_pod(); + + let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; + let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); + + // TODO uncomment once to_pod() works correctly. +// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); + try!(self.commit()); + let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); + trace!(target: "state", "Transaction receipt: {:?}", receipt); + Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) + } + + /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. + /// `accounts` is mutable because we may need to commit the code or storage and record that. + #[cfg_attr(feature="dev", allow(match_ref_pats))] + pub fn commit_into( + trie_factory: &TrieFactory, + db: &mut HashDB, + root: &mut H256, + accounts: &mut HashMap> + ) -> Result<(), Error> { + // first, commit the sub trees. + // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? + for (address, ref mut a) in accounts.iter_mut() { + match a { + &mut&mut Some(ref mut account) if account.is_dirty() => { + let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address)); + account.commit_storage(trie_factory, &mut account_db); + account.commit_code(&mut account_db); + } + _ => {} + } + } + + { + let mut trie = trie_factory.from_existing(db, root).unwrap(); + for (address, ref mut a) in accounts.iter_mut() { + match **a { + Some(ref mut account) if account.is_dirty() => { + account.set_clean(); + try!(trie.insert(address, &account.rlp())) + }, + None => try!(trie.remove(address)), + _ => (), + } + } + } + + Ok(()) + } + + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) -> Result<(), Error> { + assert!(self.snapshots.borrow().is_empty()); + Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut()) + } + + /// Clear state cache + pub fn clear(&mut self) { + self.cache.borrow_mut().clear(); + } + + #[cfg(test)] + #[cfg(feature = "json-tests")] + /// Populate the state from `accounts`. + pub fn populate_from(&mut self, accounts: PodState) { + assert!(self.snapshots.borrow().is_empty()); + for (add, acc) in accounts.drain().into_iter() { + self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc))); + } + } + + /// Populate a PodAccount map from this state. + pub fn to_pod(&self) -> PodState { + assert!(self.snapshots.borrow().is_empty()); + // TODO: handle database rather than just the cache. + // will need fat db. + PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { + if let Some(ref acc) = *opt { + m.insert(add.clone(), PodAccount::from_account(acc)); + } + m + })) + } + + fn query_pod(&mut self, query: &PodState) { + for (address, pod_account) in query.get() { + self.ensure_cached(address, true, |a| { + if a.is_some() { + for key in pod_account.storage.keys() { + self.storage_at(address, key); + } + } + }); + } + } + + /// Returns a `StateDiff` describing the difference from `orig` to `self`. + /// Consumes self. + pub fn diff_from(&self, orig: State) -> StateDiff { + let pod_state_post = self.to_pod(); + let mut state_pre = orig; + state_pre.query_pod(&pod_state_post); + pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post) + } + + /// Ensure account `a` is in our cache of the trie DB and return a handle for getting it. + /// `require_code` requires that the code be cached, too. + fn ensure_cached<'a, F, U>(&'a self, a: &'a Address, require_code: bool, f: F) -> U + where F: FnOnce(&Option) -> U { + let have_key = self.cache.borrow().contains_key(a); + if !have_key { + let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let maybe_acc = match db.get(a) { + Ok(acc) => acc.map(Account::from_rlp), + Err(e) => panic!("Potential DB corruption encountered: {}", e), + }; + self.insert_cache(a, maybe_acc); + } + if require_code { + if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { + let addr_hash = account.address_hash(a); + account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); + } + } + + f(self.cache.borrow().get(a).unwrap()) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + fn require<'a>(&'a self, a: &Address, require_code: bool) -> RefMut<'a, Account> { + self.require_or_from(a, require_code, || Account::new_basic(U256::from(0u8), self.account_start_nonce), |_|{}) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// If it doesn't exist, make account equal the evaluation of `default`. + fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) + -> RefMut<'a, Account> + { + let contains_key = self.cache.borrow().contains_key(a); + if !contains_key { + let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let maybe_acc = match db.get(a) { + Ok(acc) => acc.map(Account::from_rlp), + Err(e) => panic!("Potential DB corruption encountered: {}", e), + }; + + self.insert_cache(a, maybe_acc); + } else { + self.note_cache(a); + } + + match self.cache.borrow_mut().get_mut(a).unwrap() { + &mut Some(ref mut acc) => not_default(acc), + slot @ &mut None => *slot = Some(default()), + } + + RefMut::map(self.cache.borrow_mut(), |c| { + let account = c.get_mut(a).unwrap().as_mut().unwrap(); + if require_code { + let addr_hash = account.address_hash(a); + account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); + } + account + }) + } +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.cache.borrow()) + } +} + +impl Clone for State { + fn clone(&self) -> State { + State { + db: self.db.boxed_clone(), + root: self.root.clone(), + cache: RefCell::new(self.cache.borrow().clone()), + snapshots: RefCell::new(self.snapshots.borrow().clone()), + account_start_nonce: self.account_start_nonce.clone(), + trie_factory: self.trie_factory.clone(), + } + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use super::*; + use util::{U256, H256, FixedHash, Address, Hashable}; + use tests::helpers::*; + use devtools::*; + use env_info::*; + use spec::*; + use transaction::*; + use util::log::init_log; + use trace::trace; + use trace::FlatTrace; + use types::executed::CallType; + + #[test] + fn should_apply_create_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), + }.sign(&"".sha3()); + + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 77412.into(), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_work_when_cloned() { + init_log(); + + let a = Address::zero(); + + let temp = RandomTempPath::new(); + let mut state = { + let mut state = get_temp_state_in(temp.as_path()); + assert_eq!(state.exists(&a), false); + state.inc_nonce(&a); + state.commit().unwrap(); + state.clone() + }; + + state.inc_nonce(&a); + state.commit().unwrap(); + } + + #[test] + fn should_trace_failed_create_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("5b600056").unwrap(), + }.sign(&"".sha3()); + + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 78792.into(), + init: vec![91, 96, 0, 86], + }), + result: trace::Res::FailedCreate, + subtraces: 0 + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_basic_call_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(0), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction_to_builtin() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0x1.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3()); + + let vm_factory = Default::default(); + let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: "0000000000000000000000000000000000000001".into(), + value: 0.into(), + gas: 79_000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_subcall_transaction_to_builtin() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); + let vm_factory = Default::default(); + let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(28_061), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_callcode() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + let vm_factory = Default::default(); + let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 64.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xa.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_delegatecall() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + info.number = 0x789b0; + let engine = &*Spec::new_test().engine; + + println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + let vm_factory = Default::default(); + let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(61), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_call_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall, + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult::default()), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(135), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }) + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall, + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + call_type: CallType::Call, + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_suicide() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); + state.add_balance(&0xa.into(), &50.into()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + let vm_factory = Default::default(); + let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: 0xa.into(), + refund_address: 0xb.into(), + balance: 150.into(), + }), + result: trace::Res::None, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn code_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); + state.init_code(&a, vec![1, 2, 3]); + assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + state.commit().unwrap(); + assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); + } + + #[test] + fn storage_at_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64))); + state.commit().unwrap(); + state.drop() + }; + + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64))); + } + + #[test] + fn get_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.inc_nonce(&a); + state.add_balance(&a, &U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.balance(&a), U256::from(69u64)); + assert_eq!(state.nonce(&a), U256::from(1u64)); + } + + #[test] + fn remove() { + let a = Address::zero(); + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + assert_eq!(state.exists(&a), false); + state.inc_nonce(&a); + assert_eq!(state.exists(&a), true); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.kill_account(&a); + assert_eq!(state.exists(&a), false); + assert_eq!(state.nonce(&a), U256::from(0u64)); + } + + #[test] + fn remove_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.inc_nonce(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a), true); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.drop() + }; + + let (root, db) = { + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a), true); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.kill_account(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a), false); + assert_eq!(state.nonce(&a), U256::from(0u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a), false); + assert_eq!(state.nonce(&a), U256::from(0u64)); + } + + #[test] + fn alter_balance() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + let b = 1u64.into(); + state.add_balance(&a, &U256::from(69u64)); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.sub_balance(&a, &U256::from(42u64)); + assert_eq!(state.balance(&a), U256::from(27u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(27u64)); + state.transfer_balance(&a, &b, &U256::from(18u64)); + assert_eq!(state.balance(&a), U256::from(9u64)); + assert_eq!(state.balance(&b), U256::from(18u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(9u64)); + assert_eq!(state.balance(&b), U256::from(18u64)); + } + + #[test] + fn alter_nonce() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(2u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a), U256::from(2u64)); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(3u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a), U256::from(3u64)); + } + + #[test] + fn balance_nonce() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + assert_eq!(state.balance(&a), U256::from(0u64)); + assert_eq!(state.nonce(&a), U256::from(0u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(0u64)); + assert_eq!(state.nonce(&a), U256::from(0u64)); + } + + #[test] + fn ensure_cached() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.require(&a, false); + state.commit().unwrap(); + assert_eq!(state.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785"); + } + + #[test] + fn snapshot_basic() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.snapshot(); + state.add_balance(&a, &U256::from(69u64)); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.clear_snapshot(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.snapshot(); + state.add_balance(&a, &U256::from(1u64)); + assert_eq!(state.balance(&a), U256::from(70u64)); + state.revert_snapshot(); + assert_eq!(state.balance(&a), U256::from(69u64)); + } + + #[test] + fn snapshot_nested() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.snapshot(); + state.snapshot(); + state.add_balance(&a, &U256::from(69u64)); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.clear_snapshot(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.revert_snapshot(); + assert_eq!(state.balance(&a), U256::from(0)); + } + + #[test] + fn create_empty() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + state.commit().unwrap(); + assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); + } + +} diff --git a/ethcore/src/substate.rs b/ethcore/src/state/substate.rs similarity index 100% rename from ethcore/src/substate.rs rename to ethcore/src/state/substate.rs diff --git a/ethcore/src/state_diff.rs b/ethcore/src/state_diff.rs deleted file mode 100644 index c362d96d1..000000000 --- a/ethcore/src/state_diff.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use util::*; -#[cfg(test)] -use pod_state::*; -use account_diff::*; - -#[derive(Debug,Clone,PartialEq,Eq)] -/// Expression for the delta between two system states. Encoded the -/// delta of every altered account. -pub struct StateDiff (BTreeMap); - -impl StateDiff { - #[cfg(test)] - /// Calculate and return diff between `pre` state and `post` state. - pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { - StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| AccountDiff::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect()) - } -} - -impl fmt::Display for StateDiff { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.0 { - try!(write!(f, "{} {}: {}", acc.existance(), add, acc)); - } - Ok(()) - } -} - -impl Deref for StateDiff { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[cfg(test)] -mod test { - use common::*; - use pod_state::*; - use account_diff::*; - use pod_account::*; - use super::*; - - #[test] - fn create_delete() { - let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); - assert_eq!(StateDiff::diff_pod(&a, &PodState::new()), StateDiff(map![ - 1.into() => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ])); - assert_eq!(StateDiff::diff_pod(&PodState::new(), &a), StateDiff(map![ - 1.into() => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ])); - } - - #[test] - fn create_delete_with_unchanged() { - let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); - let b = PodState::from(map![ - 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) - ]); - assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ - 2.into() => AccountDiff{ - balance: Diff::Born(69.into()), - nonce: Diff::Born(0.into()), - code: Diff::Born(vec![]), - storage: map![], - } - ])); - assert_eq!(StateDiff::diff_pod(&b, &a), StateDiff(map![ - 2.into() => AccountDiff{ - balance: Diff::Died(69.into()), - nonce: Diff::Died(0.into()), - code: Diff::Died(vec![]), - storage: map![], - } - ])); - } - - #[test] - fn change_with_unchanged() { - let a = PodState::from(map![ - 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) - ]); - let b = PodState::from(map![ - 1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) - ]); - assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ - 1.into() => AccountDiff{ - balance: Diff::Same, - nonce: Diff::Changed(0.into(), 1.into()), - code: Diff::Same, - storage: map![], - } - ])); - } - -}