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:
parent
429c83f92f
commit
8017daf47c
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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)",
|
||||
|
@ -17,7 +17,9 @@
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
"networkID" : "0x1",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -137,7 +137,9 @@
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
"networkID" : "0x1",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(¶ms.address) >= value.unwrap();
|
||||
(¶ms.address, &code_address, has_balance)
|
||||
(¶ms.address, &code_address, has_balance, CallType::Call)
|
||||
},
|
||||
instructions::CALLCODE => {
|
||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
||||
(¶ms.address, ¶ms.address, has_balance)
|
||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||
},
|
||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true),
|
||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.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 {
|
||||
|
@ -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,
|
||||
|
@ -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(¶ms.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 {
|
||||
|
@ -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]
|
||||
|
@ -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()),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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![],
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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![]
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
178
ethcore/src/types/trace_types/flat.rs
Normal file
178
ethcore/src/types/trace_types/flat.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -17,5 +17,6 @@
|
||||
//! Types used in the public api
|
||||
|
||||
pub mod filter;
|
||||
pub mod flat;
|
||||
pub mod trace;
|
||||
pub mod localized;
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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)]
|
||||
|
@ -75,7 +75,9 @@ mod tests {
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
"networkID" : "0x2",
|
||||
"forkBlock": "0xffffffffffffffff",
|
||||
"forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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"}"#);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user