Backport commits to beta (#1763)

* Don't try to sync to ancient blocks

* Parallel block body download

* Fixed reading chunked EIP8 handshake (#1712)

* Fixed reading chunked EIP8 handshake

* Added missing break

* Disconnect peers on a fork

* Updated json-ipc-server

* Combine mining queue and enabled into single locked datum (#1749)

* Combine mining queue and enabled into single locked datum

Additional tracing.

* Fix bug uncovered by test.

* Fix typo

* Remove unneeded log initialisation in test.

* fix failing test (#1756)

* Fixed test

* Suicides tracing (#1688)

* tracing suicide

* fixed #1635

* fixed typo

* Stackoverflow #1686 (#1698)

* flat trace serialization

* tracing finds transaction which creates contract

* flatten traces before inserting them to the db

* Trace other types of calls (#1727)

* Trace through DELEGATECALL and CALLCODE

Add them to the JSON output and RLP database store.

* Fix tests.

* Fix all tests.

* Fix one more test.

* filtering transactions toAddress includes contract creation (#1697)

* tracing finds transaction which creates contract

* comma cleanup

Remove when following `}`s, add to final entries.

* Various improvements to tracing & diagnostics. (#1707)

* Various improvements to tracing & diagnostics.

- Manage possibility of `Account` not having code for `PodAccount`
- New RPC: `trace_sendRawTransaction`
- See raw transaction dump when inspecting over RPC

* Fix test

* Remove one of the dupe error messages

* Remove unneeded `&`s

* Reformat and extremely minor optimisation

* Minor optimisation

* Remove unneeded let

* Fix tests.

* Additional fix.

* Minor rename.

* Bowing to the pressure.

* Stackoverflow fix (#1742)

* executive tracer builds flat traces without intermediate struct

* temporarilt commented out tests for traces

* fixed new way of building trace address

* fixed new way of building trace address

* updating state tests with flat tracing in progress

* fixed flat tracing tests

* fixed compiling ethcore-rpc with new flat traces

* removed warnings from ethcore module

* remove unused data structures
This commit is contained in:
Arkadiy Paronyan 2016-07-30 15:37:18 +02:00 committed by Gav Wood
parent 429c83f92f
commit 8017daf47c
47 changed files with 1441 additions and 891 deletions

2
Cargo.lock generated
View File

@ -597,7 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "json-ipc-server" name = "json-ipc-server"
version = "0.2.4" version = "0.2.4"
source = "git+https://github.com/ethcore/json-ipc-server.git#902b031b8f50a59ecb4f389cbec1d264a98556bc" source = "git+https://github.com/ethcore/json-ipc-server.git#93c2756f669c6a1872dec1ef755a0870f40c03c3"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -17,7 +17,9 @@
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"networkID" : "0x1" "networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
}, },
"genesis": { "genesis": {
"seal": { "seal": {

View File

@ -137,7 +137,9 @@
"accountStartNonce": "0x00", "accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"networkID" : "0x1" "networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
}, },
"genesis": { "genesis": {
"seal": { "seal": {

View File

@ -58,8 +58,8 @@ impl Account {
nonce: pod.nonce, nonce: pod.nonce,
storage_root: SHA3_NULL_RLP, storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()), storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(pod.code.sha3()), code_hash: pod.code.as_ref().map(|c| c.sha3()),
code_cache: pod.code code_cache: pod.code.as_ref().map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c.clone()),
} }
} }

View File

@ -17,6 +17,7 @@
//! Evm input params. //! Evm input params.
use common::*; use common::*;
use ethjson; use ethjson;
use types::executed::CallType;
/// Transaction value /// Transaction value
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -58,7 +59,10 @@ pub struct ActionParams {
/// Code being executed. /// Code being executed.
pub code: Option<Bytes>, pub code: Option<Bytes>,
/// Input data. /// Input data.
pub data: Option<Bytes> pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
} }
impl Default for ActionParams { impl Default for ActionParams {
@ -73,16 +77,18 @@ impl Default for ActionParams {
gas_price: U256::zero(), gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()), value: ActionValue::Transfer(U256::zero()),
code: None, code: None,
data: None data: None,
call_type: CallType::None,
} }
} }
} }
impl From<ethjson::vm::Transaction> for ActionParams { impl From<ethjson::vm::Transaction> for ActionParams {
fn from(t: ethjson::vm::Transaction) -> Self { fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams { ActionParams {
code_address: Address::new(), code_address: Address::new(),
address: t.address.into(), address: address,
sender: t.sender.into(), sender: t.sender.into(),
origin: t.origin.into(), origin: t.origin.into(),
code: Some(t.code.into()), code: Some(t.code.into()),
@ -90,6 +96,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
gas: t.gas.into(), gas: t.gas.into(),
gas_price: t.gas_price.into(), gas_price: t.gas_price.into(),
value: ActionValue::Transfer(t.value.into()), value: ActionValue::Transfer(t.value.into()),
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
} }
} }
} }

View File

@ -22,7 +22,7 @@ use common::*;
use engine::*; use engine::*;
use state::*; use state::*;
use verification::PreverifiedBlock; use verification::PreverifiedBlock;
use trace::Trace; use trace::FlatTrace;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
@ -78,7 +78,7 @@ pub struct ExecutedBlock {
receipts: Vec<Receipt>, receipts: Vec<Receipt>,
transactions_set: HashSet<H256>, transactions_set: HashSet<H256>,
state: State, state: State,
traces: Option<Vec<Trace>>, traces: Option<Vec<Vec<FlatTrace>>>,
} }
/// A set of references to `ExecutedBlock` fields that are publicly accessible. /// A set of references to `ExecutedBlock` fields that are publicly accessible.
@ -94,7 +94,7 @@ pub struct BlockRefMut<'a> {
/// State. /// State.
pub state: &'a mut State, pub state: &'a mut State,
/// Traces. /// Traces.
pub traces: &'a Option<Vec<Trace>>, pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
} }
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible. /// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
@ -110,7 +110,7 @@ pub struct BlockRef<'a> {
/// State. /// State.
pub state: &'a State, pub state: &'a State,
/// Traces. /// Traces.
pub traces: &'a Option<Vec<Trace>>, pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
} }
impl ExecutedBlock { impl ExecutedBlock {
@ -171,7 +171,7 @@ pub trait IsBlock {
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts } fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
/// Get all information concerning transaction tracing in this block. /// Get all information concerning transaction tracing in this block.
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces } fn traces(&self) -> &Option<Vec<Vec<FlatTrace>>> { &self.block().traces }
/// Get all uncles in this block. /// Get all uncles in this block.
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles } fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
@ -329,7 +329,7 @@ impl<'x> OpenBlock<'x> {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t); self.block.base.transactions.push(t);
let t = outcome.trace; let t = outcome.trace;
self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed"))); self.block.traces.as_mut().map(|traces| traces.push(t));
self.block.receipts.push(outcome.receipt); self.block.receipts.push(outcome.receipt);
Ok(&self.block.receipts.last().unwrap()) Ok(&self.block.receipts.last().unwrap())
} }

View File

@ -48,6 +48,9 @@ use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize; pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace; use trace;
use trace::FlatTransactionTraces;
// re-export
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus; pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
@ -289,8 +292,6 @@ impl Client {
let _timer = PerfTimer::new("import_verified_blocks"); let _timer = PerfTimer::new("import_verified_blocks");
let blocks = self.block_queue.drain(max_blocks_to_import); let blocks = self.block_queue.drain(max_blocks_to_import);
let original_best = self.chain_info().best_block_hash;
for block in blocks { for block in blocks {
let header = &block.header; let header = &block.header;
@ -343,10 +344,6 @@ impl Client {
} }
} }
if self.chain_info().best_block_hash != original_best {
self.miner.update_sealing(self);
}
imported imported
} }
@ -361,8 +358,13 @@ impl Client {
}; };
// Commit results // Commit results
let receipts = block.receipts().clone(); let receipts = block.receipts().to_owned();
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); let traces = block.traces().clone().unwrap_or_else(Vec::new);
let traces: Vec<FlatTransactionTraces> = traces.into_iter()
.map(Into::into)
.collect();
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
// CHECK! I *think* this is fine, even if the state_root is equal to another // CHECK! I *think* this is fine, even if the state_root is equal to another
// already-imported block of the same number. // already-imported block of the same number.
@ -373,7 +375,7 @@ impl Client {
// (when something is in chain but you are not able to fetch details) // (when something is in chain but you are not able to fetch details)
let route = self.chain.insert_block(block_data, receipts); let route = self.chain.insert_block(block_data, receipts);
self.tracedb.import(TraceImportRequest { self.tracedb.import(TraceImportRequest {
traces: traces, traces: traces.into(),
block_hash: hash.clone(), block_hash: hash.clone(),
block_number: number, block_number: number,
enacted: route.enacted.clone(), enacted: route.enacted.clone(),
@ -840,8 +842,6 @@ impl MiningBlockChainClient for Client {
let _import_lock = self.import_lock.lock(); let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_sealed_block"); let _timer = PerfTimer::new("import_sealed_block");
let original_best = self.chain_info().best_block_hash;
let h = block.header().hash(); let h = block.header().hash();
let number = block.header().number(); let number = block.header().number();
@ -862,10 +862,6 @@ impl MiningBlockChainClient for Client {
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e)); })).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
} }
if self.chain_info().best_block_hash != original_best {
self.miner.update_sealing(self);
}
Ok(h) Ok(h)
} }
} }

View File

@ -18,6 +18,7 @@
use util::common::*; use util::common::*;
use evm::{self, Schedule}; use evm::{self, Schedule};
use types::executed::CallType;
use env_info::*; use env_info::*;
/// Result of externalities create function. /// Result of externalities create function.
@ -69,13 +70,15 @@ pub trait Ext {
/// and true if subcall was successfull. /// and true if subcall was successfull.
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
fn call(&mut self, fn call(&mut self,
gas: &U256, gas: &U256,
sender_address: &Address, sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
output: &mut [u8]) -> MessageCallResult; output: &mut [u8],
call_type: CallType
) -> MessageCallResult;
/// Returns code at given address /// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes; fn extcode(&self, address: &Address) -> Bytes;

View File

@ -20,6 +20,7 @@ use common::*;
use super::instructions as instructions; use super::instructions as instructions;
use super::instructions::{Instruction, get_info}; use super::instructions::{Instruction, get_info};
use std::marker::Copy; use std::marker::Copy;
use types::executed::CallType;
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))] #[cfg(not(feature = "evm-debug"))]
@ -648,16 +649,16 @@ impl Interpreter {
}); });
// Get sender & receive addresses, check if we have balance // Get sender & receive addresses, check if we have balance
let (sender_address, receive_address, has_balance) = match instruction { let (sender_address, receive_address, has_balance, call_type) = match instruction {
instructions::CALL => { instructions::CALL => {
let has_balance = ext.balance(&params.address) >= value.unwrap(); let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &code_address, has_balance) (&params.address, &code_address, has_balance, CallType::Call)
}, },
instructions::CALLCODE => { instructions::CALLCODE => {
let has_balance = ext.balance(&params.address) >= value.unwrap(); let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &params.address, has_balance) (&params.address, &params.address, has_balance, CallType::CallCode)
}, },
instructions::DELEGATECALL => (&params.sender, &params.address, true), instructions::DELEGATECALL => (&params.sender, &params.address, true, CallType::DelegateCall),
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
}; };
@ -672,7 +673,7 @@ impl Interpreter {
// and we don't want to copy // and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_size); let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output) ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output, call_type)
}; };
return match call_result { return match call_result {

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*; use common::*;
use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug; use std::fmt::Debug;
@ -36,7 +37,7 @@ struct FakeCall {
receive_address: Option<Address>, receive_address: Option<Address>,
value: Option<U256>, value: Option<U256>,
data: Bytes, data: Bytes,
code_address: Option<Address> code_address: Option<Address>,
} }
/// Fake externalities test structure. /// Fake externalities test structure.
@ -119,7 +120,9 @@ impl Ext for FakeExt {
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
_output: &mut [u8]) -> MessageCallResult { _output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.calls.insert(FakeCall { self.calls.insert(FakeCall {
call_type: FakeCallType::Call, call_type: FakeCallType::Call,

View File

@ -18,10 +18,11 @@
use common::*; use common::*;
use state::*; use state::*;
use engine::*; use engine::*;
use types::executed::CallType;
use evm::{self, Ext, Factory, Finalize}; use evm::{self, Ext, Factory, Finalize};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam; use crossbeam;
pub use types::executed::{Executed, ExecutionResult}; pub use types::executed::{Executed, ExecutionResult};
@ -173,6 +174,7 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.value),
code: Some(t.data.clone()), code: Some(t.data.clone()),
data: None, data: None,
call_type: CallType::None,
}; };
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
}, },
@ -187,6 +189,7 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.value),
code: self.state.code(address), code: self.state.code(address),
data: Some(t.data.clone()), data: Some(t.data.clone()),
call_type: CallType::Call,
}; };
// TODO: move output upstream // TODO: move output upstream
let mut out = vec![]; let mut out = vec![];
@ -195,7 +198,7 @@ impl<'a> Executive<'a> {
}; };
// finalize here! // finalize here!
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())))
} }
fn exec_vm<T, V>( fn exec_vm<T, V>(
@ -248,8 +251,6 @@ impl<'a> Executive<'a> {
} }
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
let delegate_call = params.code_address != params.address;
if self.engine.is_builtin(&params.code_address) { if self.engine.is_builtin(&params.code_address) {
// if destination is builtin, try to execute it // if destination is builtin, try to execute it
@ -275,20 +276,15 @@ impl<'a> Executive<'a> {
trace_info, trace_info,
cost, cost,
trace_output, trace_output,
self.depth, vec![]
vec![],
delegate_call
); );
} }
Ok(params.gas - cost) Ok(params.gas - cost)
}, },
// just drain the whole gas // just drain the whole gas
false => { false => {
self.state.revert_snapshot(); self.state.revert_snapshot();
tracer.trace_failed_call(trace_info, vec![]);
tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call);
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
} }
} }
@ -320,11 +316,9 @@ impl<'a> Executive<'a> {
trace_info, trace_info,
gas - gas_left, gas - gas_left,
trace_output, trace_output,
self.depth, traces
traces,
delegate_call
), ),
_ => tracer.trace_failed_call(trace_info, self.depth, traces, delegate_call), _ => tracer.trace_failed_call(trace_info, traces),
}; };
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
@ -336,7 +330,7 @@ impl<'a> Executive<'a> {
// otherwise it's just a basic transaction, only do tracing, if necessary. // otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.clear_snapshot(); self.state.clear_snapshot();
tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![], delegate_call); tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
Ok(params.gas) Ok(params.gas)
} }
} }
@ -387,10 +381,9 @@ impl<'a> Executive<'a> {
gas - gas_left, gas - gas_left,
trace_output, trace_output,
created, created,
self.depth,
subtracer.traces() subtracer.traces()
), ),
_ => tracer.trace_failed_create(trace_info, self.depth, subtracer.traces()) _ => tracer.trace_failed_create(trace_info, subtracer.traces())
}; };
self.enact_result(&res, substate, unconfirmed_substate); self.enact_result(&res, substate, unconfirmed_substate);
@ -404,7 +397,7 @@ impl<'a> Executive<'a> {
substate: Substate, substate: Substate,
result: evm::Result<U256>, result: evm::Result<U256>,
output: Bytes, output: Bytes,
trace: Option<Trace>, trace: Vec<FlatTrace>,
vm_trace: Option<VMTrace> vm_trace: Option<VMTrace>
) -> ExecutionResult { ) -> ExecutionResult {
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
@ -496,8 +489,9 @@ mod tests {
use substate::*; use substate::*;
use tests::helpers::*; use tests::helpers::*;
use trace::trace; use trace::trace;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use types::executed::CallType;
#[test] #[test]
fn test_contract_address() { fn test_contract_address() {
@ -631,6 +625,7 @@ mod tests {
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
params.code = Some(code.clone()); params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100)); params.value = ActionValue::Transfer(U256::from(100));
params.call_type = CallType::Call;
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100));
@ -648,35 +643,37 @@ mod tests {
assert_eq!(gas_left, U256::from(44_752)); assert_eq!(gas_left, U256::from(44_752));
let expected_trace = vec![ Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 100.into(), value: 100.into(),
gas: 100000.into(), gas: 100000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(55_248), gas_used: U256::from(55_248),
output: vec![], output: vec![],
}), }),
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
action: trace::Action::Create(trace::Create { subtraces: 0,
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), action: trace::Action::Create(trace::Create {
value: 23.into(), from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
gas: 67979.into(), value: 23.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] gas: 67979.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), result: trace::Res::Create(trace::CreateResult {
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(), gas_used: U256::from(3224),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
}), code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
subs: vec![] }),
}]
}]; }];
assert_eq!(tracer.traces(), expected_trace); assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace { let expected_vm_trace = VMTrace {
@ -754,8 +751,9 @@ mod tests {
assert_eq!(gas_left, U256::from(96_776)); assert_eq!(gas_left, U256::from(96_776));
let expected_trace = vec![Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Create(trace::Create { action: trace::Action::Create(trace::Create {
from: params.sender, from: params.sender,
value: 100.into(), value: 100.into(),
@ -767,8 +765,8 @@ mod tests {
address: params.address, address: params.address,
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}), }),
subs: vec![]
}]; }];
assert_eq!(tracer.traces(), expected_trace); assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace { let expected_vm_trace = VMTrace {

View File

@ -21,6 +21,7 @@ use engine::*;
use executive::*; use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*; use substate::*;
use types::executed::CallType;
use trace::{Tracer, VMTracer}; use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
@ -148,6 +149,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
value: ActionValue::Transfer(*value), value: ActionValue::Transfer(*value),
code: Some(code.to_vec()), code: Some(code.to_vec()),
data: None, data: None,
call_type: CallType::None,
}; };
self.state.inc_nonce(&self.origin_info.address); self.state.inc_nonce(&self.origin_info.address);
@ -170,7 +172,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
output: &mut [u8] output: &mut [u8],
call_type: CallType
) -> MessageCallResult { ) -> MessageCallResult {
trace!(target: "externalities", "call"); trace!(target: "externalities", "call");
@ -184,6 +187,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
gas_price: self.origin_info.gas_price, gas_price: self.origin_info.gas_price,
code: self.state.code(code_address), code: self.state.code(code_address),
data: Some(data.to_vec()), data: Some(data.to_vec()),
call_type: call_type,
}; };
if let Some(value) = value { if let Some(value) = value {
@ -262,6 +266,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance); trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
self.state.transfer_balance(&address, refund_address, &balance); self.state.transfer_balance(&address, refund_address, &balance);
} }
self.tracer.trace_suicide(address, balance, refund_address.clone());
self.substate.suicides.insert(address); self.substate.suicides.insert(address);
} }
@ -300,6 +306,7 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
use super::*; use super::*;
use trace::{NoopTracer, NoopVMTracer}; use trace::{NoopTracer, NoopVMTracer};
use types::executed::CallType;
fn get_test_origin() -> OriginInfo { fn get_test_origin() -> OriginInfo {
OriginInfo { OriginInfo {
@ -418,7 +425,9 @@ mod tests {
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()), Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
&[], &[],
&Address::new(), &Address::new(),
&mut output); &mut output,
CallType::Call
);
} }
#[test] #[test]

View File

@ -22,6 +22,7 @@ use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use types::executed::CallType;
use tests::helpers::*; use tests::helpers::*;
use ethjson; use ethjson;
use trace::{Tracer, NoopTracer}; use trace::{Tracer, NoopTracer};
@ -109,13 +110,15 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
} }
fn call(&mut self, fn call(&mut self,
gas: &U256, gas: &U256,
_sender_address: &Address, _sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
_code_address: &Address, _code_address: &Address,
_output: &mut [u8]) -> MessageCallResult { _output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: data.to_vec(), data: data.to_vec(),
destination: Some(receive_address.clone()), destination: Some(receive_address.clone()),

View File

@ -15,7 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rayon::prelude::*; use rayon::prelude::*;
use std::sync::atomic::AtomicBool;
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use util::*; use util::*;
@ -87,15 +86,20 @@ impl Default for MinerOptions {
} }
} }
struct SealingWork {
queue: UsingQueue<ClosedBlock>,
enabled: bool,
}
/// Keeps track of transactions using priority queue and holds currently mined block. /// Keeps track of transactions using priority queue and holds currently mined block.
pub struct Miner { pub struct Miner {
// NOTE [ToDr] When locking always lock in this order! // NOTE [ToDr] When locking always lock in this order!
transaction_queue: Mutex<TransactionQueue>, transaction_queue: Mutex<TransactionQueue>,
sealing_work: Mutex<UsingQueue<ClosedBlock>>, sealing_work: Mutex<SealingWork>,
// for sealing... // for sealing...
options: MinerOptions, options: MinerOptions,
sealing_enabled: AtomicBool,
next_allowed_reseal: Mutex<Instant>, next_allowed_reseal: Mutex<Instant>,
sealing_block_last_request: Mutex<u64>, sealing_block_last_request: Mutex<u64>,
gas_range_target: RwLock<(U256, U256)>, gas_range_target: RwLock<(U256, U256)>,
@ -113,10 +117,9 @@ impl Miner {
Miner { Miner {
transaction_queue: Mutex::new(TransactionQueue::new()), transaction_queue: Mutex::new(TransactionQueue::new()),
options: Default::default(), options: Default::default(),
sealing_enabled: AtomicBool::new(false),
next_allowed_reseal: Mutex::new(Instant::now()), next_allowed_reseal: Mutex::new(Instant::now()),
sealing_block_last_request: Mutex::new(0), sealing_block_last_request: Mutex::new(0),
sealing_work: Mutex::new(UsingQueue::new(20)), sealing_work: Mutex::new(SealingWork{queue: UsingQueue::new(20), enabled: false}),
gas_range_target: RwLock::new((U256::zero(), U256::zero())), gas_range_target: RwLock::new((U256::zero(), U256::zero())),
author: RwLock::new(Address::default()), author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()), extra_data: RwLock::new(Vec::new()),
@ -131,10 +134,9 @@ impl Miner {
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None }; let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
Arc::new(Miner { Arc::new(Miner {
transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)), transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)),
sealing_enabled: AtomicBool::new(options.force_sealing || !options.new_work_notify.is_empty()),
next_allowed_reseal: Mutex::new(Instant::now()), next_allowed_reseal: Mutex::new(Instant::now()),
sealing_block_last_request: Mutex::new(0), sealing_block_last_request: Mutex::new(0),
sealing_work: Mutex::new(UsingQueue::new(options.work_queue_size)), sealing_work: Mutex::new(SealingWork{queue: UsingQueue::new(options.work_queue_size), enabled: options.force_sealing || !options.new_work_notify.is_empty()}),
gas_range_target: RwLock::new((U256::zero(), U256::zero())), gas_range_target: RwLock::new((U256::zero(), U256::zero())),
author: RwLock::new(Address::default()), author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()), extra_data: RwLock::new(Vec::new()),
@ -155,12 +157,12 @@ impl Miner {
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
pub fn pending_state(&self) -> Option<State> { pub fn pending_state(&self) -> Option<State> {
self.sealing_work.lock().unwrap().peek_last_ref().map(|b| b.block().fields().state.clone()) self.sealing_work.lock().unwrap().queue.peek_last_ref().map(|b| b.block().fields().state.clone())
} }
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
pub fn pending_block(&self) -> Option<Block> { pub fn pending_block(&self) -> Option<Block> {
self.sealing_work.lock().unwrap().peek_last_ref().map(|b| b.base().clone()) self.sealing_work.lock().unwrap().queue.peek_last_ref().map(|b| b.base().clone())
} }
/// Prepares new block for sealing including top transactions from queue. /// Prepares new block for sealing including top transactions from queue.
@ -172,7 +174,7 @@ impl Miner {
let (transactions, mut open_block, original_work_hash) = { let (transactions, mut open_block, original_work_hash) = {
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()}; let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
let mut sealing_work = self.sealing_work.lock().unwrap(); let mut sealing_work = self.sealing_work.lock().unwrap();
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash()); let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
let best_hash = chain.best_block_header().sha3(); let best_hash = chain.best_block_header().sha3();
/* /*
// check to see if last ClosedBlock in would_seals is actually same parent block. // check to see if last ClosedBlock in would_seals is actually same parent block.
@ -182,7 +184,7 @@ impl Miner {
// otherwise, leave everything alone. // otherwise, leave everything alone.
// otherwise, author a fresh block. // otherwise, author a fresh block.
*/ */
let open_block = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) { let open_block = match sealing_work.queue.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
Some(old_block) => { Some(old_block) => {
trace!(target: "miner", "Already have previous work; updating and returning"); trace!(target: "miner", "Already have previous work; updating and returning");
// add transactions to old_block // add transactions to old_block
@ -273,7 +275,7 @@ impl Miner {
let (work, is_new) = { let (work, is_new) = {
let mut sealing_work = self.sealing_work.lock().unwrap(); let mut sealing_work = self.sealing_work.lock().unwrap();
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash()); let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash()); trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash());
let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) { let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) {
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash()); trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
@ -281,16 +283,16 @@ impl Miner {
let number = block.block().fields().header.number(); let number = block.block().fields().header.number();
let difficulty = *block.block().fields().header.difficulty(); let difficulty = *block.block().fields().header.difficulty();
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h); let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
sealing_work.push(block); sealing_work.queue.push(block);
// If push notifications are enabled we assume all work items are used. // If push notifications are enabled we assume all work items are used.
if self.work_poster.is_some() && is_new { if self.work_poster.is_some() && is_new {
sealing_work.use_last_ref(); sealing_work.queue.use_last_ref();
} }
(Some((pow_hash, difficulty, number)), is_new) (Some((pow_hash, difficulty, number)), is_new)
} else { } else {
(None, false) (None, false)
}; };
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.queue.peek_last_ref().map(|b| b.block().fields().header.hash()));
(work, is_new) (work, is_new)
}; };
if is_new { if is_new {
@ -307,14 +309,22 @@ impl Miner {
/// Returns true if we had to prepare new pending block /// Returns true if we had to prepare new pending block
fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool { fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool {
trace!(target: "miner", "enable_and_prepare_sealing: entering"); trace!(target: "miner", "enable_and_prepare_sealing: entering");
let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some(); let prepare_new = {
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work); let mut sealing_work = self.sealing_work.lock().unwrap();
if !have_work { let have_work = sealing_work.queue.peek_last_ref().is_some();
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
if !have_work {
sealing_work.enabled = true;
true
} else {
false
}
};
if prepare_new {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. | // | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. | // | Make sure to release the locks before calling that method. |
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
self.prepare_sealing(chain); self.prepare_sealing(chain);
} }
let mut sealing_block_last_request = self.sealing_block_last_request.lock().unwrap(); let mut sealing_block_last_request = self.sealing_block_last_request.lock().unwrap();
@ -324,8 +334,8 @@ impl Miner {
*sealing_block_last_request = best_number; *sealing_block_last_request = best_number;
} }
// Return if // Return if we restarted
!have_work prepare_new
} }
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) -> fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
@ -364,13 +374,13 @@ impl MinerService for Miner {
MinerStatus { MinerStatus {
transactions_in_pending_queue: status.pending, transactions_in_pending_queue: status.pending,
transactions_in_future_queue: status.future, transactions_in_future_queue: status.future,
transactions_in_pending_block: sealing_work.peek_last_ref().map_or(0, |b| b.transactions().len()), transactions_in_pending_block: sealing_work.queue.peek_last_ref().map_or(0, |b| b.transactions().len()),
} }
} }
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() { match sealing_work.queue.peek_last_ref() {
Some(work) => { Some(work) => {
let block = work.block(); let block = work.block();
@ -417,7 +427,7 @@ impl MinerService for Miner {
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 { fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else( sealing_work.queue.peek_last_ref().map_or_else(
|| chain.latest_balance(address), || chain.latest_balance(address),
|b| b.block().fields().state.balance(address) |b| b.block().fields().state.balance(address)
) )
@ -425,7 +435,7 @@ impl MinerService for Miner {
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 { fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else( sealing_work.queue.peek_last_ref().map_or_else(
|| chain.latest_storage_at(address, position), || chain.latest_storage_at(address, position),
|b| b.block().fields().state.storage_at(address, position) |b| b.block().fields().state.storage_at(address, position)
) )
@ -433,12 +443,12 @@ impl MinerService for Miner {
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 { fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address)) sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
} }
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> { fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address)) sealing_work.queue.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
} }
fn set_author(&self, author: Address) { fn set_author(&self, author: Address) {
@ -582,8 +592,8 @@ impl MinerService for Miner {
let queue = self.transaction_queue.lock().unwrap(); let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap(); let sw = self.sealing_work.lock().unwrap();
// TODO: should only use the sealing_work when it's current (it could be an old block) // TODO: should only use the sealing_work when it's current (it could be an old block)
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { let sealing_set = match sw.enabled {
true => sw.peek_last_ref(), true => sw.queue.peek_last_ref(),
false => None, false => None,
}; };
match (&self.options.pending_set, sealing_set) { match (&self.options.pending_set, sealing_set) {
@ -595,8 +605,8 @@ impl MinerService for Miner {
fn pending_transactions_hashes(&self) -> Vec<H256> { fn pending_transactions_hashes(&self) -> Vec<H256> {
let queue = self.transaction_queue.lock().unwrap(); let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap(); let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { let sealing_set = match sw.enabled {
true => sw.peek_last_ref(), true => sw.queue.peek_last_ref(),
false => None, false => None,
}; };
match (&self.options.pending_set, sealing_set) { match (&self.options.pending_set, sealing_set) {
@ -608,8 +618,8 @@ impl MinerService for Miner {
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> { fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap(); let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap(); let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { let sealing_set = match sw.enabled {
true => sw.peek_last_ref(), true => sw.queue.peek_last_ref(),
false => None, false => None,
}; };
match (&self.options.pending_set, sealing_set) { match (&self.options.pending_set, sealing_set) {
@ -619,7 +629,8 @@ impl MinerService for Miner {
} }
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> { fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) { let sealing_work = self.sealing_work.lock().unwrap();
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
(true, Some(pending)) => { (true, Some(pending)) => {
let hashes = pending.transactions() let hashes = pending.transactions()
.iter() .iter()
@ -638,27 +649,43 @@ impl MinerService for Miner {
} }
fn update_sealing(&self, chain: &MiningBlockChainClient) { fn update_sealing(&self, chain: &MiningBlockChainClient) {
if self.sealing_enabled.load(atomic::Ordering::Relaxed) { trace!(target: "miner", "update_sealing");
let current_no = chain.chain_info().best_block_number; let requires_reseal = {
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions(); let mut sealing_work = self.sealing_work.lock().unwrap();
let last_request = *self.sealing_block_last_request.lock().unwrap(); if sealing_work.enabled {
let should_disable_sealing = !self.forced_sealing() trace!(target: "miner", "update_sealing: sealing enabled");
&& !has_local_transactions let current_no = chain.chain_info().best_block_number;
&& current_no > last_request let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS; let last_request = *self.sealing_block_last_request.lock().unwrap();
let should_disable_sealing = !self.forced_sealing()
&& !has_local_transactions
&& current_no > last_request
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
if should_disable_sealing { trace!(target: "miner", "update_sealing: should_disable_sealing={}; current_no={}, last_request={}", should_disable_sealing, current_no, last_request);
trace!(target: "miner", "Miner sleeping (current {}, last {})", current_no, last_request);
self.sealing_enabled.store(false, atomic::Ordering::Relaxed); if should_disable_sealing {
self.sealing_work.lock().unwrap().reset(); trace!(target: "miner", "Miner sleeping (current {}, last {})", current_no, last_request);
sealing_work.enabled = false;
sealing_work.queue.reset();
false
} else {
// sealing enabled and we don't want to sleep.
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period;
true
}
} else { } else {
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period; // sealing is disabled.
// -------------------------------------------------------------------------- false
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.prepare_sealing(chain);
} }
};
if requires_reseal {
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.prepare_sealing(chain);
} }
} }
@ -667,13 +694,13 @@ impl MinerService for Miner {
self.enable_and_prepare_sealing(chain); self.enable_and_prepare_sealing(chain);
trace!(target: "miner", "map_sealing_work: sealing prepared"); trace!(target: "miner", "map_sealing_work: sealing prepared");
let mut sealing_work = self.sealing_work.lock().unwrap(); let mut sealing_work = self.sealing_work.lock().unwrap();
let ret = sealing_work.use_last_ref(); let ret = sealing_work.queue.use_last_ref();
trace!(target: "miner", "map_sealing_work: leaving use_last_ref={:?}", ret.as_ref().map(|b| b.block().fields().header.hash())); trace!(target: "miner", "map_sealing_work: leaving use_last_ref={:?}", ret.as_ref().map(|b| b.block().fields().header.hash()));
ret.map(f) ret.map(f)
} }
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
let result = if let Some(b) = self.sealing_work.lock().unwrap().get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) { let result = if let Some(b) = self.sealing_work.lock().unwrap().queue.get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) {
b.lock().try_seal(self.engine(), seal).or_else(|_| { b.lock().try_seal(self.engine(), seal).or_else(|_| {
warn!(target: "miner", "Mined solution rejected: Invalid."); warn!(target: "miner", "Mined solution rejected: Invalid.");
Err(Error::PowInvalid) Err(Error::PowInvalid)
@ -692,6 +719,8 @@ impl MinerService for Miner {
} }
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
trace!(target: "miner", "chain_new_blocks");
fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> { fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
let block = chain let block = chain
.block(BlockID::Hash(*hash)) .block(BlockID::Hash(*hash))
@ -746,11 +775,13 @@ impl MinerService for Miner {
}); });
} }
// -------------------------------------------------------------------------- if enacted.len() > 0 {
// | NOTE Code below requires transaction_queue and sealing_work locks. | // --------------------------------------------------------------------------
// | Make sure to release the locks before calling that method. | // | NOTE Code below requires transaction_queue and sealing_work locks. |
// -------------------------------------------------------------------------- // | Make sure to release the locks before calling that method. |
self.update_sealing(chain); // --------------------------------------------------------------------------
self.update_sealing(chain);
}
} }
} }

View File

@ -28,8 +28,8 @@ pub struct PodAccount {
pub balance: U256, pub balance: U256,
/// The nonce of the account. /// The nonce of the account.
pub nonce: U256, pub nonce: U256,
/// The code of the account. /// The code of the account or `None` in the special case that it is unknown.
pub code: Bytes, pub code: Option<Bytes>,
/// The storage of the account. /// The storage of the account.
pub storage: BTreeMap<H256, H256>, pub storage: BTreeMap<H256, H256>,
} }
@ -38,7 +38,7 @@ impl PodAccount {
/// Construct new object. /// Construct new object.
#[cfg(test)] #[cfg(test)]
pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount { pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount {
PodAccount { balance: balance, nonce: nonce, code: code, storage: storage } PodAccount { balance: balance, nonce: nonce, code: Some(code), storage: storage }
} }
/// Convert Account to a PodAccount. /// Convert Account to a PodAccount.
@ -48,7 +48,7 @@ impl PodAccount {
balance: *acc.balance(), balance: *acc.balance(),
nonce: *acc.nonce(), nonce: *acc.nonce(),
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}), storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
code: acc.code().unwrap().to_vec(), code: acc.code().map(|x| x.to_vec()),
} }
} }
@ -58,14 +58,15 @@ impl PodAccount {
stream.append(&self.nonce); stream.append(&self.nonce);
stream.append(&self.balance); stream.append(&self.balance);
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect())); stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
stream.append(&self.code.sha3()); stream.append(&self.code.as_ref().unwrap_or(&vec![]).sha3());
stream.out() stream.out()
} }
/// Place additional data into given hash DB. /// Place additional data into given hash DB.
pub fn insert_additional(&self, db: &mut AccountDBMut) { pub fn insert_additional(&self, db: &mut AccountDBMut) {
if !self.code.is_empty() { match self.code {
db.insert(&self.code); Some(ref c) if !c.is_empty() => { db.insert(c); }
_ => {}
} }
let mut r = H256::new(); let mut r = H256::new();
let mut t = SecTrieDBMut::new(db, &mut r); let mut t = SecTrieDBMut::new(db, &mut r);
@ -80,7 +81,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
PodAccount { PodAccount {
balance: a.balance.into(), balance: a.balance.into(),
nonce: a.nonce.into(), nonce: a.nonce.into(),
code: a.code.into(), code: Some(a.code.into()),
storage: a.storage.into_iter().map(|(key, value)| { storage: a.storage.into_iter().map(|(key, value)| {
let key: U256 = key.into(); let key: U256 = key.into();
let value: U256 = value.into(); let value: U256 = value.into();
@ -95,7 +96,7 @@ impl From<ethjson::spec::Account> for PodAccount {
PodAccount { PodAccount {
balance: a.balance.map_or_else(U256::zero, Into::into), balance: a.balance.map_or_else(U256::zero, Into::into),
nonce: a.nonce.map_or_else(U256::zero, Into::into), nonce: a.nonce.map_or_else(U256::zero, Into::into),
code: vec![], code: Some(vec![]),
storage: BTreeMap::new() storage: BTreeMap::new()
} }
} }
@ -103,7 +104,13 @@ impl From<ethjson::spec::Account> for PodAccount {
impl fmt::Display for PodAccount { impl fmt::Display for PodAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len()) write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)",
self.balance,
self.nonce,
self.code.as_ref().map_or(0, |c| c.len()),
self.code.as_ref().map_or_else(H256::new, |c| c.sha3()),
self.storage.len()
)
} }
} }
@ -114,13 +121,13 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
(None, Some(x)) => Some(AccountDiff { (None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance), balance: Diff::Born(x.balance),
nonce: Diff::Born(x.nonce), nonce: Diff::Born(x.nonce),
code: Diff::Born(x.code.clone()), code: Diff::Born(x.code.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(), storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}), }),
(Some(x), None) => Some(AccountDiff { (Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance), balance: Diff::Died(x.balance),
nonce: Diff::Died(x.nonce), nonce: Diff::Died(x.nonce),
code: Diff::Died(x.code.clone()), code: Diff::Died(x.code.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(), storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}), }),
(Some(pre), Some(post)) => { (Some(pre), Some(post)) => {
@ -130,7 +137,10 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
let r = AccountDiff { let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance), balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce), nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()), code: match (pre.code.clone(), post.code.clone()) {
(Some(pre_code), Some(post_code)) => Diff::new(pre_code, post_code),
_ => Diff::Same,
},
storage: storage.into_iter().map(|k| storage: storage.into_iter().map(|k|
(k.clone(), Diff::new( (k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new), pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
@ -156,7 +166,7 @@ mod test {
#[test] #[test]
fn existence() { fn existence() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
assert_eq!(diff_pod(Some(&a), Some(&a)), None); assert_eq!(diff_pod(Some(&a), Some(&a)), None);
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(69.into()), balance: Diff::Born(69.into()),
@ -168,8 +178,8 @@ mod test {
#[test] #[test]
fn basic() { fn basic() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]}; let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]};
assert_eq!(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()), balance: Diff::Changed(69.into(), 42.into()),
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
@ -180,8 +190,8 @@ mod test {
#[test] #[test]
fn code() { fn code() {
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]}; let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]}; let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]};
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
@ -195,13 +205,13 @@ mod test {
let a = PodAccount { let a = PodAccount {
balance: 0.into(), balance: 0.into(),
nonce: 0.into(), nonce: 0.into(),
code: vec![], code: Some(vec![]),
storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
}; };
let b = PodAccount { let b = PodAccount {
balance: 0.into(), balance: 0.into(),
nonce: 0.into(), nonce: 0.into(),
code: vec![], code: Some(vec![]),
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
}; };
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {

View File

@ -38,6 +38,8 @@ pub struct CommonParams {
pub network_id: U256, pub network_id: U256,
/// Minimum gas limit. /// Minimum gas limit.
pub min_gas_limit: U256, pub min_gas_limit: U256,
/// Fork block to check.
pub fork_block: Option<(BlockNumber, H256)>,
} }
impl From<ethjson::spec::Params> for CommonParams { impl From<ethjson::spec::Params> for CommonParams {
@ -47,6 +49,7 @@ impl From<ethjson::spec::Params> for CommonParams {
maximum_extra_data_size: p.maximum_extra_data_size.into(), maximum_extra_data_size: p.maximum_extra_data_size.into(),
network_id: p.network_id.into(), network_id: p.network_id.into(),
min_gas_limit: p.min_gas_limit.into(), min_gas_limit: p.min_gas_limit.into(),
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
} }
} }
} }
@ -148,6 +151,9 @@ impl Spec {
/// Get the configured Network ID. /// Get the configured Network ID.
pub fn network_id(&self) -> U256 { self.params.network_id } pub fn network_id(&self) -> U256 { self.params.network_id }
/// Get the configured network fork block.
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params.fork_block }
/// Get the header of the genesis block. /// Get the header of the genesis block.
pub fn genesis_header(&self) -> Header { pub fn genesis_header(&self) -> Header {
Header { Header {

View File

@ -19,7 +19,7 @@ use engine::Engine;
use executive::{Executive, TransactOptions}; use executive::{Executive, TransactOptions};
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use account_db::*; use account_db::*;
use trace::Trace; use trace::FlatTrace;
use pod_account::*; use pod_account::*;
use pod_state::{self, PodState}; use pod_state::{self, PodState};
use types::state_diff::StateDiff; use types::state_diff::StateDiff;
@ -29,7 +29,7 @@ pub struct ApplyOutcome {
/// The receipt for the applied transaction. /// The receipt for the applied transaction.
pub receipt: Receipt, pub receipt: Receipt,
/// The trace for the applied transaction, if None if tracing is disabled. /// The trace for the applied transaction, if None if tracing is disabled.
pub trace: Option<Trace>, pub trace: Vec<FlatTrace>,
} }
/// Result type for the execution ("application") of a transaction. /// Result type for the execution ("application") of a transaction.
@ -389,7 +389,8 @@ use spec::*;
use transaction::*; use transaction::*;
use util::log::init_log; use util::log::init_log;
use trace::trace; use trace::trace;
use trace::trace::{Trace}; use trace::FlatTrace;
use types::executed::CallType;
#[test] #[test]
fn should_apply_create_transaction() { fn should_apply_create_transaction() {
@ -414,8 +415,9 @@ fn should_apply_create_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Create(trace::Create { action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(), value: 100.into(),
@ -427,8 +429,7 @@ fn should_apply_create_transaction() {
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}), }),
subs: vec![] }];
});
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -475,8 +476,8 @@ fn should_trace_failed_create_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Create(trace::Create { action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(), value: 100.into(),
@ -484,8 +485,8 @@ fn should_trace_failed_create_transaction() {
init: vec![91, 96, 0, 86], init: vec![91, 96, 0, 86],
}), }),
result: trace::Res::FailedCreate, result: trace::Res::FailedCreate,
subs: vec![] subtraces: 0
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -514,21 +515,22 @@ fn should_trace_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3), gas_used: U256::from(3),
output: vec![] output: vec![]
}), }),
subs: vec![] subtraces: 0,
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -556,21 +558,22 @@ fn should_trace_basic_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(0), gas_used: U256::from(0),
output: vec![] output: vec![]
}), }),
subs: vec![] subtraces: 0,
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -598,21 +601,24 @@ fn should_trace_call_transaction_to_builtin() {
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
assert_eq!(result.trace, Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: "0000000000000000000000000000000000000001".into(), to: "0000000000000000000000000000000000000001".into(),
value: 0.into(), value: 0.into(),
gas: 79_000.into(), gas: 79_000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3000), gas_used: U256::from(3000),
output: vec![] output: vec![]
}), }),
subs: vec![] subtraces: 0,
})); }];
assert_eq!(result.trace, expected_trace);
} }
#[test] #[test]
@ -639,21 +645,23 @@ fn should_not_trace_subcall_transaction_to_builtin() {
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(28_061), gas_used: U256::from(28_061),
output: vec![] output: vec![]
}), }),
subs: vec![] subtraces: 0,
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -682,21 +690,38 @@ fn should_not_trace_callcode() {
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(64), gas_used: 64.into(),
output: vec![] output: vec![]
}), }),
subs: 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); assert_eq!(result.trace, expected_trace);
} }
@ -728,21 +753,38 @@ fn should_not_trace_delegatecall() {
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(61), gas_used: U256::from(61),
output: vec![] output: vec![]
}), }),
subs: 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); assert_eq!(result.trace, expected_trace);
} }
@ -770,20 +812,19 @@ fn should_trace_failed_call_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall,
subs: vec![] subtraces: 0,
}); }];
println!("trace: {:?}", result.trace);
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -813,35 +854,38 @@ fn should_trace_call_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0, let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69), gas_used: U256::from(69),
output: vec![] output: vec![]
}), }),
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
action: trace::Action::Call(trace::Call { subtraces: 0,
from: 0xa.into(), action: trace::Action::Call(trace::Call {
to: 0xb.into(), from: 0xa.into(),
value: 0.into(), to: 0xb.into(),
gas: 78934.into(), value: 0.into(),
input: vec![], gas: 78934.into(),
}), input: vec![],
result: trace::Res::Call(trace::CallResult { call_type: CallType::Call,
gas_used: U256::from(3), }),
output: vec![] result: trace::Res::Call(trace::CallResult {
}), gas_used: U256::from(3),
subs: vec![] output: vec![]
}] }),
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -870,32 +914,34 @@ fn should_trace_call_with_basic_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761), gas_used: U256::from(31761),
output: vec![] output: vec![]
}), }),
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
action: trace::Action::Call(trace::Call { subtraces: 0,
from: 0xa.into(), action: trace::Action::Call(trace::Call {
to: 0xb.into(), from: 0xa.into(),
value: 69.into(), to: 0xb.into(),
gas: 2300.into(), value: 69.into(),
input: vec![], gas: 2300.into(),
}), input: vec![],
result: trace::Res::Call(trace::CallResult::default()), call_type: CallType::Call,
subs: vec![] }),
}] result: trace::Res::Call(trace::CallResult::default()),
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -924,21 +970,22 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761), gas_used: U256::from(31761),
output: vec![] output: vec![]
}), }),
subs: vec![] }];
});
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -968,32 +1015,34 @@ fn should_trace_failed_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000), gas_used: U256::from(79_000),
output: vec![] output: vec![]
}), }),
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
action: trace::Action::Call(trace::Call { subtraces: 0,
from: 0xa.into(), action: trace::Action::Call(trace::Call {
to: 0xb.into(), from: 0xa.into(),
value: 0.into(), to: 0xb.into(),
gas: 78934.into(), value: 0.into(),
input: vec![], gas: 78934.into(),
}), input: vec![],
result: trace::Res::FailedCall, call_type: CallType::Call,
subs: vec![] }),
}] result: trace::Res::FailedCall,
}); }];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -1024,49 +1073,52 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace { let expected_trace = vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(135), gas_used: U256::from(135),
output: vec![] output: vec![]
}), }),
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
action: trace::Action::Call(trace::Call { subtraces: 1,
from: 0xa.into(), action: trace::Action::Call(trace::Call {
to: 0xb.into(), from: 0xa.into(),
value: 0.into(), to: 0xb.into(),
gas: 78934.into(), value: 0.into(),
input: vec![], gas: 78934.into(),
}), input: vec![],
result: trace::Res::Call(trace::CallResult { call_type: CallType::Call,
gas_used: U256::from(69), }),
output: vec![] result: trace::Res::Call(trace::CallResult {
}), gas_used: U256::from(69),
subs: vec![Trace { output: vec![]
depth: 2, }),
action: trace::Action::Call(trace::Call { }, FlatTrace {
from: 0xb.into(), trace_address: vec![0, 0].into_iter().collect(),
to: 0xc.into(), subtraces: 0,
value: 0.into(), action: trace::Action::Call(trace::Call {
gas: 78868.into(), from: 0xb.into(),
input: vec![], to: 0xc.into(),
}), value: 0.into(),
result: trace::Res::Call(trace::CallResult { gas: 78868.into(),
gas_used: U256::from(3), input: vec![],
output: vec![] call_type: CallType::Call,
}), }),
subs: vec![] result: trace::Res::Call(trace::CallResult {
}] gas_used: U256::from(3),
}] output: vec![]
}); }),
}];
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
@ -1097,46 +1149,104 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0, let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(), to: 0xa.into(),
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000), gas_used: U256::from(79_000),
output: vec![] output: vec![]
}), })
subs: vec![Trace { }, FlatTrace {
depth: 1, trace_address: vec![0].into_iter().collect(),
subtraces: 1,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
from: 0xa.into(), from: 0xa.into(),
to: 0xb.into(), to: 0xb.into(),
value: 0.into(), value: 0.into(),
gas: 78934.into(), gas: 78934.into(),
input: vec![], input: vec![],
}), call_type: CallType::Call,
result: trace::Res::FailedCall, }),
subs: vec![Trace { result: trace::Res::FailedCall,
depth: 2, }, FlatTrace {
action: trace::Action::Call(trace::Call { trace_address: vec![0, 0].into_iter().collect(),
from: 0xb.into(), subtraces: 0,
to: 0xc.into(), action: trace::Action::Call(trace::Call {
value: 0.into(), from: 0xb.into(),
gas: 78868.into(), to: 0xc.into(),
input: vec![], value: 0.into(),
}), gas: 78868.into(),
result: trace::Res::Call(trace::CallResult { call_type: CallType::Call,
gas_used: U256::from(3), input: vec![],
output: vec![] }),
}), result: trace::Res::Call(trace::CallResult {
subs: vec![] 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); assert_eq!(result.trace, expected_trace);
} }

View File

@ -1,58 +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 <http://www.gnu.org/licenses/>.
use util::rlp::*;
use basic_types::LogBloom;
use super::Trace;
/// Traces created by transactions from the same block.
#[derive(Clone)]
pub struct BlockTraces(Vec<Trace>);
impl From<Vec<Trace>> for BlockTraces {
fn from(traces: Vec<Trace>) -> Self {
BlockTraces(traces)
}
}
impl Into<Vec<Trace>> for BlockTraces {
fn into(self) -> Vec<Trace> {
self.0
}
}
impl Decodable for BlockTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let traces = try!(Decodable::decode(decoder));
let block_traces = BlockTraces(traces);
Ok(block_traces)
}
}
impl Encodable for BlockTraces {
fn rlp_append(&self, s: &mut RlpStream) {
Encodable::rlp_append(&self.0, s)
}
}
impl BlockTraces {
/// Returns bloom of all traces in given block.
pub fn bloom(&self) -> LogBloom {
self.0.iter()
.fold(LogBloom::default(), |acc, trace| acc | trace.bloom())
}
}

View File

@ -23,7 +23,7 @@ use bloomchain::{Number, Config as BloomConfig};
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup}; use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
use util::{H256, H264, Database, DatabaseConfig, DBTransaction}; use util::{H256, H264, Database, DatabaseConfig, DBTransaction};
use header::BlockNumber; use header::BlockNumber;
use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest, use trace::{LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
DatabaseExtras, Error}; DatabaseExtras, Error};
use db::{Key, Writable, Readable, CacheUpdatePolicy}; use db::{Key, Writable, Readable, CacheUpdatePolicy};
use blooms; use blooms;
@ -40,7 +40,7 @@ enum TraceDBIndex {
BloomGroups = 1, BloomGroups = 1,
} }
impl Key<BlockTraces> for H256 { impl Key<FlatBlockTraces> for H256 {
type Target = H264; type Target = H264;
fn key(&self) -> H264 { fn key(&self) -> H264 {
@ -91,7 +91,7 @@ impl Key<blooms::BloomGroup> for TraceGroupPosition {
/// Trace database. /// Trace database.
pub struct TraceDB<T> where T: DatabaseExtras { pub struct TraceDB<T> where T: DatabaseExtras {
// cache // cache
traces: RwLock<HashMap<H256, BlockTraces>>, traces: RwLock<HashMap<H256, FlatBlockTraces>>,
blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>, blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>,
// db // db
tracesdb: Database, tracesdb: Database,
@ -153,15 +153,13 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
} }
/// Returns traces for block with hash. /// Returns traces for block with hash.
fn traces(&self, block_hash: &H256) -> Option<BlockTraces> { fn traces(&self, block_hash: &H256) -> Option<FlatBlockTraces> {
self.tracesdb.read_with_cache(&self.traces, block_hash) self.tracesdb.read_with_cache(&self.traces, block_hash)
} }
/// Returns vector of transaction traces for given block. /// Returns vector of transaction traces for given block.
fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> { fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> {
self.traces(block_hash) self.traces(block_hash).map(Into::into)
.map(FlatBlockTraces::from)
.map(Into::into)
} }
fn matching_block_traces( fn matching_block_traces(
@ -199,7 +197,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
action: trace.action, action: trace.action,
result: trace.result, result: trace.result,
subtraces: trace.subtraces, subtraces: trace.subtraces,
trace_address: trace.trace_address, trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_number, transaction_number: tx_number,
transaction_hash: tx_hash.clone(), transaction_hash: tx_hash.clone(),
block_number: block_number, block_number: block_number,
@ -265,12 +263,13 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
} }
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> { fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
let trace_position_deq = trace_position.into_iter().collect();
self.extras.block_hash(block_number) self.extras.block_hash(block_number)
.and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|block_hash| self.transactions_traces(&block_hash)
.and_then(|traces| traces.into_iter().nth(tx_position)) .and_then(|traces| traces.into_iter().nth(tx_position))
.map(Into::<Vec<FlatTrace>>::into) .map(Into::<Vec<FlatTrace>>::into)
// this may and should be optimized // this may and should be optimized
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position)) .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq))
.map(|trace| { .map(|trace| {
let tx_hash = self.extras.transaction_hash(block_number, tx_position) let tx_hash = self.extras.transaction_hash(block_number, tx_position)
.expect("Expected to find transaction hash. Database is probably corrupted"); .expect("Expected to find transaction hash. Database is probably corrupted");
@ -279,7 +278,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action, action: trace.action,
result: trace.result, result: trace.result,
subtraces: trace.subtraces, subtraces: trace.subtraces,
trace_address: trace.trace_address, trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position, transaction_number: tx_position,
transaction_hash: tx_hash, transaction_hash: tx_hash,
block_number: block_number, block_number: block_number,
@ -303,7 +302,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action, action: trace.action,
result: trace.result, result: trace.result,
subtraces: trace.subtraces, subtraces: trace.subtraces,
trace_address: trace.trace_address, trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position, transaction_number: tx_position,
transaction_hash: tx_hash.clone(), transaction_hash: tx_hash.clone(),
block_number: block_number, block_number: block_number,
@ -330,7 +329,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
action: trace.action, action: trace.action,
result: trace.result, result: trace.result,
subtraces: trace.subtraces, subtraces: trace.subtraces,
trace_address: trace.trace_address, trace_address: trace.trace_address.into_iter().collect(),
transaction_number: tx_position, transaction_number: tx_position,
transaction_hash: tx_hash.clone(), transaction_hash: tx_hash.clone(),
block_number: block_number, block_number: block_number,
@ -353,8 +352,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
.expect("Expected to find block hash. Extras db is probably corrupted"); .expect("Expected to find block hash. Extras db is probably corrupted");
let traces = self.traces(&hash) let traces = self.traces(&hash)
.expect("Expected to find a trace. Db is probably corrupted."); .expect("Expected to find a trace. Db is probably corrupted.");
let flat_block = FlatBlockTraces::from(traces); self.matching_block_traces(filter, traces, hash, number)
self.matching_block_traces(filter, flat_block, hash, number)
}) })
.collect() .collect()
} }
@ -368,8 +366,10 @@ mod tests {
use devtools::RandomTempPath; use devtools::RandomTempPath;
use header::BlockNumber; use header::BlockNumber;
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::{Filter, LocalizedTrace, AddressesFilter};
use trace::trace::{Call, Action, Res}; use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
use types::executed::CallType;
struct NoopExtras; struct NoopExtras;
@ -487,18 +487,19 @@ mod tests {
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest { fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
ImportRequest { ImportRequest {
traces: BlockTraces::from(vec![Trace { traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace {
depth: 0, trace_address: Default::default(),
subtraces: 0,
action: Action::Call(Call { action: Action::Call(Call {
from: Address::from(1), from: 1.into(),
to: Address::from(2), to: 2.into(),
value: U256::from(3), value: 3.into(),
gas: U256::from(4), gas: 4.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
subs: vec![], }])]),
}]),
block_hash: block_hash.clone(), block_hash: block_hash.clone(),
block_number: block_number, block_number: block_number,
enacted: vec![block_hash], enacted: vec![block_hash],
@ -514,6 +515,7 @@ mod tests {
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
trace_address: vec![], trace_address: vec![],

View File

@ -18,13 +18,53 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer}; use trace::{Tracer, VMTracer, FlatTrace};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
pub struct ExecutiveTracer { pub struct ExecutiveTracer {
traces: Vec<Trace>, traces: Vec<FlatTrace>,
}
fn top_level_subtraces(traces: &[FlatTrace]) -> usize {
traces.iter().filter(|t| t.trace_address.is_empty()).count()
}
fn update_trace_address(traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
// input traces are expected to be ordered like
// []
// [0]
// [0, 0]
// [0, 1]
// []
// [0]
//
// so they can be transformed to
//
// [0]
// [0, 0]
// [0, 0, 0]
// [0, 0, 1]
// [1]
// [1, 0]
let mut top_subtrace_index = 0;
let mut subtrace_subtraces_left = 0;
traces.into_iter().map(|mut trace| {
let is_top_subtrace = trace.trace_address.is_empty();
trace.trace_address.push_front(top_subtrace_index);
if is_top_subtrace {
subtrace_subtraces_left = trace.subtraces;
} else {
subtrace_subtraces_left -= 1;
}
if subtrace_subtraces_left == 0 {
top_subtrace_index += 1;
}
trace
}).collect()
} }
impl Tracer for ExecutiveTracer { impl Tracer for ExecutiveTracer {
@ -40,59 +80,67 @@ impl Tracer for ExecutiveTracer {
Some(vec![]) Some(vec![])
} }
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) { fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, subs: Vec<FlatTrace>) {
// don't trace if it's DELEGATECALL or CALLCODE. let trace = FlatTrace {
if delegate_call { trace_address: Default::default(),
return; subtraces: top_level_subtraces(&subs),
}
let trace = Trace {
depth: depth,
subs: subs,
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: gas_used, gas_used: gas_used,
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed") output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
}) }),
}; };
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
} }
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, depth: usize, subs: Vec<Trace>) { fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, subs: Vec<FlatTrace>) {
let trace = Trace { let trace = FlatTrace {
depth: depth, subtraces: top_level_subtraces(&subs),
subs: subs,
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::Create(CreateResult { result: Res::Create(CreateResult {
gas_used: gas_used, gas_used: gas_used,
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"), code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
address: address address: address
}) }),
trace_address: Default::default(),
}; };
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
} }
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool) { fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
// don't trace if it's DELEGATECALL or CALLCODE. let trace = FlatTrace {
if delegate_call { trace_address: Default::default(),
return; subtraces: top_level_subtraces(&subs),
}
let trace = Trace {
depth: depth,
subs: subs,
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::FailedCall, result: Res::FailedCall,
}; };
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
} }
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>) { fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
let trace = Trace { let trace = FlatTrace {
depth: depth, subtraces: top_level_subtraces(&subs),
subs: subs,
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::FailedCreate, result: Res::FailedCreate,
trace_address: Default::default(),
};
self.traces.push(trace);
self.traces.extend(update_trace_address(subs));
}
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
let trace = FlatTrace {
subtraces: 0,
action: Action::Suicide(Suicide {
address: address,
refund_address: refund_address,
balance: balance,
}),
result: Res::None,
trace_address: Default::default(),
}; };
self.traces.push(trace); self.traces.push(trace);
} }
@ -101,7 +149,7 @@ impl Tracer for ExecutiveTracer {
ExecutiveTracer::default() ExecutiveTracer::default()
} }
fn traces(self) -> Vec<Trace> { fn traces(self) -> Vec<FlatTrace> {
self.traces self.traces
} }
} }

View File

@ -1,183 +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 <http://www.gnu.org/licenses/>.
//! Flat trace module
use trace::BlockTraces;
use super::trace::{Trace, Action, Res};
/// Trace localized in vector of traces produced by a single transaction.
///
/// Parent and children indexes refer to positions in this vector.
pub struct FlatTrace {
/// Type of action performed by a transaction.
pub action: Action,
/// Result of this action.
pub result: Res,
/// Number of subtraces.
pub subtraces: usize,
/// Exact location of trace.
///
/// [index in root, index in first CALL, index in second CALL, ...]
pub trace_address: Vec<usize>,
}
/// Represents all traces produced by a single transaction.
pub struct FlatTransactionTraces(Vec<FlatTrace>);
impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
fn into(self) -> Vec<FlatTrace> {
self.0
}
}
/// Represents all traces produced by transactions in a single block.
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
impl From<BlockTraces> for FlatBlockTraces {
fn from(block_traces: BlockTraces) -> Self {
let traces: Vec<Trace> = block_traces.into();
let ordered = traces.into_iter()
.map(|trace| FlatBlockTraces::flatten(vec![], trace))
.map(FlatTransactionTraces)
.collect();
FlatBlockTraces(ordered)
}
}
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
fn into(self) -> Vec<FlatTransactionTraces> {
self.0
}
}
impl FlatBlockTraces {
/// Helper function flattening nested tree structure to vector of ordered traces.
fn flatten(address: Vec<usize>, trace: Trace) -> Vec<FlatTrace> {
let subtraces = trace.subs.len();
let all_subs = trace.subs
.into_iter()
.enumerate()
.flat_map(|(index, subtrace)| {
let mut subtrace_address = address.clone();
subtrace_address.push(index);
FlatBlockTraces::flatten(subtrace_address, subtrace)
})
.collect::<Vec<_>>();
let ordered = FlatTrace {
action: trace.action,
result: trace.result,
subtraces: subtraces,
trace_address: address,
};
let mut result = vec![ordered];
result.extend(all_subs);
result
}
}
#[cfg(test)]
mod tests {
use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace};
use util::{U256, Address};
use trace::trace::{Action, Res, CallResult, Call, Create, Trace};
use trace::BlockTraces;
#[test]
fn test_block_from() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5]
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
],
result: Res::FailedCreate
},
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![
],
result: Res::FailedCreate
}
],
result: Res::FailedCreate
},
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::FailedCreate,
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let block_traces = FlatBlockTraces::from(BlockTraces::from(vec![trace]));
let transaction_traces: Vec<FlatTransactionTraces> = block_traces.into();
assert_eq!(transaction_traces.len(), 1);
let ordered_traces: Vec<FlatTrace> = transaction_traces.into_iter().nth(0).unwrap().into();
assert_eq!(ordered_traces.len(), 5);
assert_eq!(ordered_traces[0].trace_address, vec![]);
assert_eq!(ordered_traces[0].subtraces, 2);
assert_eq!(ordered_traces[1].trace_address, vec![0]);
assert_eq!(ordered_traces[1].subtraces, 2);
assert_eq!(ordered_traces[2].trace_address, vec![0, 0]);
assert_eq!(ordered_traces[2].subtraces, 0);
assert_eq!(ordered_traces[3].trace_address, vec![0, 1]);
assert_eq!(ordered_traces[3].subtraces, 0);
assert_eq!(ordered_traces[4].trace_address, vec![1]);
assert_eq!(ordered_traces[4].subtraces, 0);
}
}

View File

@ -17,12 +17,12 @@
//! Traces import request. //! Traces import request.
use util::H256; use util::H256;
use header::BlockNumber; use header::BlockNumber;
use trace::BlockTraces; use trace::FlatBlockTraces;
/// Traces import request. /// Traces import request.
pub struct ImportRequest { pub struct ImportRequest {
/// Traces to import. /// Traces to import.
pub traces: BlockTraces, pub traces: FlatBlockTraces,
/// Hash of traces block. /// Hash of traces block.
pub block_hash: H256, pub block_hash: H256,
/// Number of traces block. /// Number of traces block.

View File

@ -16,22 +16,20 @@
//! Tracing //! Tracing
mod block;
mod bloom; mod bloom;
mod config; mod config;
mod db; mod db;
mod error; mod error;
mod executive_tracer; mod executive_tracer;
pub mod flat;
mod import; mod import;
mod noop_tracer; mod noop_tracer;
pub use types::trace_types::*; pub use types::trace_types::*;
pub use self::block::BlockTraces;
pub use self::config::{Config, Switch}; pub use self::config::{Config, Switch};
pub use self::db::TraceDB; pub use self::db::TraceDB;
pub use self::error::Error; pub use self::error::Error;
pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use types::trace_types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use types::trace_types::filter::{Filter, AddressesFilter};
@ -59,9 +57,7 @@ pub trait Tracer: Send {
call: Option<Call>, call: Option<Call>,
gas_used: U256, gas_used: U256,
output: Option<Bytes>, output: Option<Bytes>,
depth: usize, subs: Vec<FlatTrace>,
subs: Vec<Trace>,
delegate_call: bool
); );
/// Stores trace create info. /// Stores trace create info.
@ -71,21 +67,23 @@ pub trait Tracer: Send {
gas_used: U256, gas_used: U256,
code: Option<Bytes>, code: Option<Bytes>,
address: Address, address: Address,
depth: usize, subs: Vec<FlatTrace>
subs: Vec<Trace>
); );
/// Stores failed call trace. /// Stores failed call trace.
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool); fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>);
/// Stores failed create trace. /// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>); fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>);
/// Stores suicide info.
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
/// Spawn subtracer which will be used to trace deeper levels of execution. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized; fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces. /// Consumes self and returns all traces.
fn traces(self) -> Vec<Trace>; fn traces(self) -> Vec<FlatTrace>;
} }
/// Used by executive to build VM traces. /// Used by executive to build VM traces.

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::{Tracer, VMTracer}; use trace::{Tracer, VMTracer, FlatTrace};
use trace::trace::{Trace, Call, Create, VMTrace}; use trace::trace::{Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer; pub struct NoopTracer;
@ -37,29 +37,32 @@ impl Tracer for NoopTracer {
None None
} }
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>, _: bool) { fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: Vec<FlatTrace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
} }
fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: usize, _: Vec<Trace>) { fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: Vec<FlatTrace>) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
} }
fn trace_failed_call(&mut self, call: Option<Call>, _: usize, _: Vec<Trace>, _: bool) { fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
} }
fn trace_failed_create(&mut self, create: Option<Create>, _: usize, _: Vec<Trace>) { fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed"); assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
} }
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) {
}
fn subtracer(&self) -> Self { fn subtracer(&self) -> Self {
NoopTracer NoopTracer
} }
fn traces(self) -> Vec<Trace> { fn traces(self) -> Vec<FlatTrace> {
vec![] vec![]
} }
} }

View File

@ -18,7 +18,8 @@
use util::numbers::*; use util::numbers::*;
use util::Bytes; use util::Bytes;
use trace::{Trace, VMTrace}; use util::rlp::*;
use trace::{VMTrace, FlatTrace};
use types::log_entry::LogEntry; use types::log_entry::LogEntry;
use types::state_diff::StateDiff; use types::state_diff::StateDiff;
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
@ -26,6 +27,43 @@ use std::fmt;
use std::mem; use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
/// The type of the call-like instruction.
#[derive(Debug, PartialEq, Clone, Binary)]
pub enum CallType {
/// Not a CALL.
None,
/// CALL.
Call,
/// CALLCODE.
CallCode,
/// DELEGATECALL.
DelegateCall,
}
impl Encodable for CallType {
fn rlp_append(&self, s: &mut RlpStream) {
let v = match *self {
CallType::None => 0u32,
CallType::Call => 1,
CallType::CallCode => 2,
CallType::DelegateCall => 3,
};
s.append(&v);
}
}
impl Decodable for CallType {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().as_val().and_then(|v| Ok(match v {
0u32 => CallType::None,
1 => CallType::Call,
2 => CallType::CallCode,
3 => CallType::DelegateCall,
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
}))
}
}
/// Transaction execution receipt. /// Transaction execution receipt.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Executed { pub struct Executed {
@ -59,7 +97,7 @@ pub struct Executed {
/// Transaction output. /// Transaction output.
pub output: Bytes, pub output: Bytes,
/// The trace of this transaction. /// The trace of this transaction.
pub trace: Option<Trace>, pub trace: Vec<FlatTrace>,
/// The VM trace of this transaction. /// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>, pub vm_trace: Option<VMTrace>,
/// The state diff, if we traced it. /// The state diff, if we traced it.
@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError {
/// Transaction execution result. /// Transaction execution result.
pub type ExecutionResult = Result<Executed, ExecutionError>; pub type ExecutionResult = Result<Executed, ExecutionError>;
#[test]
fn should_encode_and_decode_call_type() {
use util::rlp;
let original = CallType::Call;
let encoded = rlp::encode(&original);
let decoded = rlp::decode(&encoded);
assert_eq!(original, decoded);
}

View File

@ -22,7 +22,7 @@ use util::{Address, FixedHash};
use util::sha3::Hashable; use util::sha3::Hashable;
use basic_types::LogBloom; use basic_types::LogBloom;
use trace::flat::FlatTrace; use trace::flat::FlatTrace;
use types::trace_types::trace::Action; use types::trace_types::trace::{Action, Res};
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::mem; use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -58,7 +58,7 @@ impl AddressesFilter {
true => vec![LogBloom::new()], true => vec![LogBloom::new()],
false => self.list.iter() false => self.list.iter()
.map(|address| LogBloom::from_bloomed(&address.sha3())) .map(|address| LogBloom::from_bloomed(&address.sha3()))
.collect() .collect(),
} }
} }
@ -71,7 +71,7 @@ impl AddressesFilter {
.flat_map(|bloom| self.list.iter() .flat_map(|bloom| self.list.iter()
.map(|address| bloom.with_bloomed(&address.sha3())) .map(|address| bloom.with_bloomed(&address.sha3()))
.collect::<Vec<_>>()) .collect::<Vec<_>>())
.collect() .collect(),
} }
} }
} }
@ -110,29 +110,40 @@ impl Filter {
/// Returns true if given trace matches the filter. /// Returns true if given trace matches the filter.
pub fn matches(&self, trace: &FlatTrace) -> bool { pub fn matches(&self, trace: &FlatTrace) -> bool {
match trace.action { let action = match trace.action {
Action::Call(ref call) => { Action::Call(ref call) => {
let from_matches = self.from_address.matches(&call.from); let from_matches = self.from_address.matches(&call.from);
let to_matches = self.to_address.matches(&call.to); let to_matches = self.to_address.matches(&call.to);
from_matches && to_matches from_matches && to_matches
}, }
Action::Create(ref create) => { Action::Create(ref create) => {
let from_matches = self.from_address.matches(&create.from); let from_matches = self.from_address.matches(&create.from);
let to_matches = self.to_address.matches_all(); let to_matches = self.to_address.matches_all();
from_matches && to_matches from_matches && to_matches
},
Action::Suicide(ref suicide) => {
let from_matches = self.from_address.matches(&suicide.address);
let to_matches = self.to_address.matches(&suicide.refund_address);
from_matches && to_matches
} }
};
action || match trace.result {
Res::Create(ref create) => self.to_address.matches(&create.address),
_ => false
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use util::{FixedHash, Address, U256}; use util::{FixedHash, Address};
use util::sha3::Hashable; use util::sha3::Hashable;
use trace::trace::{Action, Call, Res}; use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
use trace::flat::FlatTrace; use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter}; use trace::{Filter, AddressesFilter};
use basic_types::LogBloom; use basic_types::LogBloom;
use types::executed::CallType;
#[test] #[test]
fn empty_trace_filter_bloom_possibilities() { fn empty_trace_filter_bloom_possibilities() {
@ -270,14 +281,15 @@ mod tests {
let trace = FlatTrace { let trace = FlatTrace {
action: Action::Call(Call { action: Action::Call(Call {
from: Address::from(1), from: 1.into(),
to: Address::from(2), to: 2.into(),
value: U256::from(3), value: 3.into(),
gas: U256::from(4), gas: 4.into(),
input: vec![0x5], input: vec![0x5],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
trace_address: vec![0], trace_address: vec![0].into_iter().collect(),
subtraces: 0, subtraces: 0,
}; };
@ -288,5 +300,48 @@ mod tests {
assert!(f4.matches(&trace)); assert!(f4.matches(&trace));
assert!(f5.matches(&trace)); assert!(f5.matches(&trace));
assert!(!f6.matches(&trace)); assert!(!f6.matches(&trace));
let trace = FlatTrace {
action: Action::Create(Create {
from: 1.into(),
value: 3.into(),
gas: 4.into(),
init: vec![0x5],
}),
result: Res::Create(CreateResult {
gas_used: 10.into(),
code: vec![],
address: 2.into(),
}),
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
};
assert!(f0.matches(&trace));
assert!(f1.matches(&trace));
assert!(f2.matches(&trace));
assert!(f3.matches(&trace));
assert!(f4.matches(&trace));
assert!(f5.matches(&trace));
assert!(!f6.matches(&trace));
let trace = FlatTrace {
action: Action::Suicide(Suicide {
address: 1.into(),
refund_address: 2.into(),
balance: 3.into(),
}),
result: Res::None,
trace_address: vec![].into_iter().collect(),
subtraces: 0
};
assert!(f0.matches(&trace));
assert!(f1.matches(&trace));
assert!(f2.matches(&trace));
assert!(f3.matches(&trace));
assert!(f4.matches(&trace));
assert!(f5.matches(&trace));
assert!(!f6.matches(&trace));
} }
} }

View File

@ -0,0 +1,178 @@
// 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 <http://www.gnu.org/licenses/>.
//! Flat trace module
use std::collections::VecDeque;
use std::mem;
use ipc::binary::BinaryConvertError;
use util::rlp::*;
use basic_types::LogBloom;
use super::trace::{Action, Res};
/// Trace localized in vector of traces produced by a single transaction.
///
/// Parent and children indexes refer to positions in this vector.
#[derive(Debug, PartialEq, Clone, Binary)]
pub struct FlatTrace {
/// Type of action performed by a transaction.
pub action: Action,
/// Result of this action.
pub result: Res,
/// Number of subtraces.
pub subtraces: usize,
/// Exact location of trace.
///
/// [index in root, index in first CALL, index in second CALL, ...]
pub trace_address: VecDeque<usize>,
}
impl FlatTrace {
/// Returns bloom of the trace.
pub fn bloom(&self) -> LogBloom {
self.action.bloom() | self.result.bloom()
}
}
impl Encodable for FlatTrace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.action);
s.append(&self.result);
s.append(&self.subtraces);
s.append(&self.trace_address.clone().into_iter().collect::<Vec<_>>());
}
}
impl Decodable for FlatTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let v: Vec<usize> = try!(d.val_at(3));
let res = FlatTrace {
action: try!(d.val_at(0)),
result: try!(d.val_at(1)),
subtraces: try!(d.val_at(2)),
trace_address: v.into_iter().collect(),
};
Ok(res)
}
}
/// Represents all traces produced by a single transaction.
#[derive(Debug, PartialEq, Clone)]
pub struct FlatTransactionTraces(Vec<FlatTrace>);
impl From<Vec<FlatTrace>> for FlatTransactionTraces {
fn from(v: Vec<FlatTrace>) -> Self {
FlatTransactionTraces(v)
}
}
impl FlatTransactionTraces {
/// Returns bloom of the trace.
pub fn bloom(&self) -> LogBloom {
self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom())
}
}
impl Encodable for FlatTransactionTraces {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.0);
}
}
impl Decodable for FlatTransactionTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Ok(FlatTransactionTraces(try!(Decodable::decode(decoder))))
}
}
impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
fn into(self) -> Vec<FlatTrace> {
self.0
}
}
/// Represents all traces produced by transactions in a single block.
#[derive(Debug, PartialEq, Clone)]
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
impl From<Vec<FlatTransactionTraces>> for FlatBlockTraces {
fn from(v: Vec<FlatTransactionTraces>) -> Self {
FlatBlockTraces(v)
}
}
impl FlatBlockTraces {
/// Returns bloom of the trace.
pub fn bloom(&self) -> LogBloom {
self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom())
}
}
impl Encodable for FlatBlockTraces {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.0);
}
}
impl Decodable for FlatBlockTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Ok(FlatBlockTraces(try!(Decodable::decode(decoder))))
}
}
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
fn into(self) -> Vec<FlatTransactionTraces> {
self.0
}
}
#[cfg(test)]
mod tests {
use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace};
use trace::trace::{Action, Res, CallResult, Call};
use types::executed::CallType;
#[test]
fn test_trace_serialization() {
use util::rlp;
let flat_trace = FlatTrace {
action: Action::Call(Call {
from: 1.into(),
to: 2.into(),
value: 3.into(),
gas: 4.into(),
input: vec![0x5],
call_type: CallType::Call,
}),
result: Res::Call(CallResult {
gas_used: 10.into(),
output: vec![0x11, 0x12]
}),
trace_address: Default::default(),
subtraces: 0,
};
let block_traces = FlatBlockTraces(vec![FlatTransactionTraces(vec![flat_trace])]);
let encoded = rlp::encode(&block_traces);
let decoded = rlp::decode(&encoded);
assert_eq!(block_traces, decoded);
}
}

View File

@ -17,5 +17,6 @@
//! Types used in the public api //! Types used in the public api
pub mod filter; pub mod filter;
pub mod flat;
pub mod trace; pub mod trace;
pub mod localized; pub mod localized;

View File

@ -21,6 +21,7 @@ use util::rlp::*;
use util::sha3::Hashable; use util::sha3::Hashable;
use action_params::ActionParams; use action_params::ActionParams;
use basic_types::LogBloom; use basic_types::LogBloom;
use types::executed::CallType;
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::mem; use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -87,6 +88,13 @@ impl Decodable for CreateResult {
} }
} }
impl CreateResult {
/// Returns bloom.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.address.sha3())
}
}
/// Description of a _call_ action, either a `CALL` operation or a message transction. /// Description of a _call_ action, either a `CALL` operation or a message transction.
#[derive(Debug, Clone, PartialEq, Binary)] #[derive(Debug, Clone, PartialEq, Binary)]
pub struct Call { pub struct Call {
@ -100,6 +108,8 @@ pub struct Call {
pub gas: U256, pub gas: U256,
/// The input data provided to the call. /// The input data provided to the call.
pub input: Bytes, pub input: Bytes,
/// The type of the call.
pub call_type: CallType,
} }
impl From<ActionParams> for Call { impl From<ActionParams> for Call {
@ -110,18 +120,20 @@ impl From<ActionParams> for Call {
value: p.value.value(), value: p.value.value(),
gas: p.gas, gas: p.gas,
input: p.data.unwrap_or_else(Vec::new), input: p.data.unwrap_or_else(Vec::new),
call_type: p.call_type,
} }
} }
} }
impl Encodable for Call { impl Encodable for Call {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(5); s.begin_list(6);
s.append(&self.from); s.append(&self.from);
s.append(&self.to); s.append(&self.to);
s.append(&self.value); s.append(&self.value);
s.append(&self.gas); s.append(&self.gas);
s.append(&self.input); s.append(&self.input);
s.append(&self.call_type);
} }
} }
@ -134,6 +146,7 @@ impl Decodable for Call {
value: try!(d.val_at(2)), value: try!(d.val_at(2)),
gas: try!(d.val_at(3)), gas: try!(d.val_at(3)),
input: try!(d.val_at(4)), input: try!(d.val_at(4)),
call_type: try!(d.val_at(5)),
}; };
Ok(res) Ok(res)
@ -205,6 +218,48 @@ impl Create {
} }
} }
/// Suicide action.
#[derive(Debug, Clone, PartialEq, Binary)]
pub struct Suicide {
/// Suicided address.
pub address: Address,
/// Suicided contract heir.
pub refund_address: Address,
/// Balance of the contract just before suicide.
pub balance: U256,
}
impl Suicide {
/// Return suicide action bloom.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.address.sha3())
.with_bloomed(&self.refund_address.sha3())
}
}
impl Encodable for Suicide {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.address);
s.append(&self.refund_address);
s.append(&self.balance);
}
}
impl Decodable for Suicide {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = Suicide {
address: try!(d.val_at(0)),
refund_address: try!(d.val_at(1)),
balance: try!(d.val_at(3)),
};
Ok(res)
}
}
/// Description of an action that we trace; will be either a call or a create. /// Description of an action that we trace; will be either a call or a create.
#[derive(Debug, Clone, PartialEq, Binary)] #[derive(Debug, Clone, PartialEq, Binary)]
pub enum Action { pub enum Action {
@ -212,6 +267,8 @@ pub enum Action {
Call(Call), Call(Call),
/// It's a create action. /// It's a create action.
Create(Create), Create(Create),
/// Suicide.
Suicide(Suicide),
} }
impl Encodable for Action { impl Encodable for Action {
@ -225,6 +282,10 @@ impl Encodable for Action {
Action::Create(ref create) => { Action::Create(ref create) => {
s.append(&1u8); s.append(&1u8);
s.append(create); s.append(create);
},
Action::Suicide(ref suicide) => {
s.append(&2u8);
s.append(suicide);
} }
} }
} }
@ -237,6 +298,7 @@ impl Decodable for Action {
match action_type { match action_type {
0 => d.val_at(1).map(Action::Call), 0 => d.val_at(1).map(Action::Call),
1 => d.val_at(1).map(Action::Create), 1 => d.val_at(1).map(Action::Create),
2 => d.val_at(2).map(Action::Suicide),
_ => Err(DecoderError::Custom("Invalid action type.")), _ => Err(DecoderError::Custom("Invalid action type.")),
} }
} }
@ -248,6 +310,7 @@ impl Action {
match *self { match *self {
Action::Call(ref call) => call.bloom(), Action::Call(ref call) => call.bloom(),
Action::Create(ref create) => create.bloom(), Action::Create(ref create) => create.bloom(),
Action::Suicide(ref suicide) => suicide.bloom(),
} }
} }
} }
@ -263,6 +326,8 @@ pub enum Res {
FailedCall, FailedCall,
/// Failed create. /// Failed create.
FailedCreate, FailedCreate,
/// None
None,
} }
impl Encodable for Res { impl Encodable for Res {
@ -285,6 +350,10 @@ impl Encodable for Res {
Res::FailedCreate => { Res::FailedCreate => {
s.begin_list(1); s.begin_list(1);
s.append(&3u8); s.append(&3u8);
},
Res::None => {
s.begin_list(1);
s.append(&4u8);
} }
} }
} }
@ -299,53 +368,19 @@ impl Decodable for Res {
1 => d.val_at(1).map(Res::Create), 1 => d.val_at(1).map(Res::Create),
2 => Ok(Res::FailedCall), 2 => Ok(Res::FailedCall),
3 => Ok(Res::FailedCreate), 3 => Ok(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")), _ => Err(DecoderError::Custom("Invalid result type.")),
} }
} }
} }
#[derive(Debug, Clone, PartialEq, Binary)] impl Res {
/// A trace; includes a description of the action being traced and sub traces of each interior action. /// Returns result bloom.
pub struct Trace {
/// The number of EVM execution environments active when this action happened; 0 if it's
/// the outer action of the transaction.
pub depth: usize,
/// The action being performed.
pub action: Action,
/// The sub traces for each interior action performed as part of this call.
pub subs: Vec<Trace>,
/// The result of the performed action.
pub result: Res,
}
impl Encodable for Trace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.depth);
s.append(&self.action);
s.append(&self.subs);
s.append(&self.result);
}
}
impl Decodable for Trace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = Trace {
depth: try!(d.val_at(0)),
action: try!(d.val_at(1)),
subs: try!(d.val_at(2)),
result: try!(d.val_at(3)),
};
Ok(res)
}
}
impl Trace {
/// Returns trace bloom.
pub fn bloom(&self) -> LogBloom { pub fn bloom(&self) -> LogBloom {
self.subs.iter().fold(self.action.bloom(), |b, s| b | s.bloom()) match *self {
Res::Create(ref create) => create.bloom(),
Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(),
}
} }
} }
@ -513,84 +548,3 @@ impl Decodable for VMTrace {
} }
} }
#[cfg(test)]
mod tests {
use util::{Address, U256, FixedHash};
use util::rlp::{encode, decode};
use util::sha3::Hashable;
use trace::trace::{Call, CallResult, Create, Res, Action, Trace};
#[test]
fn traces_rlp() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5]
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::FailedCreate
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let encoded = encode(&trace);
let decoded: Trace = decode(&encoded);
assert_eq!(trace, decoded);
}
#[test]
fn traces_bloom() {
let trace = Trace {
depth: 2,
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
input: vec![0x5]
}),
subs: vec![
Trace {
depth: 3,
action: Action::Create(Create {
from: Address::from(6),
value: U256::from(7),
gas: U256::from(8),
init: vec![0x9]
}),
subs: vec![],
result: Res::FailedCreate
}
],
result: Res::Call(CallResult {
gas_used: U256::from(10),
output: vec![0x11, 0x12]
})
};
let bloom = trace.bloom();
// right now only addresses are bloomed
assert!(bloom.contains_bloomed(&Address::from(1).sha3()));
assert!(bloom.contains_bloomed(&Address::from(2).sha3()));
assert!(!bloom.contains_bloomed(&Address::from(20).sha3()));
assert!(bloom.contains_bloomed(&Address::from(6).sha3()));
}
}

View File

@ -139,6 +139,74 @@ impl<R: BinaryConvertable, E: BinaryConvertable> BinaryConvertable for Result<R,
} }
} }
impl<T> BinaryConvertable for VecDeque<T> where T: BinaryConvertable {
fn size(&self) -> usize {
match T::len_params() {
0 => mem::size_of::<T>() * self.len(),
_ => self.iter().fold(0usize, |acc, t| acc + t.size()),
}
}
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
let mut offset = 0usize;
for item in self.iter() {
let next_size = match T::len_params() {
0 => mem::size_of::<T>(),
_ => { let size = item.size(); length_stack.push_back(size); size },
};
if next_size > 0 {
let item_end = offset + next_size;
try!(item.to_bytes(&mut buffer[offset..item_end], length_stack));
offset = item_end;
}
}
Ok(())
}
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
let mut index = 0;
let mut result = Self::with_capacity(
match T::len_params() {
0 => buffer.len() / mem::size_of::<T>(),
_ => 128,
});
if buffer.len() == 0 { return Ok(result); }
loop {
let next_size = match T::len_params() {
0 => mem::size_of::<T>(),
_ => try!(length_stack.pop_front().ok_or(BinaryConvertError)),
};
let item = if next_size == 0 {
try!(T::from_empty_bytes())
}
else {
try!(T::from_bytes(&buffer[index..index+next_size], length_stack))
};
result.push_back(item);
index = index + next_size;
if index == buffer.len() { break; }
if index + next_size > buffer.len() {
return Err(BinaryConvertError)
}
}
Ok(result)
}
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
Ok(Self::new())
}
fn len_params() -> usize {
1
}
}
//
impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable { impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable {
fn size(&self) -> usize { fn size(&self) -> usize {
match T::len_params() { match T::len_params() {

View File

@ -17,6 +17,7 @@
//! Spec params deserialization. //! Spec params deserialization.
use uint::Uint; use uint::Uint;
use hash::H256;
/// Spec params. /// Spec params.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
@ -33,6 +34,12 @@ pub struct Params {
/// Minimum gas limit. /// Minimum gas limit.
#[serde(rename="minGasLimit")] #[serde(rename="minGasLimit")]
pub min_gas_limit: Uint, pub min_gas_limit: Uint,
/// Option fork block number to check.
#[serde(rename="forkBlock")]
pub fork_block: Option<Uint>,
/// Expected fork block hash.
#[serde(rename="forkCanonHash")]
pub fork_hash: Option<H256>,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -75,7 +75,9 @@ mod tests {
"frontierCompatibilityModeLimit": "0x789b0", "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388", "minGasLimit": "0x1388",
"networkID" : "0x2" "networkID" : "0x2",
"forkBlock": "0xffffffffffffffff",
"forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}, },
"genesis": { "genesis": {
"seal": { "seal": {

View File

@ -335,6 +335,7 @@ impl Configuration {
sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| { sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id)) U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
}); });
sync_config.fork_block = spec.fork_block().clone();
sync_config sync_config
} }

View File

@ -18,13 +18,13 @@
use std::sync::{Weak, Arc}; use std::sync::{Weak, Arc};
use jsonrpc_core::*; use jsonrpc_core::*;
use std::collections::BTreeMap;
use util::H256; use util::H256;
use util::rlp::{UntrustedRlp, View};
use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use v1::traits::Traces; use v1::traits::Traces;
use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace}; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults};
/// Traces api implementation. /// Traces api implementation.
pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService { pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService {
@ -113,22 +113,30 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
state_diffing: flags.contains(&("stateDiff".to_owned())), state_diffing: flags.contains(&("stateDiff".to_owned())),
}; };
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = take_weak!(self.client).call(&signed, analytics); match take_weak!(self.client).call(&signed, analytics) {
if let Ok(executed) = r { Ok(e) => to_value(&TraceResults::from(e)),
// TODO maybe add other stuff to this? _ => Ok(Value::Null),
let mut ret = map!["output".to_owned() => to_value(&Bytes(executed.output)).unwrap()]; }
if let Some(trace) = executed.trace { })
ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap()); }
}
if let Some(vm_trace) = executed.vm_trace { fn raw_transaction(&self, params: Params) -> Result<Value, Error> {
ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap()); trace!(target: "jsonrpc", "call: {:?}", params);
} from_params::<(Bytes, Vec<String>)>(params)
if let Some(state_diff) = executed.state_diff { .and_then(|(raw_transaction, flags)| {
ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap()); let raw_transaction = raw_transaction.to_vec();
} let analytics = CallAnalytics {
return Ok(Value::Object(ret)) transaction_tracing: flags.contains(&("trace".to_owned())),
vm_tracing: flags.contains(&("vmTrace".to_owned())),
state_diffing: flags.contains(&("stateDiff".to_owned())),
};
match UntrustedRlp::new(&raw_transaction).as_val() {
Ok(signed) => match take_weak!(self.client).call(&signed, analytics) {
Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null),
},
Err(_) => Err(Error::invalid_params()),
} }
Ok(Value::Null)
}) })
} }
} }

View File

@ -366,7 +366,7 @@ fn rpc_eth_pending_transaction_by_hash() {
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
} }
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "eth_getTransactionByHash", "method": "eth_getTransactionByHash",
@ -430,7 +430,7 @@ fn rpc_eth_call() {
logs: vec![], logs: vec![],
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); });
@ -465,7 +465,7 @@ fn rpc_eth_call_default_block() {
logs: vec![], logs: vec![],
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); });
@ -499,7 +499,7 @@ fn rpc_eth_estimate_gas() {
logs: vec![], logs: vec![],
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); });
@ -534,7 +534,7 @@ fn rpc_eth_estimate_gas_default_block() {
logs: vec![], logs: vec![],
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); });

View File

@ -35,6 +35,9 @@ pub trait Traces: Sized + Send + Sync + 'static {
/// Executes the given call and returns a number of possible traces for it. /// Executes the given call and returns a number of possible traces for it.
fn call(&self, _: Params) -> Result<Value, Error>; fn call(&self, _: Params) -> Result<Value, Error>;
/// Executes the given raw transaction and returns a number of possible traces for it.
fn raw_transaction(&self, _: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate. /// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> { fn to_delegate(self) -> IoDelegate<Self> {
let mut delegate = IoDelegate::new(Arc::new(self)); let mut delegate = IoDelegate::new(Arc::new(self));
@ -43,6 +46,7 @@ pub trait Traces: Sized + Send + Sync + 'static {
delegate.add_method("trace_transaction", Traces::transaction_traces); delegate.add_method("trace_transaction", Traces::transaction_traces);
delegate.add_method("trace_block", Traces::block_traces); delegate.add_method("trace_block", Traces::block_traces);
delegate.add_method("trace_call", Traces::call); delegate.add_method("trace_call", Traces::call);
delegate.add_method("trace_rawTransaction", Traces::raw_transaction);
delegate delegate
} }

View File

@ -103,7 +103,7 @@ mod tests {
fn test_serialize_block_transactions() { fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]); let t = BlockTransactions::Full(vec![Transaction::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}]"#);
let t = BlockTransactions::Hashes(vec![H256::default()]); let t = BlockTransactions::Hashes(vec![H256::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();

View File

@ -41,5 +41,5 @@ pub use self::transaction::Transaction;
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
pub use self::call_request::CallRequest; pub use self::call_request::CallRequest;
pub use self::receipt::Receipt; pub use self::receipt::Receipt;
pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace}; pub use self::trace::{LocalizedTrace, TraceResults};
pub use self::trace_filter::TraceFilter; pub use self::trace_filter::TraceFilter;

View File

@ -18,11 +18,13 @@ use std::collections::BTreeMap;
use util::{Address, U256, H256, Uint}; use util::{Address, U256, H256, Uint};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use ethcore::trace::trace; use ethcore::trace::trace;
use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace as et; use ethcore::trace as et;
use ethcore::state_diff; use ethcore::state_diff;
use ethcore::account_diff; use ethcore::account_diff;
use v1::types::Bytes; use v1::types::{Bytes};
use ethcore::client::Executed;
use ethcore::executed;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
/// A diff of some chunk of memory. /// A diff of some chunk of memory.
@ -193,6 +195,7 @@ impl From<account_diff::AccountDiff> for AccountDiff {
} }
} }
#[derive(Debug)]
/// Serde-friendly `StateDiff` shadow. /// Serde-friendly `StateDiff` shadow.
pub struct StateDiff(BTreeMap<Address, AccountDiff>); pub struct StateDiff(BTreeMap<Address, AccountDiff>);
@ -233,6 +236,34 @@ impl From<trace::Create> for Create {
} }
} }
/// Call type.
#[derive(Debug, Serialize)]
pub enum CallType {
/// None
#[serde(rename="none")]
None,
/// Call
#[serde(rename="call")]
Call,
/// Call code
#[serde(rename="callcode")]
CallCode,
/// Delegate call
#[serde(rename="delegatecall")]
DelegateCall,
}
impl From<executed::CallType> for CallType {
fn from(c: executed::CallType) -> Self {
match c {
executed::CallType::None => CallType::None,
executed::CallType::Call => CallType::Call,
executed::CallType::CallCode => CallType::CallCode,
executed::CallType::DelegateCall => CallType::DelegateCall,
}
}
}
/// Call response /// Call response
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Call { pub struct Call {
@ -246,6 +277,9 @@ pub struct Call {
gas: U256, gas: U256,
/// Input data /// Input data
input: Bytes, input: Bytes,
/// The type of the call.
#[serde(rename="callType")]
call_type: CallType,
} }
impl From<trace::Call> for Call { impl From<trace::Call> for Call {
@ -256,6 +290,29 @@ impl From<trace::Call> for Call {
value: c.value, value: c.value,
gas: c.gas, gas: c.gas,
input: Bytes::new(c.input), input: Bytes::new(c.input),
call_type: c.call_type.into(),
}
}
}
/// Suicide
#[derive(Debug, Serialize)]
pub struct Suicide {
/// Address.
pub address: Address,
/// Refund address.
#[serde(rename="refundAddress")]
pub refund_address: Address,
/// Balance.
pub balance: U256,
}
impl From<trace::Suicide> for Suicide {
fn from(s: trace::Suicide) -> Self {
Suicide {
address: s.address.into(),
refund_address: s.refund_address.into(),
balance: s.balance.into(),
} }
} }
} }
@ -269,13 +326,17 @@ pub enum Action {
/// Create /// Create
#[serde(rename="create")] #[serde(rename="create")]
Create(Create), Create(Create),
/// Suicide
#[serde(rename="suicide")]
Suicide(Suicide),
} }
impl From<trace::Action> for Action { impl From<trace::Action> for Action {
fn from(c: trace::Action) -> Self { fn from(c: trace::Action) -> Self {
match c { match c {
trace::Action::Call(call) => Action::Call(Call::from(call)), trace::Action::Call(call) => Action::Call(call.into()),
trace::Action::Create(create) => Action::Create(Create::from(create)), trace::Action::Create(create) => Action::Create(create.into()),
trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()),
} }
} }
} }
@ -336,6 +397,9 @@ pub enum Res {
/// Creation failure /// Creation failure
#[serde(rename="failedCreate")] #[serde(rename="failedCreate")]
FailedCreate, FailedCreate,
/// None
#[serde(rename="none")]
None,
} }
impl From<trace::Res> for Res { impl From<trace::Res> for Res {
@ -345,6 +409,7 @@ impl From<trace::Res> for Res {
trace::Res::Create(create) => Res::Create(CreateResult::from(create)), trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
trace::Res::FailedCall => Res::FailedCall, trace::Res::FailedCall => Res::FailedCall,
trace::Res::FailedCreate => Res::FailedCreate, trace::Res::FailedCreate => Res::FailedCreate,
trace::Res::None => Res::None,
} }
} }
} }
@ -393,23 +458,50 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
/// Trace /// Trace
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Trace { pub struct Trace {
/// Depth within the call trace tree. /// Trace address
depth: usize, #[serde(rename="traceAddress")]
trace_address: Vec<U256>,
/// Subtraces
subtraces: U256,
/// Action /// Action
action: Action, action: Action,
/// Result /// Result
result: Res, result: Res,
/// Subtraces
subtraces: Vec<Trace>,
} }
impl From<EthTrace> for Trace { impl From<FlatTrace> for Trace {
fn from(t: EthTrace) -> Self { fn from(t: FlatTrace) -> Self {
Trace { Trace {
depth: t.depth.into(), trace_address: t.trace_address.into_iter().map(Into::into).collect(),
subtraces: t.subtraces.into(),
action: t.action.into(), action: t.action.into(),
result: t.result.into(), result: t.result.into(),
subtraces: t.subs.into_iter().map(From::from).collect(), }
}
}
#[derive(Debug, Serialize)]
/// A diff of some chunk of memory.
pub struct TraceResults {
/// The output of the call/create
pub output: Vec<u8>,
/// The transaction trace.
pub trace: Vec<Trace>,
/// The transaction trace.
#[serde(rename="vmTrace")]
pub vm_trace: Option<VMTrace>,
/// The transaction trace.
#[serde(rename="stateDiff")]
pub state_diff: Option<StateDiff>,
}
impl From<Executed> for TraceResults {
fn from(t: Executed) -> Self {
TraceResults {
output: t.output.into(),
trace: t.trace.into_iter().map(Into::into).collect(),
vm_trace: t.vm_trace.map(Into::into),
state_diff: t.state_diff.map(Into::into),
} }
} }
} }
@ -422,6 +514,18 @@ mod tests {
use v1::types::Bytes; use v1::types::Bytes;
use super::*; use super::*;
#[test]
fn should_serialize_trace_results() {
let r = TraceResults {
output: vec![0x60],
trace: vec![],
vm_trace: None,
state_diff: None,
};
let serialized = serde_json::to_string(&r).unwrap();
assert_eq!(serialized, r#"{"output":[96],"trace":[],"vmTrace":null,"stateDiff":null}"#);
}
#[test] #[test]
fn test_trace_serialize() { fn test_trace_serialize() {
let t = LocalizedTrace { let t = LocalizedTrace {
@ -431,6 +535,7 @@ mod tests {
value: U256::from(6), value: U256::from(6),
gas: U256::from(7), gas: U256::from(7),
input: Bytes::new(vec![0x12, 0x34]), input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}), }),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: U256::from(8), gas_used: U256::from(8),
@ -444,7 +549,7 @@ mod tests {
block_hash: H256::from(14), block_hash: H256::from(14),
}; };
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234","callType":{"call":[]}}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
} }
#[test] #[test]
@ -520,6 +625,7 @@ mod tests {
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: Bytes::new(vec![0x12, 0x34]), input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}), Action::Create(Create { }), Action::Create(Create {
from: Address::from(5), from: Address::from(5),
value: U256::from(6), value: U256::from(6),
@ -528,7 +634,7 @@ mod tests {
})]; })];
let serialized = serde_json::to_string(&actions).unwrap(); let serialized = serde_json::to_string(&actions).unwrap();
assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234","callType":{"call":[]}}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#);
} }
#[test] #[test]

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::numbers::*; use util::numbers::*;
use util::rlp::encode;
use ethcore::contract_address; use ethcore::contract_address;
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, OptionalValue}; use v1::types::{Bytes, OptionalValue};
@ -50,6 +51,8 @@ pub struct Transaction {
pub input: Bytes, pub input: Bytes,
/// Creates contract /// Creates contract
pub creates: OptionalValue<Address>, pub creates: OptionalValue<Address>,
/// Raw transaction data
pub raw: Bytes,
} }
impl From<LocalizedTransaction> for Transaction { impl From<LocalizedTransaction> for Transaction {
@ -73,6 +76,7 @@ impl From<LocalizedTransaction> for Transaction {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null, Action::Call(_) => OptionalValue::Null,
}, },
raw: encode(&t.signed).to_vec().into(),
} }
} }
} }
@ -98,6 +102,7 @@ impl From<SignedTransaction> for Transaction {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null, Action::Call(_) => OptionalValue::Null,
}, },
raw: encode(&t).to_vec().into(),
} }
} }
} }
@ -111,7 +116,7 @@ mod tests {
fn test_transaction_serialize() { fn test_transaction_serialize() {
let t = Transaction::default(); let t = Transaction::default();
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#); assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}"#);
} }
} }

View File

@ -120,13 +120,22 @@ impl BlockCollection {
if let Some(head) = head { if let Some(head) = head {
match self.blocks.get(&head) { match self.blocks.get(&head) {
Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => { Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => {
self.downloading_bodies.insert(head.clone());
needed_bodies.push(head.clone()); needed_bodies.push(head.clone());
} }
_ => (), _ => (),
} }
} }
} }
self.downloading_bodies.extend(needed_bodies.iter()); for h in self.header_ids.values() {
if needed_bodies.len() >= count {
break;
}
if !self.downloading_bodies.contains(h) {
needed_bodies.push(h.clone());
self.downloading_bodies.insert(h.clone());
}
}
needed_bodies needed_bodies
} }
@ -286,6 +295,7 @@ impl BlockCollection {
self.parents.insert(info.parent_hash.clone(), hash.clone()); self.parents.insert(info.parent_hash.clone(), hash.clone());
self.blocks.insert(hash.clone(), block); self.blocks.insert(hash.clone(), block);
trace!(target: "sync", "New header: {}", hash.hex());
Ok(hash) Ok(hash)
} }

View File

@ -113,14 +113,15 @@ const MAX_NODE_DATA_TO_SEND: usize = 1024;
const MAX_RECEIPTS_TO_SEND: usize = 1024; const MAX_RECEIPTS_TO_SEND: usize = 1024;
const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256;
const MAX_HEADERS_TO_REQUEST: usize = 128; const MAX_HEADERS_TO_REQUEST: usize = 128;
const MAX_BODIES_TO_REQUEST: usize = 64; const MAX_BODIES_TO_REQUEST: usize = 128;
const MIN_PEERS_PROPAGATION: usize = 4; const MIN_PEERS_PROPAGATION: usize = 4;
const MAX_PEERS_PROPAGATION: usize = 128; const MAX_PEERS_PROPAGATION: usize = 128;
const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20; const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20;
const SUBCHAIN_SIZE: usize = 64; const SUBCHAIN_SIZE: usize = 256;
const MAX_ROUND_PARENTS: usize = 32; const MAX_ROUND_PARENTS: usize = 32;
const MAX_NEW_HASHES: usize = 64; const MAX_NEW_HASHES: usize = 64;
const MAX_TX_TO_IMPORT: usize = 512; const MAX_TX_TO_IMPORT: usize = 512;
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
const STATUS_PACKET: u8 = 0x00; const STATUS_PACKET: u8 = 0x00;
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01; const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
@ -136,7 +137,9 @@ const NODE_DATA_PACKET: u8 = 0x0e;
const GET_RECEIPTS_PACKET: u8 = 0x0f; const GET_RECEIPTS_PACKET: u8 = 0x0f;
const RECEIPTS_PACKET: u8 = 0x10; const RECEIPTS_PACKET: u8 = 0x10;
const CONNECTION_TIMEOUT_SEC: f64 = 15f64; const HEADERS_TIMEOUT_SEC: f64 = 15f64;
const BODIES_TIMEOUT_SEC: f64 = 5f64;
const FORK_HEADER_TIMEOUT_SEC: f64 = 3f64;
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
/// Sync state /// Sync state
@ -184,6 +187,7 @@ pub struct SyncStatus {
/// Peer data type requested /// Peer data type requested
enum PeerAsking { enum PeerAsking {
Nothing, Nothing,
ForkHeader,
BlockHeaders, BlockHeaders,
BlockBodies, BlockBodies,
Heads, Heads,
@ -212,6 +216,16 @@ struct PeerInfo {
asking_hash: Option<H256>, asking_hash: Option<H256>,
/// Request timestamp /// Request timestamp
ask_time: f64, ask_time: f64,
/// Pending request is expird and result should be ignored
expired: bool,
/// Peer fork confirmed
confirmed: bool,
}
impl PeerInfo {
fn is_available(&self) -> bool {
self.confirmed && !self.expired
}
} }
/// Blockchain sync handler. /// Blockchain sync handler.
@ -245,6 +259,8 @@ pub struct ChainSync {
round_parents: VecDeque<(H256, H256)>, round_parents: VecDeque<(H256, H256)>,
/// Network ID /// Network ID
network_id: U256, network_id: U256,
/// Optional fork block to check
fork_block: Option<(BlockNumber, H256)>,
} }
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>; type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
@ -268,6 +284,7 @@ impl ChainSync {
round_parents: VecDeque::new(), round_parents: VecDeque::new(),
_max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks), _max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
network_id: config.network_id, network_id: config.network_id,
fork_block: config.fork_block,
}; };
sync.reset(); sync.reset();
sync sync
@ -284,8 +301,8 @@ impl ChainSync {
highest_block_number: self.highest_block.map(|n| max(n, self.last_imported_block)), highest_block_number: self.highest_block.map(|n| max(n, self.last_imported_block)),
blocks_received: if self.last_imported_block > self.starting_block { self.last_imported_block - self.starting_block } else { 0 }, blocks_received: if self.last_imported_block > self.starting_block { self.last_imported_block - self.starting_block } else { 0 },
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 }, blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
num_peers: self.peers.len(), num_peers: self.peers.values().filter(|p| p.confirmed).count(),
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(), num_active_peers: self.peers.values().filter(|p| p.confirmed && p.asking != PeerAsking::Nothing).count(),
mem_used: mem_used:
self.blocks.heap_size() self.blocks.heap_size()
+ self.peers.heap_size_of_children() + self.peers.heap_size_of_children()
@ -306,6 +323,10 @@ impl ChainSync {
for (_, ref mut p) in &mut self.peers { for (_, ref mut p) in &mut self.peers {
p.asking_blocks.clear(); p.asking_blocks.clear();
p.asking_hash = None; p.asking_hash = None;
// mark any pending requests as expired
if p.asking != PeerAsking::Nothing && p.confirmed {
p.expired = true;
}
} }
self.syncing_difficulty = From::from(0u64); self.syncing_difficulty = From::from(0u64);
self.state = SyncState::Idle; self.state = SyncState::Idle;
@ -356,6 +377,8 @@ impl ChainSync {
asking_blocks: Vec::new(), asking_blocks: Vec::new(),
asking_hash: None, asking_hash: None,
ask_time: 0f64, ask_time: 0f64,
expired: false,
confirmed: self.fork_block.is_none(),
}; };
trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis); trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis);
@ -383,18 +406,43 @@ impl ChainSync {
self.peers.insert(peer_id.clone(), peer); self.peers.insert(peer_id.clone(), peer);
self.active_peers.insert(peer_id.clone()); self.active_peers.insert(peer_id.clone());
debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id)); debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id));
self.sync_peer(io, peer_id, false); if let Some((fork_block, _)) = self.fork_block {
self.request_headers_by_number(io, peer_id, fork_block, 1, 0, false, PeerAsking::ForkHeader);
} else {
self.sync_peer(io, peer_id, false);
}
Ok(()) Ok(())
} }
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
/// Called by peer once it has new block headers during sync /// Called by peer once it has new block headers during sync
fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
let confirmed = match self.peers.get_mut(&peer_id) {
Some(ref mut peer) if peer.asking == PeerAsking::ForkHeader => {
let item_count = r.item_count();
if item_count == 0 || (item_count == 1 && try!(r.at(0)).as_raw().sha3() == self.fork_block.unwrap().1) {
trace!(target: "sync", "{}: Confirmed peer", peer_id);
peer.asking = PeerAsking::Nothing;
peer.confirmed = true;
true
} else {
trace!(target: "sync", "{}: Fork mismatch", peer_id);
io.disconnect_peer(peer_id);
false
}
},
_ => false,
};
if confirmed {
self.sync_peer(io, peer_id, false);
return Ok(());
}
self.clear_peer_download(peer_id); self.clear_peer_download(peer_id);
let expected_hash = self.peers.get(&peer_id).and_then(|p| p.asking_hash);
let expected_asking = if self.state == SyncState::ChainHead { PeerAsking::Heads } else { PeerAsking::BlockHeaders }; let expected_asking = if self.state == SyncState::ChainHead { PeerAsking::Heads } else { PeerAsking::BlockHeaders };
let expected_hash = self.peers.get(&peer_id).and_then(|p| p.asking_hash);
if !self.reset_peer_asking(peer_id, expected_asking) || expected_hash.is_none() { if !self.reset_peer_asking(peer_id, expected_asking) || expected_hash.is_none() {
trace!(target: "sync", "Ignored unexpected headers"); trace!(target: "sync", "{}: Ignored unexpected headers", peer_id);
self.continue_sync(io); self.continue_sync(io);
return Ok(()); return Ok(());
} }
@ -460,14 +508,14 @@ impl ChainSync {
// Disable the peer for this syncing round if it gives invalid chain // Disable the peer for this syncing round if it gives invalid chain
if !valid_response { if !valid_response {
trace!(target: "sync", "{} Deactivated for invalid headers response", peer_id); trace!(target: "sync", "{} Disabled for invalid headers response", peer_id);
self.deactivate_peer(io, peer_id); io.disable_peer(peer_id);
} }
if headers.is_empty() { if headers.is_empty() {
// Peer does not have any new subchain heads, deactivate it nd try with another // Peer does not have any new subchain heads, deactivate it nd try with another
trace!(target: "sync", "{} Deactivated for no data", peer_id); trace!(target: "sync", "{} Disabled for no data", peer_id);
self.deactivate_peer(io, peer_id); io.disable_peer(peer_id);
} }
match self.state { match self.state {
SyncState::ChainHead => { SyncState::ChainHead => {
@ -491,6 +539,9 @@ impl ChainSync {
} }
self.collect_blocks(io); self.collect_blocks(io);
// give a task to the same peer first if received valuable headers.
self.sync_peer(io, peer_id, false);
// give tasks to other peers
self.continue_sync(io); self.continue_sync(io);
Ok(()) Ok(())
} }
@ -543,6 +594,11 @@ impl ChainSync {
peer.latest_hash = header.hash(); peer.latest_hash = header.hash();
peer.latest_number = Some(header.number()); peer.latest_number = Some(header.number());
} }
if self.last_imported_block > header.number() && self.last_imported_block - header.number() > MAX_NEW_BLOCK_AGE {
trace!(target: "sync", "Ignored ancient new block {:?}", h);
io.disable_peer(peer_id);
return Ok(());
}
match io.chain().import_block(block_rlp.as_raw().to_vec()) { match io.chain().import_block(block_rlp.as_raw().to_vec()) {
Err(Error::Import(ImportError::AlreadyInChain)) => { Err(Error::Import(ImportError::AlreadyInChain)) => {
trace!(target: "sync", "New block already in chain {:?}", h); trace!(target: "sync", "New block already in chain {:?}", h);
@ -600,34 +656,39 @@ impl ChainSync {
let hashes = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1))); let hashes = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1)));
let mut max_height: BlockNumber = 0; let mut max_height: BlockNumber = 0;
let mut new_hashes = Vec::new(); let mut new_hashes = Vec::new();
for (rh, rd) in hashes { for (rh, rn) in hashes {
let h = try!(rh); let hash = try!(rh);
let d = try!(rd); let number = try!(rn);
if d > self.highest_block.unwrap_or(0) { if number > self.highest_block.unwrap_or(0) {
self.highest_block = Some(d); self.highest_block = Some(number);
} }
if self.blocks.is_downloading(&h) { if self.blocks.is_downloading(&hash) {
continue; continue;
} }
match io.chain().block_status(BlockID::Hash(h.clone())) { if self.last_imported_block > number && self.last_imported_block - number > MAX_NEW_BLOCK_AGE {
trace!(target: "sync", "Ignored ancient new block hash {:?}", hash);
io.disable_peer(peer_id);
continue;
}
match io.chain().block_status(BlockID::Hash(hash.clone())) {
BlockStatus::InChain => { BlockStatus::InChain => {
trace!(target: "sync", "New block hash already in chain {:?}", h); trace!(target: "sync", "New block hash already in chain {:?}", hash);
}, },
BlockStatus::Queued => { BlockStatus::Queued => {
trace!(target: "sync", "New hash block already queued {:?}", h); trace!(target: "sync", "New hash block already queued {:?}", hash);
}, },
BlockStatus::Unknown => { BlockStatus::Unknown => {
new_hashes.push(h.clone()); new_hashes.push(hash.clone());
if d > max_height { if number > max_height {
trace!(target: "sync", "New unknown block hash {:?}", h); trace!(target: "sync", "New unknown block hash {:?}", hash);
let peer = self.peers.get_mut(&peer_id).unwrap(); let peer = self.peers.get_mut(&peer_id).unwrap();
peer.latest_hash = h.clone(); peer.latest_hash = hash.clone();
peer.latest_number = Some(d); peer.latest_number = Some(number);
max_height = d; max_height = number;
} }
}, },
BlockStatus::Bad => { BlockStatus::Bad => {
debug!(target: "sync", "Bad new block hash {:?}", h); debug!(target: "sync", "Bad new block hash {:?}", hash);
io.disable_peer(peer_id); io.disable_peer(peer_id);
return Ok(()); return Ok(());
} }
@ -665,7 +726,8 @@ impl ChainSync {
/// Resume downloading /// Resume downloading
fn continue_sync(&mut self, io: &mut SyncIo) { fn continue_sync(&mut self, io: &mut SyncIo) {
let mut peers: Vec<(PeerId, U256)> = self.peers.iter().map(|(k, p)| (*k, p.difficulty.unwrap_or_else(U256::zero))).collect(); let mut peers: Vec<(PeerId, U256)> = self.peers.iter().filter_map(|(k, p)|
if p.is_available() { Some((*k, p.difficulty.unwrap_or_else(U256::zero))) } else { None }).collect();
thread_rng().shuffle(&mut peers); //TODO: sort by rating thread_rng().shuffle(&mut peers); //TODO: sort by rating
trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len()); trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len());
for (p, _) in peers { for (p, _) in peers {
@ -673,7 +735,7 @@ impl ChainSync {
self.sync_peer(io, p, false); self.sync_peer(io, p, false);
} }
} }
if self.state != SyncState::Waiting && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing) { if self.state != SyncState::Waiting && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.is_available()) {
self.complete_sync(); self.complete_sync();
} }
} }
@ -699,7 +761,7 @@ impl ChainSync {
} }
let (peer_latest, peer_difficulty) = { let (peer_latest, peer_difficulty) = {
let peer = self.peers.get_mut(&peer_id).unwrap(); let peer = self.peers.get_mut(&peer_id).unwrap();
if peer.asking != PeerAsking::Nothing { if peer.asking != PeerAsking::Nothing || !peer.is_available() {
return; return;
} }
if self.state == SyncState::Waiting { if self.state == SyncState::Waiting {
@ -727,7 +789,9 @@ impl ChainSync {
// Request subchain headers // Request subchain headers
trace!(target: "sync", "Starting sync with better chain"); trace!(target: "sync", "Starting sync with better chain");
let last = self.last_imported_hash.clone(); let last = self.last_imported_hash.clone();
self.request_headers_by_hash(io, peer_id, &last, SUBCHAIN_SIZE, MAX_HEADERS_TO_REQUEST - 1, false, PeerAsking::Heads); // Request MAX_HEADERS_TO_REQUEST - 2 headers apart so that
// MAX_HEADERS_TO_REQUEST would include headers for neighbouring subchains
self.request_headers_by_hash(io, peer_id, &last, SUBCHAIN_SIZE, MAX_HEADERS_TO_REQUEST - 2, false, PeerAsking::Heads);
}, },
SyncState::Blocks | SyncState::NewBlocks => { SyncState::Blocks | SyncState::NewBlocks => {
if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown { if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown {
@ -895,6 +959,17 @@ impl ChainSync {
.asking_hash = Some(h.clone()); .asking_hash = Some(h.clone());
} }
/// Request headers from a peer by block number
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn request_headers_by_number(&mut self, sync: &mut SyncIo, peer_id: PeerId, n: BlockNumber, count: usize, skip: usize, reverse: bool, asking: PeerAsking) {
trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}", peer_id, count, n);
let mut rlp = RlpStream::new_list(4);
rlp.append(&n);
rlp.append(&count);
rlp.append(&skip);
rlp.append(&if reverse {1u32} else {0u32});
self.send_request(sync, peer_id, asking, GET_BLOCK_HEADERS_PACKET, rlp.out());
}
/// Request block bodies from a peer /// Request block bodies from a peer
fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>) { fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>) {
let mut rlp = RlpStream::new_list(hashes.len()); let mut rlp = RlpStream::new_list(hashes.len());
@ -908,6 +983,7 @@ impl ChainSync {
/// Reset peer status after request is complete. /// Reset peer status after request is complete.
fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool { fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool {
let peer = self.peers.get_mut(&peer_id).unwrap(); let peer = self.peers.get_mut(&peer_id).unwrap();
peer.expired = false;
if peer.asking != asking { if peer.asking != asking {
trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking);
peer.asking = PeerAsking::Nothing; peer.asking = PeerAsking::Nothing;
@ -947,6 +1023,9 @@ impl ChainSync {
if !io.is_chain_queue_empty() { if !io.is_chain_queue_empty() {
return Ok(()); return Ok(());
} }
if self.peers.get(&peer_id).map_or(false, |p| p.confirmed) {
trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id);
}
let mut item_count = r.item_count(); let mut item_count = r.item_count();
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count); trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
@ -1178,7 +1257,13 @@ impl ChainSync {
let tick = time::precise_time_s(); let tick = time::precise_time_s();
let mut aborting = Vec::new(); let mut aborting = Vec::new();
for (peer_id, peer) in &self.peers { for (peer_id, peer) in &self.peers {
if peer.asking != PeerAsking::Nothing && (tick - peer.ask_time) > CONNECTION_TIMEOUT_SEC { let timeout = match peer.asking {
PeerAsking::BlockHeaders | PeerAsking::Heads => (tick - peer.ask_time) > HEADERS_TIMEOUT_SEC,
PeerAsking::BlockBodies => (tick - peer.ask_time) > BODIES_TIMEOUT_SEC,
PeerAsking::Nothing => false,
PeerAsking::ForkHeader => (tick - peer.ask_time) > FORK_HEADER_TIMEOUT_SEC,
};
if timeout {
trace!(target:"sync", "Timeout {}", peer_id); trace!(target:"sync", "Timeout {}", peer_id);
io.disconnect_peer(*peer_id); io.disconnect_peer(*peer_id);
aborting.push(*peer_id); aborting.push(*peer_id);
@ -1593,6 +1678,8 @@ mod tests {
asking_blocks: Vec::new(), asking_blocks: Vec::new(),
asking_hash: None, asking_hash: None,
ask_time: 0f64, ask_time: 0f64,
expired: false,
confirmed: false,
}); });
sync sync
} }

View File

@ -72,8 +72,9 @@ use std::ops::*;
use std::sync::*; use std::sync::*;
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId}; use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
use util::TimerToken; use util::TimerToken;
use util::{U256, ONE_U256}; use util::{U256, ONE_U256, H256};
use ethcore::client::Client; use ethcore::client::Client;
use ethcore::header::BlockNumber;
use ethcore::service::{SyncMessage, NetSyncMessage}; use ethcore::service::{SyncMessage, NetSyncMessage};
use io::NetSyncIo; use io::NetSyncIo;
use util::io::IoChannel; use util::io::IoChannel;
@ -93,6 +94,8 @@ pub struct SyncConfig {
pub max_download_ahead_blocks: usize, pub max_download_ahead_blocks: usize,
/// Network ID /// Network ID
pub network_id: U256, pub network_id: U256,
/// Optional fork block id
pub fork_block: Option<(BlockNumber, H256)>,
} }
impl Default for SyncConfig { impl Default for SyncConfig {
@ -100,6 +103,7 @@ impl Default for SyncConfig {
SyncConfig { SyncConfig {
max_download_ahead_blocks: 20000, max_download_ahead_blocks: 20000,
network_id: ONE_U256, network_id: ONE_U256,
fork_block: None,
} }
} }
} }

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::*; use util::*;
use ethcore::client::{BlockChainClient, BlockID, EachBlockWith}; use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith};
use chain::{SyncState}; use chain::{SyncState};
use super::helpers::*; use super::helpers::*;
@ -95,6 +95,25 @@ fn forked() {
assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain); assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain);
} }
#[test]
fn net_hard_fork() {
::env_logger::init().ok();
let ref_client = TestBlockChainClient::new();
ref_client.add_blocks(50, EachBlockWith::Uncle);
{
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap())));
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100);
}
{
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap())));
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing);
net.sync();
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0);
}
}
#[test] #[test]
fn restart() { fn restart() {
let mut net = TestNet::new(3); let mut net = TestNet::new(3);
@ -108,7 +127,7 @@ fn restart() {
net.restart_peer(0); net.restart_peer(0);
let status = net.peer(0).sync.read().unwrap().status(); let status = net.peer(0).sync.read().unwrap().status();
assert_eq!(status.state, SyncState::ChainHead); assert_eq!(status.state, SyncState::Idle);
} }
#[test] #[test]

View File

@ -16,6 +16,7 @@
use util::*; use util::*;
use ethcore::client::{TestBlockChainClient, BlockChainClient}; use ethcore::client::{TestBlockChainClient, BlockChainClient};
use ethcore::header::BlockNumber;
use io::SyncIo; use io::SyncIo;
use chain::ChainSync; use chain::ChainSync;
use ::SyncConfig; use ::SyncConfig;
@ -89,13 +90,19 @@ pub struct TestNet {
impl TestNet { impl TestNet {
pub fn new(n: usize) -> TestNet { pub fn new(n: usize) -> TestNet {
Self::new_with_fork(n, None)
}
pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet {
let mut net = TestNet { let mut net = TestNet {
peers: Vec::new(), peers: Vec::new(),
started: false, started: false,
}; };
for _ in 0..n { for _ in 0..n {
let chain = TestBlockChainClient::new(); let chain = TestBlockChainClient::new();
let sync = ChainSync::new(SyncConfig::default(), &chain); let mut config = SyncConfig::default();
config.fork_block = fork;
let sync = ChainSync::new(config, &chain);
net.peers.push(TestPeer { net.peers.push(TestPeer {
sync: RwLock::new(sync), sync: RwLock::new(sync),
chain: chain, chain: chain,

View File

@ -77,7 +77,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
/// Readable IO handler. Called when there is some data to be read. /// Readable IO handler. Called when there is some data to be read.
pub fn readable(&mut self) -> io::Result<Option<Bytes>> { pub fn readable(&mut self) -> io::Result<Option<Bytes>> {
if self.rec_size == 0 || self.rec_buf.len() >= self.rec_size { if self.rec_size == 0 || self.rec_buf.len() >= self.rec_size {
warn!(target:"network", "Unexpected connection read"); return Ok(None);
} }
let sock_ref = <Socket as Read>::by_ref(&mut self.socket); let sock_ref = <Socket as Read>::by_ref(&mut self.socket);
loop { loop {

View File

@ -128,31 +128,27 @@ impl Handshake {
/// Readable IO handler. Drives the state change. /// Readable IO handler. Drives the state change.
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone { pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone {
if !self.expired() { if !self.expired() {
match self.state { while let Some(data) = try!(self.connection.readable()) {
HandshakeState::New => {} match self.state {
HandshakeState::ReadingAuth => { HandshakeState::New => {},
if let Some(data) = try!(self.connection.readable()) { HandshakeState::StartSession => {},
HandshakeState::ReadingAuth => {
try!(self.read_auth(io, host.secret(), &data)); try!(self.read_auth(io, host.secret(), &data));
}; },
}, HandshakeState::ReadingAuthEip8 => {
HandshakeState::ReadingAuthEip8 => {
if let Some(data) = try!(self.connection.readable()) {
try!(self.read_auth_eip8(io, host.secret(), &data)); try!(self.read_auth_eip8(io, host.secret(), &data));
}; },
}, HandshakeState::ReadingAck => {
HandshakeState::ReadingAck => {
if let Some(data) = try!(self.connection.readable()) {
try!(self.read_ack(host.secret(), &data)); try!(self.read_ack(host.secret(), &data));
}; },
}, HandshakeState::ReadingAckEip8 => {
HandshakeState::ReadingAckEip8 => {
if let Some(data) = try!(self.connection.readable()) {
try!(self.read_ack_eip8(host.secret(), &data)); try!(self.read_ack_eip8(host.secret(), &data));
}; },
}, }
HandshakeState::StartSession => { if self.state == HandshakeState::StartSession {
io.clear_timer(self.connection.token).ok(); io.clear_timer(self.connection.token).ok();
}, break;
}
} }
} }
Ok(()) Ok(())
@ -178,9 +174,9 @@ impl Handshake {
/// Parse, validate and confirm auth message /// Parse, validate and confirm auth message
fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone { fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
trace!(target:"network", "Received handshake auth from {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Received handshake auth from {:?}", self.connection.remote_addr_str());
if data.len() != V4_AUTH_PACKET_SIZE { if data.len() != V4_AUTH_PACKET_SIZE {
debug!(target:"net", "Wrong auth packet size"); debug!(target: "network", "Wrong auth packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
} }
self.auth_cipher = data.to_vec(); self.auth_cipher = data.to_vec();
@ -197,7 +193,7 @@ impl Handshake {
// Try to interpret as EIP-8 packet // Try to interpret as EIP-8 packet
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2; let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
if total < V4_AUTH_PACKET_SIZE { if total < V4_AUTH_PACKET_SIZE {
debug!(target:"net", "Wrong EIP8 auth packet size"); debug!(target: "network", "Wrong EIP8 auth packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
} }
let rest = total - data.len(); let rest = total - data.len();
@ -209,7 +205,7 @@ impl Handshake {
} }
fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone { fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
self.auth_cipher.extend_from_slice(data); self.auth_cipher.extend_from_slice(data);
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..])); let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
let rlp = UntrustedRlp::new(&auth); let rlp = UntrustedRlp::new(&auth);
@ -224,9 +220,9 @@ impl Handshake {
/// Parse and validate ack message /// Parse and validate ack message
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received handshake ack from {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Received handshake ack from {:?}", self.connection.remote_addr_str());
if data.len() != V4_ACK_PACKET_SIZE { if data.len() != V4_ACK_PACKET_SIZE {
debug!(target:"net", "Wrong ack packet size"); debug!(target: "network", "Wrong ack packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
} }
self.ack_cipher = data.to_vec(); self.ack_cipher = data.to_vec();
@ -240,7 +236,7 @@ impl Handshake {
// Try to interpret as EIP-8 packet // Try to interpret as EIP-8 packet
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2; let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
if total < V4_ACK_PACKET_SIZE { if total < V4_ACK_PACKET_SIZE {
debug!(target:"net", "Wrong EIP8 ack packet size"); debug!(target: "network", "Wrong EIP8 ack packet size");
return Err(From::from(NetworkError::BadProtocol)); return Err(From::from(NetworkError::BadProtocol));
} }
let rest = total - data.len(); let rest = total - data.len();
@ -252,7 +248,7 @@ impl Handshake {
} }
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> { fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
self.ack_cipher.extend_from_slice(data); self.ack_cipher.extend_from_slice(data);
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..])); let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
let rlp = UntrustedRlp::new(&ack); let rlp = UntrustedRlp::new(&ack);
@ -265,7 +261,7 @@ impl Handshake {
/// Sends auth message /// Sends auth message
fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), UtilError> where Message: Send + Clone { fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), UtilError> where Message: Send + Clone {
trace!(target:"network", "Sending handshake auth to {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Sending handshake auth to {:?}", self.connection.remote_addr_str());
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
let len = data.len(); let len = data.len();
{ {
@ -292,7 +288,7 @@ impl Handshake {
/// Sends ack message /// Sends ack message
fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone { fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
trace!(target:"network", "Sending handshake ack to {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Sending handshake ack to {:?}", self.connection.remote_addr_str());
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
let len = data.len(); let len = data.len();
{ {
@ -311,7 +307,7 @@ impl Handshake {
/// Sends EIP8 ack message /// Sends EIP8 ack message
fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone { fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str()); trace!(target: "network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str());
let mut rlp = RlpStream::new_list(3); let mut rlp = RlpStream::new_list(3);
rlp.append(self.ecdhe.public()); rlp.append(self.ecdhe.public());
rlp.append(&self.nonce); rlp.append(&self.nonce);