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]]
name = "json-ipc-server"
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 = [
"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)",

View File

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

View File

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

View File

@ -58,8 +58,8 @@ impl Account {
nonce: pod.nonce,
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(pod.code.sha3()),
code_cache: pod.code
code_hash: pod.code.as_ref().map(|c| c.sha3()),
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.
use common::*;
use ethjson;
use types::executed::CallType;
/// Transaction value
#[derive(Clone, Debug)]
@ -58,7 +59,10 @@ pub struct ActionParams {
/// Code being executed.
pub code: Option<Bytes>,
/// Input data.
pub data: Option<Bytes>
pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
}
impl Default for ActionParams {
@ -73,16 +77,18 @@ impl Default for ActionParams {
gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()),
code: None,
data: None
data: None,
call_type: CallType::None,
}
}
}
impl From<ethjson::vm::Transaction> for ActionParams {
fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams {
code_address: Address::new(),
address: t.address.into(),
address: address,
sender: t.sender.into(),
origin: t.origin.into(),
code: Some(t.code.into()),
@ -90,6 +96,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
gas: t.gas.into(),
gas_price: t.gas_price.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 state::*;
use verification::PreverifiedBlock;
use trace::Trace;
use trace::FlatTrace;
use evm::Factory as EvmFactory;
/// A block, encoded as it is on the block chain.
@ -78,7 +78,7 @@ pub struct ExecutedBlock {
receipts: Vec<Receipt>,
transactions_set: HashSet<H256>,
state: State,
traces: Option<Vec<Trace>>,
traces: Option<Vec<Vec<FlatTrace>>>,
}
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
@ -94,7 +94,7 @@ pub struct BlockRefMut<'a> {
/// State.
pub state: &'a mut State,
/// 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.
@ -110,7 +110,7 @@ pub struct BlockRef<'a> {
/// State.
pub state: &'a State,
/// Traces.
pub traces: &'a Option<Vec<Trace>>,
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
}
impl ExecutedBlock {
@ -171,7 +171,7 @@ pub trait IsBlock {
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
/// 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.
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.base.transactions.push(t);
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);
Ok(&self.block.receipts.last().unwrap())
}

View File

@ -48,6 +48,9 @@ use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use trace::FlatTransactionTraces;
// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory;
@ -289,8 +292,6 @@ impl Client {
let _timer = PerfTimer::new("import_verified_blocks");
let blocks = self.block_queue.drain(max_blocks_to_import);
let original_best = self.chain_info().best_block_hash;
for block in blocks {
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
}
@ -361,8 +358,13 @@ impl Client {
};
// Commit results
let receipts = block.receipts().clone();
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
let receipts = block.receipts().to_owned();
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
// 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)
let route = self.chain.insert_block(block_data, receipts);
self.tracedb.import(TraceImportRequest {
traces: traces,
traces: traces.into(),
block_hash: hash.clone(),
block_number: number,
enacted: route.enacted.clone(),
@ -840,8 +842,6 @@ impl MiningBlockChainClient for Client {
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_sealed_block");
let original_best = self.chain_info().best_block_hash;
let h = block.header().hash();
let number = block.header().number();
@ -862,10 +862,6 @@ impl MiningBlockChainClient for Client {
})).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)
}
}

View File

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

View File

@ -20,6 +20,7 @@ use common::*;
use super::instructions as instructions;
use super::instructions::{Instruction, get_info};
use std::marker::Copy;
use types::executed::CallType;
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))]
@ -648,16 +649,16 @@ impl Interpreter {
});
// 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 => {
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 => {
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))
};
@ -672,7 +673,7 @@ impl Interpreter {
// and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_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 {

View File

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

View File

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

View File

@ -21,6 +21,7 @@ use engine::*;
use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*;
use types::executed::CallType;
use trace::{Tracer, VMTracer};
/// 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),
code: Some(code.to_vec()),
data: None,
call_type: CallType::None,
};
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>,
data: &[u8],
code_address: &Address,
output: &mut [u8]
output: &mut [u8],
call_type: CallType
) -> MessageCallResult {
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,
code: self.state.code(code_address),
data: Some(data.to_vec()),
call_type: call_type,
};
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);
self.state.transfer_balance(&address, refund_address, &balance);
}
self.tracer.trace_suicide(address, balance, refund_address.clone());
self.substate.suicides.insert(address);
}
@ -300,6 +306,7 @@ mod tests {
use tests::helpers::*;
use super::*;
use trace::{NoopTracer, NoopVMTracer};
use types::executed::CallType;
fn get_test_origin() -> OriginInfo {
OriginInfo {
@ -418,7 +425,9 @@ mod tests {
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
&[],
&Address::new(),
&mut output);
&mut output,
CallType::Call
);
}
#[test]

View File

@ -22,6 +22,7 @@ use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*;
use substate::*;
use types::executed::CallType;
use tests::helpers::*;
use ethjson;
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,
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.callcreates.push(CallCreate {
data: data.to_vec(),
destination: Some(receive_address.clone()),

View File

@ -15,7 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rayon::prelude::*;
use std::sync::atomic::AtomicBool;
use std::time::{Instant, Duration};
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.
pub struct Miner {
// NOTE [ToDr] When locking always lock in this order!
transaction_queue: Mutex<TransactionQueue>,
sealing_work: Mutex<UsingQueue<ClosedBlock>>,
sealing_work: Mutex<SealingWork>,
// for sealing...
options: MinerOptions,
sealing_enabled: AtomicBool,
next_allowed_reseal: Mutex<Instant>,
sealing_block_last_request: Mutex<u64>,
gas_range_target: RwLock<(U256, U256)>,
@ -113,10 +117,9 @@ impl Miner {
Miner {
transaction_queue: Mutex::new(TransactionQueue::new()),
options: Default::default(),
sealing_enabled: AtomicBool::new(false),
next_allowed_reseal: Mutex::new(Instant::now()),
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())),
author: RwLock::new(Address::default()),
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 };
Arc::new(Miner {
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()),
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())),
author: RwLock::new(Address::default()),
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.
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.
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.
@ -172,7 +174,7 @@ impl Miner {
let (transactions, mut open_block, original_work_hash) = {
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
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();
/*
// 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, 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) => {
trace!(target: "miner", "Already have previous work; updating and returning");
// add transactions to old_block
@ -273,7 +275,7 @@ impl Miner {
let (work, is_new) = {
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());
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());
@ -281,16 +283,16 @@ impl Miner {
let number = block.block().fields().header.number();
let difficulty = *block.block().fields().header.difficulty();
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 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)
} else {
(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)
};
if is_new {
@ -307,14 +309,22 @@ impl Miner {
/// Returns true if we had to prepare new pending block
fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool {
trace!(target: "miner", "enable_and_prepare_sealing: entering");
let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some();
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
if !have_work {
let prepare_new = {
let mut sealing_work = self.sealing_work.lock().unwrap();
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. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
self.prepare_sealing(chain);
}
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;
}
// Return if
!have_work
// Return if we restarted
prepare_new
}
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 {
transactions_in_pending_queue: status.pending,
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> {
let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() {
match sealing_work.queue.peek_last_ref() {
Some(work) => {
let block = work.block();
@ -417,7 +427,7 @@ impl MinerService for Miner {
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
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),
|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 {
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),
|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 {
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> {
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) {
@ -582,8 +592,8 @@ impl MinerService for Miner {
let queue = self.transaction_queue.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)
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
true => sw.peek_last_ref(),
let sealing_set = match sw.enabled {
true => sw.queue.peek_last_ref(),
false => None,
};
match (&self.options.pending_set, sealing_set) {
@ -595,8 +605,8 @@ impl MinerService for Miner {
fn pending_transactions_hashes(&self) -> Vec<H256> {
let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
true => sw.peek_last_ref(),
let sealing_set = match sw.enabled {
true => sw.queue.peek_last_ref(),
false => None,
};
match (&self.options.pending_set, sealing_set) {
@ -608,8 +618,8 @@ impl MinerService for Miner {
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
true => sw.peek_last_ref(),
let sealing_set = match sw.enabled {
true => sw.queue.peek_last_ref(),
false => None,
};
match (&self.options.pending_set, sealing_set) {
@ -619,7 +629,8 @@ impl MinerService for Miner {
}
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)) => {
let hashes = pending.transactions()
.iter()
@ -638,27 +649,43 @@ impl MinerService for Miner {
}
fn update_sealing(&self, chain: &MiningBlockChainClient) {
if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
let current_no = chain.chain_info().best_block_number;
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
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;
trace!(target: "miner", "update_sealing");
let requires_reseal = {
let mut sealing_work = self.sealing_work.lock().unwrap();
if sealing_work.enabled {
trace!(target: "miner", "update_sealing: sealing enabled");
let current_no = chain.chain_info().best_block_number;
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
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", "Miner sleeping (current {}, last {})", current_no, last_request);
self.sealing_enabled.store(false, atomic::Ordering::Relaxed);
self.sealing_work.lock().unwrap().reset();
trace!(target: "miner", "update_sealing: should_disable_sealing={}; current_no={}, last_request={}", should_disable_sealing, current_no, last_request);
if should_disable_sealing {
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 {
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period;
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.prepare_sealing(chain);
// sealing is disabled.
false
}
};
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);
trace!(target: "miner", "map_sealing_work: sealing prepared");
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()));
ret.map(f)
}
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(|_| {
warn!(target: "miner", "Mined solution rejected: Invalid.");
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]) {
trace!(target: "miner", "chain_new_blocks");
fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
let block = chain
.block(BlockID::Hash(*hash))
@ -746,11 +775,13 @@ impl MinerService for Miner {
});
}
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.update_sealing(chain);
if enacted.len() > 0 {
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.update_sealing(chain);
}
}
}

View File

@ -28,8 +28,8 @@ pub struct PodAccount {
pub balance: U256,
/// The nonce of the account.
pub nonce: U256,
/// The code of the account.
pub code: Bytes,
/// The code of the account or `None` in the special case that it is unknown.
pub code: Option<Bytes>,
/// The storage of the account.
pub storage: BTreeMap<H256, H256>,
}
@ -38,7 +38,7 @@ impl PodAccount {
/// Construct new object.
#[cfg(test)]
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.
@ -48,7 +48,7 @@ impl PodAccount {
balance: *acc.balance(),
nonce: *acc.nonce(),
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.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(&self.code.sha3());
stream.append(&self.code.as_ref().unwrap_or(&vec![]).sha3());
stream.out()
}
/// Place additional data into given hash DB.
pub fn insert_additional(&self, db: &mut AccountDBMut) {
if !self.code.is_empty() {
db.insert(&self.code);
match self.code {
Some(ref c) if !c.is_empty() => { db.insert(c); }
_ => {}
}
let mut r = H256::new();
let mut t = SecTrieDBMut::new(db, &mut r);
@ -80,7 +81,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
PodAccount {
balance: a.balance.into(),
nonce: a.nonce.into(),
code: a.code.into(),
code: Some(a.code.into()),
storage: a.storage.into_iter().map(|(key, value)| {
let key: U256 = key.into();
let value: U256 = value.into();
@ -95,7 +96,7 @@ impl From<ethjson::spec::Account> for PodAccount {
PodAccount {
balance: a.balance.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()
}
}
@ -103,7 +104,13 @@ impl From<ethjson::spec::Account> for PodAccount {
impl fmt::Display for PodAccount {
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 {
balance: Diff::Born(x.balance),
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(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance),
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(),
}),
(Some(pre), Some(post)) => {
@ -130,7 +137,10 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
let r = AccountDiff {
balance: Diff::new(pre.balance, post.balance),
nonce: Diff::new(pre.nonce, post.nonce),
code: Diff::new(pre.code.clone(), post.code.clone()),
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|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
@ -156,7 +166,7 @@ mod test {
#[test]
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(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(69.into()),
@ -168,8 +178,8 @@ mod test {
#[test]
fn basic() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]};
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(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 {
balance: Diff::Changed(69.into(), 42.into()),
nonce: Diff::Changed(0.into(), 1.into()),
@ -180,8 +190,8 @@ mod test {
#[test]
fn code() {
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]};
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]};
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), 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 {
balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()),
@ -195,13 +205,13 @@ mod test {
let a = PodAccount {
balance: 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]
};
let b = PodAccount {
balance: 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]
};
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {

View File

@ -38,6 +38,8 @@ pub struct CommonParams {
pub network_id: U256,
/// Minimum gas limit.
pub min_gas_limit: U256,
/// Fork block to check.
pub fork_block: Option<(BlockNumber, H256)>,
}
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(),
network_id: p.network_id.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.
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.
pub fn genesis_header(&self) -> Header {
Header {

View File

@ -19,7 +19,7 @@ use engine::Engine;
use executive::{Executive, TransactOptions};
use evm::Factory as EvmFactory;
use account_db::*;
use trace::Trace;
use trace::FlatTrace;
use pod_account::*;
use pod_state::{self, PodState};
use types::state_diff::StateDiff;
@ -29,7 +29,7 @@ pub struct ApplyOutcome {
/// The receipt for the applied transaction.
pub receipt: Receipt,
/// The trace for the applied transaction, if None if tracing is disabled.
pub trace: Option<Trace>,
pub trace: Vec<FlatTrace>,
}
/// Result type for the execution ("application") of a transaction.
@ -217,7 +217,7 @@ impl State {
/// Reset the code of account `a` so that it is `code`.
pub fn reset_code(&mut self, a: &Address, code: Bytes) {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code);
}
}
/// Execute a given transaction.
/// This will change the state accordingly.
@ -389,7 +389,8 @@ use spec::*;
use transaction::*;
use util::log::init_log;
use trace::trace;
use trace::trace::{Trace};
use trace::FlatTrace;
use types::executed::CallType;
#[test]
fn should_apply_create_transaction() {
@ -414,8 +415,9 @@ fn should_apply_create_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(),
@ -427,8 +429,7 @@ fn should_apply_create_transaction() {
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
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);
}
@ -475,8 +476,8 @@ fn should_trace_failed_create_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Create(trace::Create {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
value: 100.into(),
@ -484,8 +485,8 @@ fn should_trace_failed_create_transaction() {
init: vec![91, 96, 0, 86],
}),
result: trace::Res::FailedCreate,
subs: vec![]
});
subtraces: 0
}];
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()));
let vm_factory = Default::default();
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(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
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()));
let vm_factory = Default::default();
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(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(0),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -598,21 +601,24 @@ fn should_trace_call_transaction_to_builtin() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
assert_eq!(result.trace, Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: "0000000000000000000000000000000000000001".into(),
value: 0.into(),
gas: 79_000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3000),
output: vec![]
}),
subs: vec![]
}));
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
#[test]
@ -639,21 +645,23 @@ fn should_not_trace_subcall_transaction_to_builtin() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 0.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(28_061),
output: vec![]
}),
subs: vec![]
});
subtraces: 0,
}];
assert_eq!(result.trace, expected_trace);
}
@ -682,21 +690,38 @@ fn should_not_trace_callcode() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &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 {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 0.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(64),
gas_used: 64.into(),
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);
}
@ -728,21 +753,38 @@ fn should_not_trace_delegatecall() {
let vm_factory = Default::default();
let result = state.apply(&info, engine.deref(), &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 {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 0.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(61),
output: vec![]
}),
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);
}
@ -770,20 +812,19 @@ fn should_trace_failed_call_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
subs: vec![]
});
println!("trace: {:?}", result.trace);
subtraces: 0,
}];
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()));
let vm_factory = Default::default();
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 {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -870,32 +914,34 @@ fn should_trace_call_with_basic_subcall_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 69.into(),
gas: 2300.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult::default()),
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 69.into(),
gas: 2300.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult::default()),
}];
assert_eq!(result.trace, expected_trace);
}
@ -924,21 +970,22 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761),
output: vec![]
}),
subs: vec![]
});
}];
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()));
let vm_factory = Default::default();
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 {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
}),
result: trace::Res::FailedCall,
subs: vec![]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
}];
assert_eq!(result.trace, expected_trace);
}
@ -1024,49 +1073,52 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(135),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69),
output: vec![]
}),
subs: vec![Trace {
depth: 2,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
}]
});
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69),
output: vec![]
}),
}, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
@ -1097,46 +1149,104 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
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 = Some(Trace {
depth: 0,
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
})
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
}),
result: trace::Res::FailedCall,
subs: vec![Trace {
depth: 2,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
subs: vec![]
}]
}]
});
from: 0xa.into(),
to: 0xb.into(),
value: 0.into(),
gas: 78934.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::FailedCall,
}, FlatTrace {
trace_address: vec![0, 0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Call(trace::Call {
from: 0xb.into(),
to: 0xc.into(),
value: 0.into(),
gas: 78868.into(),
call_type: CallType::Call,
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3),
output: vec![]
}),
}];
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_suicide() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = 1_000_000.into();
let engine = TestEngine::new(5);
let t = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3());
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
state.add_balance(&0xa.into(), &50.into());
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
let vm_factory = Default::default();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![]
}),
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Suicide(trace::Suicide {
address: 0xa.into(),
refund_address: 0xb.into(),
balance: 150.into(),
}),
result: trace::Res::None,
}];
assert_eq!(result.trace, expected_trace);
}

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

View File

@ -18,13 +18,53 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
use trace::{Tracer, VMTracer};
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer, FlatTrace};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)]
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 {
@ -40,59 +80,67 @@ impl Tracer for ExecutiveTracer {
Some(vec![])
}
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
// don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call {
return;
}
let trace = Trace {
depth: depth,
subs: subs,
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
trace_address: Default::default(),
subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::Call(CallResult {
gas_used: gas_used,
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
})
}),
};
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>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
result: Res::Create(CreateResult {
gas_used: gas_used,
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
address: address
})
}),
trace_address: Default::default(),
};
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) {
// don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call {
return;
}
let trace = Trace {
depth: depth,
subs: subs,
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
trace_address: Default::default(),
subtraces: top_level_subtraces(&subs),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
result: Res::FailedCall,
};
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>) {
let trace = Trace {
depth: depth,
subs: subs,
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
let trace = FlatTrace {
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
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);
}
@ -101,7 +149,7 @@ impl Tracer for ExecutiveTracer {
ExecutiveTracer::default()
}
fn traces(self) -> Vec<Trace> {
fn traces(self) -> Vec<FlatTrace> {
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.
use util::H256;
use header::BlockNumber;
use trace::BlockTraces;
use trace::FlatBlockTraces;
/// Traces import request.
pub struct ImportRequest {
/// Traces to import.
pub traces: BlockTraces,
pub traces: FlatBlockTraces,
/// Hash of traces block.
pub block_hash: H256,
/// Number of traces block.

View File

@ -16,22 +16,20 @@
//! Tracing
mod block;
mod bloom;
mod config;
mod db;
mod error;
mod executive_tracer;
pub mod flat;
mod import;
mod noop_tracer;
pub use types::trace_types::*;
pub use self::block::BlockTraces;
pub use self::config::{Config, Switch};
pub use self::db::TraceDB;
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::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter};
@ -59,9 +57,7 @@ pub trait Tracer: Send {
call: Option<Call>,
gas_used: U256,
output: Option<Bytes>,
depth: usize,
subs: Vec<Trace>,
delegate_call: bool
subs: Vec<FlatTrace>,
);
/// Stores trace create info.
@ -71,21 +67,23 @@ pub trait Tracer: Send {
gas_used: U256,
code: Option<Bytes>,
address: Address,
depth: usize,
subs: Vec<Trace>
subs: Vec<FlatTrace>
);
/// 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.
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.
fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces.
fn traces(self) -> Vec<Trace>;
fn traces(self) -> Vec<FlatTrace>;
}
/// Used by executive to build VM traces.

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::{Tracer, VMTracer};
use trace::trace::{Trace, Call, Create, VMTrace};
use trace::{Tracer, VMTracer, FlatTrace};
use trace::trace::{Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer;
@ -37,29 +37,32 @@ impl Tracer for NoopTracer {
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!(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!(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");
}
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");
}
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) {
}
fn subtracer(&self) -> Self {
NoopTracer
}
fn traces(self) -> Vec<Trace> {
fn traces(self) -> Vec<FlatTrace> {
vec![]
}
}

View File

@ -18,7 +18,8 @@
use util::numbers::*;
use util::Bytes;
use trace::{Trace, VMTrace};
use util::rlp::*;
use trace::{VMTrace, FlatTrace};
use types::log_entry::LogEntry;
use types::state_diff::StateDiff;
use ipc::binary::BinaryConvertError;
@ -26,6 +27,43 @@ use std::fmt;
use std::mem;
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.
#[derive(Debug, PartialEq, Clone)]
pub struct Executed {
@ -59,7 +97,7 @@ pub struct Executed {
/// Transaction output.
pub output: Bytes,
/// The trace of this transaction.
pub trace: Option<Trace>,
pub trace: Vec<FlatTrace>,
/// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>,
/// The state diff, if we traced it.
@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError {
/// Transaction execution result.
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 basic_types::LogBloom;
use trace::flat::FlatTrace;
use types::trace_types::trace::Action;
use types::trace_types::trace::{Action, Res};
use ipc::binary::BinaryConvertError;
use std::mem;
use std::collections::VecDeque;
@ -58,7 +58,7 @@ impl AddressesFilter {
true => vec![LogBloom::new()],
false => self.list.iter()
.map(|address| LogBloom::from_bloomed(&address.sha3()))
.collect()
.collect(),
}
}
@ -71,7 +71,7 @@ impl AddressesFilter {
.flat_map(|bloom| self.list.iter()
.map(|address| bloom.with_bloomed(&address.sha3()))
.collect::<Vec<_>>())
.collect()
.collect(),
}
}
}
@ -110,29 +110,40 @@ impl Filter {
/// Returns true if given trace matches the filter.
pub fn matches(&self, trace: &FlatTrace) -> bool {
match trace.action {
let action = match trace.action {
Action::Call(ref call) => {
let from_matches = self.from_address.matches(&call.from);
let to_matches = self.to_address.matches(&call.to);
from_matches && to_matches
},
}
Action::Create(ref create) => {
let from_matches = self.from_address.matches(&create.from);
let to_matches = self.to_address.matches_all();
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)]
mod tests {
use util::{FixedHash, Address, U256};
use util::{FixedHash, Address};
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::{Filter, AddressesFilter};
use basic_types::LogBloom;
use types::executed::CallType;
#[test]
fn empty_trace_filter_bloom_possibilities() {
@ -270,14 +281,15 @@ mod tests {
let trace = FlatTrace {
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
from: 1.into(),
to: 2.into(),
value: 3.into(),
gas: 4.into(),
input: vec![0x5],
call_type: CallType::Call,
}),
result: Res::FailedCall,
trace_address: vec![0],
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
};
@ -288,5 +300,48 @@ mod tests {
assert!(f4.matches(&trace));
assert!(f5.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
pub mod filter;
pub mod flat;
pub mod trace;
pub mod localized;

View File

@ -21,6 +21,7 @@ use util::rlp::*;
use util::sha3::Hashable;
use action_params::ActionParams;
use basic_types::LogBloom;
use types::executed::CallType;
use ipc::binary::BinaryConvertError;
use std::mem;
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.
#[derive(Debug, Clone, PartialEq, Binary)]
pub struct Call {
@ -100,6 +108,8 @@ pub struct Call {
pub gas: U256,
/// The input data provided to the call.
pub input: Bytes,
/// The type of the call.
pub call_type: CallType,
}
impl From<ActionParams> for Call {
@ -110,18 +120,20 @@ impl From<ActionParams> for Call {
value: p.value.value(),
gas: p.gas,
input: p.data.unwrap_or_else(Vec::new),
call_type: p.call_type,
}
}
}
impl Encodable for Call {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(5);
s.begin_list(6);
s.append(&self.from);
s.append(&self.to);
s.append(&self.value);
s.append(&self.gas);
s.append(&self.input);
s.append(&self.call_type);
}
}
@ -134,6 +146,7 @@ impl Decodable for Call {
value: try!(d.val_at(2)),
gas: try!(d.val_at(3)),
input: try!(d.val_at(4)),
call_type: try!(d.val_at(5)),
};
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.
#[derive(Debug, Clone, PartialEq, Binary)]
pub enum Action {
@ -212,6 +267,8 @@ pub enum Action {
Call(Call),
/// It's a create action.
Create(Create),
/// Suicide.
Suicide(Suicide),
}
impl Encodable for Action {
@ -225,6 +282,10 @@ impl Encodable for Action {
Action::Create(ref create) => {
s.append(&1u8);
s.append(create);
},
Action::Suicide(ref suicide) => {
s.append(&2u8);
s.append(suicide);
}
}
}
@ -237,6 +298,7 @@ impl Decodable for Action {
match action_type {
0 => d.val_at(1).map(Action::Call),
1 => d.val_at(1).map(Action::Create),
2 => d.val_at(2).map(Action::Suicide),
_ => Err(DecoderError::Custom("Invalid action type.")),
}
}
@ -248,6 +310,7 @@ impl Action {
match *self {
Action::Call(ref call) => call.bloom(),
Action::Create(ref create) => create.bloom(),
Action::Suicide(ref suicide) => suicide.bloom(),
}
}
}
@ -263,6 +326,8 @@ pub enum Res {
FailedCall,
/// Failed create.
FailedCreate,
/// None
None,
}
impl Encodable for Res {
@ -285,6 +350,10 @@ impl Encodable for Res {
Res::FailedCreate => {
s.begin_list(1);
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),
2 => Ok(Res::FailedCall),
3 => Ok(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")),
}
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A trace; includes a description of the action being traced and sub traces of each interior action.
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.
impl Res {
/// Returns result bloom.
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 {
fn size(&self) -> usize {
match T::len_params() {

View File

@ -17,6 +17,7 @@
//! Spec params deserialization.
use uint::Uint;
use hash::H256;
/// Spec params.
#[derive(Debug, PartialEq, Deserialize)]
@ -33,6 +34,12 @@ pub struct Params {
/// Minimum gas limit.
#[serde(rename="minGasLimit")]
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)]

View File

@ -75,7 +75,9 @@ mod tests {
"frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"
"networkID" : "0x2",
"forkBlock": "0xffffffffffffffff",
"forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"genesis": {
"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| {
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
}

View File

@ -18,13 +18,13 @@
use std::sync::{Weak, Arc};
use jsonrpc_core::*;
use std::collections::BTreeMap;
use util::H256;
use util::rlp::{UntrustedRlp, View};
use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
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.
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())),
};
let signed = try!(self.sign_call(request));
let r = take_weak!(self.client).call(&signed, analytics);
if let Ok(executed) = r {
// TODO maybe add other stuff to this?
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 {
ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap());
}
if let Some(state_diff) = executed.state_diff {
ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap());
}
return Ok(Value::Object(ret))
match take_weak!(self.client).call(&signed, analytics) {
Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null),
}
})
}
fn raw_transaction(&self, params: Params) -> Result<Value, Error> {
trace!(target: "jsonrpc", "call: {:?}", params);
from_params::<(Bytes, Vec<String>)>(params)
.and_then(|(raw_transaction, flags)| {
let raw_transaction = raw_transaction.to_vec();
let analytics = CallAnalytics {
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);
}
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#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
@ -430,7 +430,7 @@ fn rpc_eth_call() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -465,7 +465,7 @@ fn rpc_eth_call_default_block() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -499,7 +499,7 @@ fn rpc_eth_estimate_gas() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: None,
state_diff: None,
});
@ -534,7 +534,7 @@ fn rpc_eth_estimate_gas_default_block() {
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: None,
trace: vec![],
vm_trace: 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.
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.
fn to_delegate(self) -> IoDelegate<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_block", Traces::block_traces);
delegate.add_method("trace_call", Traces::call);
delegate.add_method("trace_rawTransaction", Traces::raw_transaction);
delegate
}

View File

@ -103,7 +103,7 @@ mod tests {
fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]);
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 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::call_request::CallRequest;
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;

View File

@ -18,11 +18,13 @@ use std::collections::BTreeMap;
use util::{Address, U256, H256, Uint};
use serde::{Serialize, Serializer};
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::state_diff;
use ethcore::account_diff;
use v1::types::Bytes;
use v1::types::{Bytes};
use ethcore::client::Executed;
use ethcore::executed;
#[derive(Debug, Serialize)]
/// A diff of some chunk of memory.
@ -193,6 +195,7 @@ impl From<account_diff::AccountDiff> for AccountDiff {
}
}
#[derive(Debug)]
/// Serde-friendly `StateDiff` shadow.
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
#[derive(Debug, Serialize)]
pub struct Call {
@ -246,6 +277,9 @@ pub struct Call {
gas: U256,
/// Input data
input: Bytes,
/// The type of the call.
#[serde(rename="callType")]
call_type: CallType,
}
impl From<trace::Call> for Call {
@ -256,6 +290,29 @@ impl From<trace::Call> for Call {
value: c.value,
gas: c.gas,
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
#[serde(rename="create")]
Create(Create),
/// Suicide
#[serde(rename="suicide")]
Suicide(Suicide),
}
impl From<trace::Action> for Action {
fn from(c: trace::Action) -> Self {
match c {
trace::Action::Call(call) => Action::Call(Call::from(call)),
trace::Action::Create(create) => Action::Create(Create::from(create)),
trace::Action::Call(call) => Action::Call(call.into()),
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
#[serde(rename="failedCreate")]
FailedCreate,
/// None
#[serde(rename="none")]
None,
}
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::FailedCall => Res::FailedCall,
trace::Res::FailedCreate => Res::FailedCreate,
trace::Res::None => Res::None,
}
}
}
@ -393,23 +458,50 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
/// Trace
#[derive(Debug, Serialize)]
pub struct Trace {
/// Depth within the call trace tree.
depth: usize,
/// Trace address
#[serde(rename="traceAddress")]
trace_address: Vec<U256>,
/// Subtraces
subtraces: U256,
/// Action
action: Action,
/// Result
result: Res,
/// Subtraces
subtraces: Vec<Trace>,
}
impl From<EthTrace> for Trace {
fn from(t: EthTrace) -> Self {
impl From<FlatTrace> for Trace {
fn from(t: FlatTrace) -> Self {
Trace {
depth: t.depth.into(),
trace_address: t.trace_address.into_iter().map(Into::into).collect(),
subtraces: t.subtraces.into(),
action: t.action.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 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]
fn test_trace_serialize() {
let t = LocalizedTrace {
@ -431,6 +535,7 @@ mod tests {
value: U256::from(6),
gas: U256::from(7),
input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}),
result: Res::Call(CallResult {
gas_used: U256::from(8),
@ -444,7 +549,7 @@ mod tests {
block_hash: H256::from(14),
};
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]
@ -520,6 +625,7 @@ mod tests {
value: U256::from(3),
gas: U256::from(4),
input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}), Action::Create(Create {
from: Address::from(5),
value: U256::from(6),
@ -528,7 +634,7 @@ mod tests {
})];
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]

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::numbers::*;
use util::rlp::encode;
use ethcore::contract_address;
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, OptionalValue};
@ -50,6 +51,8 @@ pub struct Transaction {
pub input: Bytes,
/// Creates contract
pub creates: OptionalValue<Address>,
/// Raw transaction data
pub raw: Bytes,
}
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::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::Call(_) => OptionalValue::Null,
},
raw: encode(&t).to_vec().into(),
}
}
}
@ -111,7 +116,7 @@ mod tests {
fn test_transaction_serialize() {
let t = Transaction::default();
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 {
match self.blocks.get(&head) {
Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => {
self.downloading_bodies.insert(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
}
@ -286,6 +295,7 @@ impl BlockCollection {
self.parents.insert(info.parent_hash.clone(), hash.clone());
self.blocks.insert(hash.clone(), block);
trace!(target: "sync", "New header: {}", hash.hex());
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_HEADERS_TO_SEND: usize = 256;
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 MAX_PEERS_PROPAGATION: usize = 128;
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_NEW_HASHES: usize = 64;
const MAX_TX_TO_IMPORT: usize = 512;
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
const STATUS_PACKET: u8 = 0x00;
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
@ -136,7 +137,9 @@ const NODE_DATA_PACKET: u8 = 0x0e;
const GET_RECEIPTS_PACKET: u8 = 0x0f;
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)]
/// Sync state
@ -184,6 +187,7 @@ pub struct SyncStatus {
/// Peer data type requested
enum PeerAsking {
Nothing,
ForkHeader,
BlockHeaders,
BlockBodies,
Heads,
@ -212,6 +216,16 @@ struct PeerInfo {
asking_hash: Option<H256>,
/// Request timestamp
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.
@ -245,6 +259,8 @@ pub struct ChainSync {
round_parents: VecDeque<(H256, H256)>,
/// Network ID
network_id: U256,
/// Optional fork block to check
fork_block: Option<(BlockNumber, H256)>,
}
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
@ -268,6 +284,7 @@ impl ChainSync {
round_parents: VecDeque::new(),
_max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
network_id: config.network_id,
fork_block: config.fork_block,
};
sync.reset();
sync
@ -284,8 +301,8 @@ impl ChainSync {
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_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
num_peers: self.peers.len(),
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
num_peers: self.peers.values().filter(|p| p.confirmed).count(),
num_active_peers: self.peers.values().filter(|p| p.confirmed && p.asking != PeerAsking::Nothing).count(),
mem_used:
self.blocks.heap_size()
+ self.peers.heap_size_of_children()
@ -306,6 +323,10 @@ impl ChainSync {
for (_, ref mut p) in &mut self.peers {
p.asking_blocks.clear();
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.state = SyncState::Idle;
@ -356,6 +377,8 @@ impl ChainSync {
asking_blocks: Vec::new(),
asking_hash: None,
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);
@ -383,18 +406,43 @@ impl ChainSync {
self.peers.insert(peer_id.clone(), peer);
self.active_peers.insert(peer_id.clone());
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(())
}
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
/// 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> {
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);
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_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() {
trace!(target: "sync", "Ignored unexpected headers");
trace!(target: "sync", "{}: Ignored unexpected headers", peer_id);
self.continue_sync(io);
return Ok(());
}
@ -460,14 +508,14 @@ impl ChainSync {
// Disable the peer for this syncing round if it gives invalid chain
if !valid_response {
trace!(target: "sync", "{} Deactivated for invalid headers response", peer_id);
self.deactivate_peer(io, peer_id);
trace!(target: "sync", "{} Disabled for invalid headers response", peer_id);
io.disable_peer(peer_id);
}
if headers.is_empty() {
// Peer does not have any new subchain heads, deactivate it nd try with another
trace!(target: "sync", "{} Deactivated for no data", peer_id);
self.deactivate_peer(io, peer_id);
trace!(target: "sync", "{} Disabled for no data", peer_id);
io.disable_peer(peer_id);
}
match self.state {
SyncState::ChainHead => {
@ -491,6 +539,9 @@ impl ChainSync {
}
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);
Ok(())
}
@ -543,6 +594,11 @@ impl ChainSync {
peer.latest_hash = header.hash();
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()) {
Err(Error::Import(ImportError::AlreadyInChain)) => {
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 mut max_height: BlockNumber = 0;
let mut new_hashes = Vec::new();
for (rh, rd) in hashes {
let h = try!(rh);
let d = try!(rd);
if d > self.highest_block.unwrap_or(0) {
self.highest_block = Some(d);
for (rh, rn) in hashes {
let hash = try!(rh);
let number = try!(rn);
if number > self.highest_block.unwrap_or(0) {
self.highest_block = Some(number);
}
if self.blocks.is_downloading(&h) {
if self.blocks.is_downloading(&hash) {
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 => {
trace!(target: "sync", "New block hash already in chain {:?}", h);
trace!(target: "sync", "New block hash already in chain {:?}", hash);
},
BlockStatus::Queued => {
trace!(target: "sync", "New hash block already queued {:?}", h);
trace!(target: "sync", "New hash block already queued {:?}", hash);
},
BlockStatus::Unknown => {
new_hashes.push(h.clone());
if d > max_height {
trace!(target: "sync", "New unknown block hash {:?}", h);
new_hashes.push(hash.clone());
if number > max_height {
trace!(target: "sync", "New unknown block hash {:?}", hash);
let peer = self.peers.get_mut(&peer_id).unwrap();
peer.latest_hash = h.clone();
peer.latest_number = Some(d);
max_height = d;
peer.latest_hash = hash.clone();
peer.latest_number = Some(number);
max_height = number;
}
},
BlockStatus::Bad => {
debug!(target: "sync", "Bad new block hash {:?}", h);
debug!(target: "sync", "Bad new block hash {:?}", hash);
io.disable_peer(peer_id);
return Ok(());
}
@ -665,7 +726,8 @@ impl ChainSync {
/// Resume downloading
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
trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len());
for (p, _) in peers {
@ -673,7 +735,7 @@ impl ChainSync {
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();
}
}
@ -699,7 +761,7 @@ impl ChainSync {
}
let (peer_latest, peer_difficulty) = {
let peer = self.peers.get_mut(&peer_id).unwrap();
if peer.asking != PeerAsking::Nothing {
if peer.asking != PeerAsking::Nothing || !peer.is_available() {
return;
}
if self.state == SyncState::Waiting {
@ -727,7 +789,9 @@ impl ChainSync {
// Request subchain headers
trace!(target: "sync", "Starting sync with better chain");
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 => {
if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown {
@ -895,6 +959,17 @@ impl ChainSync {
.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
fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>) {
let mut rlp = RlpStream::new_list(hashes.len());
@ -908,6 +983,7 @@ impl ChainSync {
/// Reset peer status after request is complete.
fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool {
let peer = self.peers.get_mut(&peer_id).unwrap();
peer.expired = false;
if peer.asking != asking {
trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking);
peer.asking = PeerAsking::Nothing;
@ -947,6 +1023,9 @@ impl ChainSync {
if !io.is_chain_queue_empty() {
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();
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
@ -1178,7 +1257,13 @@ impl ChainSync {
let tick = time::precise_time_s();
let mut aborting = Vec::new();
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);
io.disconnect_peer(*peer_id);
aborting.push(*peer_id);
@ -1593,6 +1678,8 @@ mod tests {
asking_blocks: Vec::new(),
asking_hash: None,
ask_time: 0f64,
expired: false,
confirmed: false,
});
sync
}

View File

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

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::*;
use ethcore::client::{BlockChainClient, BlockID, EachBlockWith};
use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith};
use chain::{SyncState};
use super::helpers::*;
@ -95,6 +95,25 @@ fn forked() {
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]
fn restart() {
let mut net = TestNet::new(3);
@ -108,7 +127,7 @@ fn restart() {
net.restart_peer(0);
let status = net.peer(0).sync.read().unwrap().status();
assert_eq!(status.state, SyncState::ChainHead);
assert_eq!(status.state, SyncState::Idle);
}
#[test]

View File

@ -16,6 +16,7 @@
use util::*;
use ethcore::client::{TestBlockChainClient, BlockChainClient};
use ethcore::header::BlockNumber;
use io::SyncIo;
use chain::ChainSync;
use ::SyncConfig;
@ -89,13 +90,19 @@ pub struct TestNet {
impl 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 {
peers: Vec::new(),
started: false,
};
for _ in 0..n {
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 {
sync: RwLock::new(sync),
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.
pub fn readable(&mut self) -> io::Result<Option<Bytes>> {
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);
loop {

View File

@ -128,31 +128,27 @@ impl Handshake {
/// Readable IO handler. Drives the state change.
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone {
if !self.expired() {
match self.state {
HandshakeState::New => {}
HandshakeState::ReadingAuth => {
if let Some(data) = try!(self.connection.readable()) {
while let Some(data) = try!(self.connection.readable()) {
match self.state {
HandshakeState::New => {},
HandshakeState::StartSession => {},
HandshakeState::ReadingAuth => {
try!(self.read_auth(io, host.secret(), &data));
};
},
HandshakeState::ReadingAuthEip8 => {
if let Some(data) = try!(self.connection.readable()) {
},
HandshakeState::ReadingAuthEip8 => {
try!(self.read_auth_eip8(io, host.secret(), &data));
};
},
HandshakeState::ReadingAck => {
if let Some(data) = try!(self.connection.readable()) {
},
HandshakeState::ReadingAck => {
try!(self.read_ack(host.secret(), &data));
};
},
HandshakeState::ReadingAckEip8 => {
if let Some(data) = try!(self.connection.readable()) {
},
HandshakeState::ReadingAckEip8 => {
try!(self.read_ack_eip8(host.secret(), &data));
};
},
HandshakeState::StartSession => {
},
}
if self.state == HandshakeState::StartSession {
io.clear_timer(self.connection.token).ok();
},
break;
}
}
}
Ok(())
@ -178,9 +174,9 @@ impl Handshake {
/// 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 {
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 {
debug!(target:"net", "Wrong auth packet size");
debug!(target: "network", "Wrong auth packet size");
return Err(From::from(NetworkError::BadProtocol));
}
self.auth_cipher = data.to_vec();
@ -197,7 +193,7 @@ impl Handshake {
// Try to interpret as EIP-8 packet
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
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));
}
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 {
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);
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
let rlp = UntrustedRlp::new(&auth);
@ -224,9 +220,9 @@ impl Handshake {
/// Parse and validate ack message
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 {
debug!(target:"net", "Wrong ack packet size");
debug!(target: "network", "Wrong ack packet size");
return Err(From::from(NetworkError::BadProtocol));
}
self.ack_cipher = data.to_vec();
@ -240,7 +236,7 @@ impl Handshake {
// Try to interpret as EIP-8 packet
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
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));
}
let rest = total - data.len();
@ -252,7 +248,7 @@ impl Handshake {
}
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);
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
let rlp = UntrustedRlp::new(&ack);
@ -265,7 +261,7 @@ impl Handshake {
/// Sends auth message
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 len = data.len();
{
@ -292,7 +288,7 @@ impl Handshake {
/// Sends ack message
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 len = data.len();
{
@ -311,7 +307,7 @@ impl Handshake {
/// Sends EIP8 ack message
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);
rlp.append(self.ecdhe.public());
rlp.append(&self.nonce);