Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Integrate state diffing into the ethcore JSONRPC #1206

Merged
merged 9 commits into from
Jun 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions ethcore/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ impl Account {
}
}

#[cfg(test)]
#[cfg(feature = "json-tests")]
/// General constructor.
pub fn from_pod(pod: PodAccount) -> Account {
Account {
Expand Down
27 changes: 18 additions & 9 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics};
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
Expand Down Expand Up @@ -429,14 +429,14 @@ impl<V> Client<V> where V: Verifier {
TransactionID::Hash(ref hash) => self.chain.transaction_address(hash),
TransactionID::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
block_hash: hash,
index: index
index: index,
})
}
}
}

impl<V> BlockChainClient for Client<V> where V: Verifier {
fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError> {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash());
Expand All @@ -456,11 +456,21 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
ExecutionError::TransactionMalformed(message)
}));
let balance = state.balance(&sender);
// give the sender a decent balance
state.sub_balance(&sender, &balance);
state.add_balance(&sender, &(U256::from(1) << 200));
let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };
Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options)
let needed_balance = t.value + t.gas * t.gas_price;
if balance < needed_balance {
// give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance));
}
let options = TransactOptions { tracing: false, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options);

// TODO gav move this into Executive.
if analytics.state_diffing {
if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(self.state()));
}
}
ret
}

fn vm_factory(&self) -> &EvmFactory {
Expand Down Expand Up @@ -505,7 +515,6 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
self.state_at(id).map(|s| s.nonce(address))
}


fn block_hash(&self, id: BlockID) -> Option<H256> {
Self::block_hash(&self.chain, id)
}
Expand Down
11 changes: 10 additions & 1 deletion ethcore/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ use evm::Factory as EvmFactory;
use miner::{TransactionImportResult};
use error::Error as EthError;

/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
pub struct CallAnalytics {
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
}

/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Get raw block header data by block id.
Expand Down Expand Up @@ -158,7 +167,7 @@ pub trait BlockChainClient : Sync + Send {

/// Makes a non-persistent transaction call.
// TODO: should be able to accept blockchain location for call.
fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError>;
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;

/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes};
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
Expand Down Expand Up @@ -251,7 +251,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
}

impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
Ok(self.execution_result.read().unwrap().clone().unwrap())
}

Expand Down
2 changes: 2 additions & 0 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ impl<'a> Executive<'a> {
output: output,
trace: trace,
vm_trace: vm_trace,
state_diff: None,
})
},
_ => {
Expand All @@ -463,6 +464,7 @@ impl<'a> Executive<'a> {
output: output,
trace: trace,
vm_trace: vm_trace,
state_diff: None,
})
},
}
Expand Down
5 changes: 2 additions & 3 deletions ethcore/src/json_tests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@

use super::test_common::*;
use tests::helpers::*;
use pod_state::*;
use state_diff::*;
use pod_state::{self, PodState};
use ethereum;
use ethjson;

Expand Down Expand Up @@ -71,7 +70,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
let our_post = state.to_pod();
println!("Got:\n{}", our_post);
println!("Expect:\n{}", post);
println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post));
println!("Diff ---expect -> +++got:\n{}", pod_state::diff_pod(&post, &our_post));
}

if let Ok(r) = res {
Expand Down
2 changes: 0 additions & 2 deletions ethcore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ mod basic_types;
#[macro_use] mod evm;
mod env_info;
mod pod_account;
mod account_diff;
mod state_diff;
mod state;
mod account;
mod account_db;
Expand Down
31 changes: 20 additions & 11 deletions ethcore/src/miner/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ use std::sync::atomic::AtomicBool;
use util::*;
use util::keys::store::{AccountProvider};
use views::{BlockView, HeaderView};
use client::{MiningBlockChainClient, BlockID};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockChainClient, BlockID, CallAnalytics};
use block::{ClosedBlock, IsBlock};
use error::*;
use client::{Executive, Executed, EnvInfo, TransactOptions};
use transaction::SignedTransaction;
use receipt::{Receipt};
use spec::Spec;
Expand Down Expand Up @@ -251,11 +250,13 @@ impl MinerService for Miner {
}
}

fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError> {
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() {
Some(work) => {
let block = work.block();

// TODO: merge this code with client.rs's fn call somwhow.
let header = block.header();
let last_hashes = chain.last_hashes();
let env_info = EnvInfo {
Expand All @@ -274,16 +275,24 @@ impl MinerService for Miner {
ExecutionError::TransactionMalformed(message)
}));
let balance = state.balance(&sender);
// give the sender max balance
state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value());
let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };

// TODO: use vm_trace here.
Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options)
let needed_balance = t.value + t.gas * t.gas_price;
if balance < needed_balance {
// give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance));
}
let options = TransactOptions { tracing: false, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options);

// TODO gav move this into Executive.
if analytics.state_diffing {
if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(block.state().clone()));
}
}
ret
},
None => {
chain.call(t, vm_tracing)
chain.call(t, analytics)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/miner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub use self::external::{ExternalMiner, ExternalMinerService};

use std::collections::BTreeMap;
use util::{H256, U256, Address, Bytes};
use client::{MiningBlockChainClient, Executed};
use client::{MiningBlockChainClient, Executed, CallAnalytics};
use block::ClosedBlock;
use receipt::Receipt;
use error::{Error, ExecutionError};
Expand Down Expand Up @@ -148,7 +148,7 @@ pub trait MinerService : Send + Sync {
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;

/// Call into contract code using pending state.
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError>;
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;

/// Get storage value in pending state.
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256;
Expand Down
56 changes: 49 additions & 7 deletions ethcore/src/pod_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use util::*;
use account::*;
use account_db::*;
use ethjson;
use types::account_diff::*;

#[derive(Debug, Clone, PartialEq, Eq)]
/// An account, expressed as Plain-Old-Data (hence the name).
Expand Down Expand Up @@ -106,17 +107,58 @@ impl fmt::Display for PodAccount {
}
}

/// Determine difference between two optionally existant `Account`s. Returns None
/// if they are the same.
pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> {
match (pre, post) {
(None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance),
nonce: Diff::Born(x.nonce),
code: Diff::Born(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance),
nonce: Diff::Died(x.nonce),
code: Diff::Died(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}),
(Some(pre), Some(post)) => {
let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys())
.filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new()))
.collect();
let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()),
storage: storage.into_iter().map(|k|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
post.storage.get(&k).cloned().unwrap_or_else(H256::new)
))).collect(),
};
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() {
None
} else {
Some(r)
}
},
_ => None,
}
}


#[cfg(test)]
mod test {
use common::*;
use account_diff::*;
use super::*;
use types::account_diff::*;
use super::{PodAccount, diff_pod};

#[test]
fn existence() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None);
assert_eq!(AccountDiff::diff_pod(None, Some(&a)), Some(AccountDiff{
assert_eq!(diff_pod(Some(&a), Some(&a)), None);
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
Expand All @@ -128,7 +170,7 @@ mod test {
fn basic() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Changed(69.into(), 42.into()),
nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same,
Expand All @@ -140,7 +182,7 @@ mod test {
fn code() {
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Changed(vec![], vec![0]),
Expand All @@ -162,7 +204,7 @@ mod test {
code: vec![],
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Same,
code: Diff::Same,
Expand Down
Loading