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:
committed by
Gav Wood
parent
429c83f92f
commit
8017daf47c
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user