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![],
- }
- ]));
- }
-
-}