Backport commits to beta (#1763)
* Don't try to sync to ancient blocks * Parallel block body download * Fixed reading chunked EIP8 handshake (#1712) * Fixed reading chunked EIP8 handshake * Added missing break * Disconnect peers on a fork * Updated json-ipc-server * Combine mining queue and enabled into single locked datum (#1749) * Combine mining queue and enabled into single locked datum Additional tracing. * Fix bug uncovered by test. * Fix typo * Remove unneeded log initialisation in test. * fix failing test (#1756) * Fixed test * Suicides tracing (#1688) * tracing suicide * fixed #1635 * fixed typo * Stackoverflow #1686 (#1698) * flat trace serialization * tracing finds transaction which creates contract * flatten traces before inserting them to the db * Trace other types of calls (#1727) * Trace through DELEGATECALL and CALLCODE Add them to the JSON output and RLP database store. * Fix tests. * Fix all tests. * Fix one more test. * filtering transactions toAddress includes contract creation (#1697) * tracing finds transaction which creates contract * comma cleanup Remove when following `}`s, add to final entries. * Various improvements to tracing & diagnostics. (#1707) * Various improvements to tracing & diagnostics. - Manage possibility of `Account` not having code for `PodAccount` - New RPC: `trace_sendRawTransaction` - See raw transaction dump when inspecting over RPC * Fix test * Remove one of the dupe error messages * Remove unneeded `&`s * Reformat and extremely minor optimisation * Minor optimisation * Remove unneeded let * Fix tests. * Additional fix. * Minor rename. * Bowing to the pressure. * Stackoverflow fix (#1742) * executive tracer builds flat traces without intermediate struct * temporarilt commented out tests for traces * fixed new way of building trace address * fixed new way of building trace address * updating state tests with flat tracing in progress * fixed flat tracing tests * fixed compiling ethcore-rpc with new flat traces * removed warnings from ethcore module * remove unused data structures
This commit is contained in:
parent
429c83f92f
commit
8017daf47c
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -597,7 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "json-ipc-server"
|
name = "json-ipc-server"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
source = "git+https://github.com/ethcore/json-ipc-server.git#902b031b8f50a59ecb4f389cbec1d264a98556bc"
|
source = "git+https://github.com/ethcore/json-ipc-server.git#93c2756f669c6a1872dec1ef755a0870f40c03c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1",
|
||||||
|
"forkBlock": "0x1d4c00",
|
||||||
|
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
|
@ -137,7 +137,9 @@
|
|||||||
"accountStartNonce": "0x00",
|
"accountStartNonce": "0x00",
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x1"
|
"networkID" : "0x1",
|
||||||
|
"forkBlock": "0x1d4c00",
|
||||||
|
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
|
@ -58,8 +58,8 @@ impl Account {
|
|||||||
nonce: pod.nonce,
|
nonce: pod.nonce,
|
||||||
storage_root: SHA3_NULL_RLP,
|
storage_root: SHA3_NULL_RLP,
|
||||||
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
||||||
code_hash: Some(pod.code.sha3()),
|
code_hash: pod.code.as_ref().map(|c| c.sha3()),
|
||||||
code_cache: pod.code
|
code_cache: pod.code.as_ref().map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
//! Evm input params.
|
//! Evm input params.
|
||||||
use common::*;
|
use common::*;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
/// Transaction value
|
/// Transaction value
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -58,7 +59,10 @@ pub struct ActionParams {
|
|||||||
/// Code being executed.
|
/// Code being executed.
|
||||||
pub code: Option<Bytes>,
|
pub code: Option<Bytes>,
|
||||||
/// Input data.
|
/// Input data.
|
||||||
pub data: Option<Bytes>
|
pub data: Option<Bytes>,
|
||||||
|
/// Type of call
|
||||||
|
pub call_type: CallType,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ActionParams {
|
impl Default for ActionParams {
|
||||||
@ -73,16 +77,18 @@ impl Default for ActionParams {
|
|||||||
gas_price: U256::zero(),
|
gas_price: U256::zero(),
|
||||||
value: ActionValue::Transfer(U256::zero()),
|
value: ActionValue::Transfer(U256::zero()),
|
||||||
code: None,
|
code: None,
|
||||||
data: None
|
data: None,
|
||||||
|
call_type: CallType::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::vm::Transaction> for ActionParams {
|
impl From<ethjson::vm::Transaction> for ActionParams {
|
||||||
fn from(t: ethjson::vm::Transaction) -> Self {
|
fn from(t: ethjson::vm::Transaction) -> Self {
|
||||||
|
let address: Address = t.address.into();
|
||||||
ActionParams {
|
ActionParams {
|
||||||
code_address: Address::new(),
|
code_address: Address::new(),
|
||||||
address: t.address.into(),
|
address: address,
|
||||||
sender: t.sender.into(),
|
sender: t.sender.into(),
|
||||||
origin: t.origin.into(),
|
origin: t.origin.into(),
|
||||||
code: Some(t.code.into()),
|
code: Some(t.code.into()),
|
||||||
@ -90,6 +96,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
|
|||||||
gas: t.gas.into(),
|
gas: t.gas.into(),
|
||||||
gas_price: t.gas_price.into(),
|
gas_price: t.gas_price.into(),
|
||||||
value: ActionValue::Transfer(t.value.into()),
|
value: ActionValue::Transfer(t.value.into()),
|
||||||
|
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use common::*;
|
|||||||
use engine::*;
|
use engine::*;
|
||||||
use state::*;
|
use state::*;
|
||||||
use verification::PreverifiedBlock;
|
use verification::PreverifiedBlock;
|
||||||
use trace::Trace;
|
use trace::FlatTrace;
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
|
|
||||||
/// A block, encoded as it is on the block chain.
|
/// A block, encoded as it is on the block chain.
|
||||||
@ -78,7 +78,7 @@ pub struct ExecutedBlock {
|
|||||||
receipts: Vec<Receipt>,
|
receipts: Vec<Receipt>,
|
||||||
transactions_set: HashSet<H256>,
|
transactions_set: HashSet<H256>,
|
||||||
state: State,
|
state: State,
|
||||||
traces: Option<Vec<Trace>>,
|
traces: Option<Vec<Vec<FlatTrace>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
||||||
@ -94,7 +94,7 @@ pub struct BlockRefMut<'a> {
|
|||||||
/// State.
|
/// State.
|
||||||
pub state: &'a mut State,
|
pub state: &'a mut State,
|
||||||
/// Traces.
|
/// Traces.
|
||||||
pub traces: &'a Option<Vec<Trace>>,
|
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
|
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
|
||||||
@ -110,7 +110,7 @@ pub struct BlockRef<'a> {
|
|||||||
/// State.
|
/// State.
|
||||||
pub state: &'a State,
|
pub state: &'a State,
|
||||||
/// Traces.
|
/// Traces.
|
||||||
pub traces: &'a Option<Vec<Trace>>,
|
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecutedBlock {
|
impl ExecutedBlock {
|
||||||
@ -171,7 +171,7 @@ pub trait IsBlock {
|
|||||||
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
|
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
|
||||||
|
|
||||||
/// Get all information concerning transaction tracing in this block.
|
/// Get all information concerning transaction tracing in this block.
|
||||||
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces }
|
fn traces(&self) -> &Option<Vec<Vec<FlatTrace>>> { &self.block().traces }
|
||||||
|
|
||||||
/// Get all uncles in this block.
|
/// Get all uncles in this block.
|
||||||
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
|
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
|
||||||
@ -329,7 +329,7 @@ impl<'x> OpenBlock<'x> {
|
|||||||
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
|
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
|
||||||
self.block.base.transactions.push(t);
|
self.block.base.transactions.push(t);
|
||||||
let t = outcome.trace;
|
let t = outcome.trace;
|
||||||
self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed")));
|
self.block.traces.as_mut().map(|traces| traces.push(t));
|
||||||
self.block.receipts.push(outcome.receipt);
|
self.block.receipts.push(outcome.receipt);
|
||||||
Ok(&self.block.receipts.last().unwrap())
|
Ok(&self.block.receipts.last().unwrap())
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,9 @@ use receipt::LocalizedReceipt;
|
|||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||||
use trace;
|
use trace;
|
||||||
|
use trace::FlatTransactionTraces;
|
||||||
|
|
||||||
|
// re-export
|
||||||
pub use types::blockchain_info::BlockChainInfo;
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
pub use types::block_status::BlockStatus;
|
pub use types::block_status::BlockStatus;
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
@ -289,8 +292,6 @@ impl Client {
|
|||||||
let _timer = PerfTimer::new("import_verified_blocks");
|
let _timer = PerfTimer::new("import_verified_blocks");
|
||||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
|
|
||||||
let original_best = self.chain_info().best_block_hash;
|
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
|
|
||||||
@ -343,10 +344,6 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.chain_info().best_block_hash != original_best {
|
|
||||||
self.miner.update_sealing(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
imported
|
imported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,8 +358,13 @@ impl Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Commit results
|
// Commit results
|
||||||
let receipts = block.receipts().clone();
|
let receipts = block.receipts().to_owned();
|
||||||
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
let traces = block.traces().clone().unwrap_or_else(Vec::new);
|
||||||
|
let traces: Vec<FlatTransactionTraces> = traces.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
||||||
|
|
||||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||||
// already-imported block of the same number.
|
// already-imported block of the same number.
|
||||||
@ -373,7 +375,7 @@ impl Client {
|
|||||||
// (when something is in chain but you are not able to fetch details)
|
// (when something is in chain but you are not able to fetch details)
|
||||||
let route = self.chain.insert_block(block_data, receipts);
|
let route = self.chain.insert_block(block_data, receipts);
|
||||||
self.tracedb.import(TraceImportRequest {
|
self.tracedb.import(TraceImportRequest {
|
||||||
traces: traces,
|
traces: traces.into(),
|
||||||
block_hash: hash.clone(),
|
block_hash: hash.clone(),
|
||||||
block_number: number,
|
block_number: number,
|
||||||
enacted: route.enacted.clone(),
|
enacted: route.enacted.clone(),
|
||||||
@ -840,8 +842,6 @@ impl MiningBlockChainClient for Client {
|
|||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
let _timer = PerfTimer::new("import_sealed_block");
|
let _timer = PerfTimer::new("import_sealed_block");
|
||||||
|
|
||||||
let original_best = self.chain_info().best_block_hash;
|
|
||||||
|
|
||||||
let h = block.header().hash();
|
let h = block.header().hash();
|
||||||
let number = block.header().number();
|
let number = block.header().number();
|
||||||
|
|
||||||
@ -862,10 +862,6 @@ impl MiningBlockChainClient for Client {
|
|||||||
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
|
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.chain_info().best_block_hash != original_best {
|
|
||||||
self.miner.update_sealing(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(h)
|
Ok(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
use util::common::*;
|
use util::common::*;
|
||||||
use evm::{self, Schedule};
|
use evm::{self, Schedule};
|
||||||
|
use types::executed::CallType;
|
||||||
use env_info::*;
|
use env_info::*;
|
||||||
|
|
||||||
/// Result of externalities create function.
|
/// Result of externalities create function.
|
||||||
@ -69,13 +70,15 @@ pub trait Ext {
|
|||||||
/// and true if subcall was successfull.
|
/// and true if subcall was successfull.
|
||||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: &U256,
|
gas: &U256,
|
||||||
sender_address: &Address,
|
sender_address: &Address,
|
||||||
receive_address: &Address,
|
receive_address: &Address,
|
||||||
value: Option<U256>,
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
output: &mut [u8]) -> MessageCallResult;
|
output: &mut [u8],
|
||||||
|
call_type: CallType
|
||||||
|
) -> MessageCallResult;
|
||||||
|
|
||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> Bytes;
|
fn extcode(&self, address: &Address) -> Bytes;
|
||||||
|
@ -20,6 +20,7 @@ use common::*;
|
|||||||
use super::instructions as instructions;
|
use super::instructions as instructions;
|
||||||
use super::instructions::{Instruction, get_info};
|
use super::instructions::{Instruction, get_info};
|
||||||
use std::marker::Copy;
|
use std::marker::Copy;
|
||||||
|
use types::executed::CallType;
|
||||||
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
|
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
|
||||||
|
|
||||||
#[cfg(not(feature = "evm-debug"))]
|
#[cfg(not(feature = "evm-debug"))]
|
||||||
@ -648,16 +649,16 @@ impl Interpreter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get sender & receive addresses, check if we have balance
|
// Get sender & receive addresses, check if we have balance
|
||||||
let (sender_address, receive_address, has_balance) = match instruction {
|
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||||
instructions::CALL => {
|
instructions::CALL => {
|
||||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
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 => {
|
instructions::CALLCODE => {
|
||||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
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))
|
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -672,7 +673,7 @@ impl Interpreter {
|
|||||||
// and we don't want to copy
|
// and we don't want to copy
|
||||||
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
|
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
|
||||||
let output = self.mem.writeable_slice(out_off, out_size);
|
let output = self.mem.writeable_slice(out_off, out_size);
|
||||||
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
|
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output, call_type)
|
||||||
};
|
};
|
||||||
|
|
||||||
return match call_result {
|
return match call_result {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use types::executed::CallType;
|
||||||
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
|
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ struct FakeCall {
|
|||||||
receive_address: Option<Address>,
|
receive_address: Option<Address>,
|
||||||
value: Option<U256>,
|
value: Option<U256>,
|
||||||
data: Bytes,
|
data: Bytes,
|
||||||
code_address: Option<Address>
|
code_address: Option<Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fake externalities test structure.
|
/// Fake externalities test structure.
|
||||||
@ -119,7 +120,9 @@ impl Ext for FakeExt {
|
|||||||
value: Option<U256>,
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
_output: &mut [u8]) -> MessageCallResult {
|
_output: &mut [u8],
|
||||||
|
_call_type: CallType
|
||||||
|
) -> MessageCallResult {
|
||||||
|
|
||||||
self.calls.insert(FakeCall {
|
self.calls.insert(FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
@ -18,10 +18,11 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use state::*;
|
use state::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
|
use types::executed::CallType;
|
||||||
use evm::{self, Ext, Factory, Finalize};
|
use evm::{self, Ext, Factory, Finalize};
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use substate::*;
|
use substate::*;
|
||||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||||
use crossbeam;
|
use crossbeam;
|
||||||
pub use types::executed::{Executed, ExecutionResult};
|
pub use types::executed::{Executed, ExecutionResult};
|
||||||
|
|
||||||
@ -173,6 +174,7 @@ impl<'a> Executive<'a> {
|
|||||||
value: ActionValue::Transfer(t.value),
|
value: ActionValue::Transfer(t.value),
|
||||||
code: Some(t.data.clone()),
|
code: Some(t.data.clone()),
|
||||||
data: None,
|
data: None,
|
||||||
|
call_type: CallType::None,
|
||||||
};
|
};
|
||||||
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
|
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
|
||||||
},
|
},
|
||||||
@ -187,6 +189,7 @@ impl<'a> Executive<'a> {
|
|||||||
value: ActionValue::Transfer(t.value),
|
value: ActionValue::Transfer(t.value),
|
||||||
code: self.state.code(address),
|
code: self.state.code(address),
|
||||||
data: Some(t.data.clone()),
|
data: Some(t.data.clone()),
|
||||||
|
call_type: CallType::Call,
|
||||||
};
|
};
|
||||||
// TODO: move output upstream
|
// TODO: move output upstream
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
@ -195,7 +198,7 @@ impl<'a> Executive<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// finalize here!
|
// finalize here!
|
||||||
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
|
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_vm<T, V>(
|
fn exec_vm<T, V>(
|
||||||
@ -248,8 +251,6 @@ impl<'a> Executive<'a> {
|
|||||||
}
|
}
|
||||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||||
|
|
||||||
let delegate_call = params.code_address != params.address;
|
|
||||||
|
|
||||||
if self.engine.is_builtin(¶ms.code_address) {
|
if self.engine.is_builtin(¶ms.code_address) {
|
||||||
// if destination is builtin, try to execute it
|
// if destination is builtin, try to execute it
|
||||||
|
|
||||||
@ -275,20 +276,15 @@ impl<'a> Executive<'a> {
|
|||||||
trace_info,
|
trace_info,
|
||||||
cost,
|
cost,
|
||||||
trace_output,
|
trace_output,
|
||||||
self.depth,
|
vec![]
|
||||||
vec![],
|
|
||||||
delegate_call
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(params.gas - cost)
|
Ok(params.gas - cost)
|
||||||
},
|
},
|
||||||
// just drain the whole gas
|
// just drain the whole gas
|
||||||
false => {
|
false => {
|
||||||
self.state.revert_snapshot();
|
self.state.revert_snapshot();
|
||||||
|
tracer.trace_failed_call(trace_info, vec![]);
|
||||||
tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call);
|
|
||||||
|
|
||||||
Err(evm::Error::OutOfGas)
|
Err(evm::Error::OutOfGas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,11 +316,9 @@ impl<'a> Executive<'a> {
|
|||||||
trace_info,
|
trace_info,
|
||||||
gas - gas_left,
|
gas - gas_left,
|
||||||
trace_output,
|
trace_output,
|
||||||
self.depth,
|
traces
|
||||||
traces,
|
|
||||||
delegate_call
|
|
||||||
),
|
),
|
||||||
_ => tracer.trace_failed_call(trace_info, self.depth, traces, delegate_call),
|
_ => tracer.trace_failed_call(trace_info, traces),
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
|
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
|
||||||
@ -336,7 +330,7 @@ impl<'a> Executive<'a> {
|
|||||||
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
||||||
self.state.clear_snapshot();
|
self.state.clear_snapshot();
|
||||||
|
|
||||||
tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![], delegate_call);
|
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
|
||||||
Ok(params.gas)
|
Ok(params.gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,10 +381,9 @@ impl<'a> Executive<'a> {
|
|||||||
gas - gas_left,
|
gas - gas_left,
|
||||||
trace_output,
|
trace_output,
|
||||||
created,
|
created,
|
||||||
self.depth,
|
|
||||||
subtracer.traces()
|
subtracer.traces()
|
||||||
),
|
),
|
||||||
_ => tracer.trace_failed_create(trace_info, self.depth, subtracer.traces())
|
_ => tracer.trace_failed_create(trace_info, subtracer.traces())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.enact_result(&res, substate, unconfirmed_substate);
|
self.enact_result(&res, substate, unconfirmed_substate);
|
||||||
@ -404,7 +397,7 @@ impl<'a> Executive<'a> {
|
|||||||
substate: Substate,
|
substate: Substate,
|
||||||
result: evm::Result<U256>,
|
result: evm::Result<U256>,
|
||||||
output: Bytes,
|
output: Bytes,
|
||||||
trace: Option<Trace>,
|
trace: Vec<FlatTrace>,
|
||||||
vm_trace: Option<VMTrace>
|
vm_trace: Option<VMTrace>
|
||||||
) -> ExecutionResult {
|
) -> ExecutionResult {
|
||||||
let schedule = self.engine.schedule(self.info);
|
let schedule = self.engine.schedule(self.info);
|
||||||
@ -496,8 +489,9 @@ mod tests {
|
|||||||
use substate::*;
|
use substate::*;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use trace::trace;
|
use trace::trace;
|
||||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
|
||||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_contract_address() {
|
fn test_contract_address() {
|
||||||
@ -631,6 +625,7 @@ mod tests {
|
|||||||
params.gas = U256::from(100_000);
|
params.gas = U256::from(100_000);
|
||||||
params.code = Some(code.clone());
|
params.code = Some(code.clone());
|
||||||
params.value = ActionValue::Transfer(U256::from(100));
|
params.value = ActionValue::Transfer(U256::from(100));
|
||||||
|
params.call_type = CallType::Call;
|
||||||
let mut state_result = get_temp_state();
|
let mut state_result = get_temp_state();
|
||||||
let mut state = state_result.reference_mut();
|
let mut state = state_result.reference_mut();
|
||||||
state.add_balance(&sender, &U256::from(100));
|
state.add_balance(&sender, &U256::from(100));
|
||||||
@ -648,35 +643,37 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(gas_left, U256::from(44_752));
|
assert_eq!(gas_left, U256::from(44_752));
|
||||||
|
|
||||||
let expected_trace = vec![ Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
|
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
|
||||||
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 100000.into(),
|
gas: 100000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(55_248),
|
gas_used: U256::from(55_248),
|
||||||
output: vec![],
|
output: vec![],
|
||||||
}),
|
}),
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
action: trace::Action::Create(trace::Create {
|
subtraces: 0,
|
||||||
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
action: trace::Action::Create(trace::Create {
|
||||||
value: 23.into(),
|
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
|
||||||
gas: 67979.into(),
|
value: 23.into(),
|
||||||
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
|
gas: 67979.into(),
|
||||||
}),
|
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
|
||||||
result: trace::Res::Create(trace::CreateResult {
|
}),
|
||||||
gas_used: U256::from(3224),
|
result: trace::Res::Create(trace::CreateResult {
|
||||||
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
|
gas_used: U256::from(3224),
|
||||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
|
||||||
}),
|
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||||
subs: vec![]
|
}),
|
||||||
}]
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
assert_eq!(tracer.traces(), expected_trace);
|
assert_eq!(tracer.traces(), expected_trace);
|
||||||
|
|
||||||
let expected_vm_trace = VMTrace {
|
let expected_vm_trace = VMTrace {
|
||||||
@ -754,8 +751,9 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(gas_left, U256::from(96_776));
|
assert_eq!(gas_left, U256::from(96_776));
|
||||||
|
|
||||||
let expected_trace = vec![Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 0,
|
||||||
action: trace::Action::Create(trace::Create {
|
action: trace::Action::Create(trace::Create {
|
||||||
from: params.sender,
|
from: params.sender,
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
@ -767,8 +765,8 @@ mod tests {
|
|||||||
address: params.address,
|
address: params.address,
|
||||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
assert_eq!(tracer.traces(), expected_trace);
|
assert_eq!(tracer.traces(), expected_trace);
|
||||||
|
|
||||||
let expected_vm_trace = VMTrace {
|
let expected_vm_trace = VMTrace {
|
||||||
|
@ -21,6 +21,7 @@ use engine::*;
|
|||||||
use executive::*;
|
use executive::*;
|
||||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
|
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
|
||||||
use substate::*;
|
use substate::*;
|
||||||
|
use types::executed::CallType;
|
||||||
use trace::{Tracer, VMTracer};
|
use trace::{Tracer, VMTracer};
|
||||||
|
|
||||||
/// Policy for handling output data on `RETURN` opcode.
|
/// Policy for handling output data on `RETURN` opcode.
|
||||||
@ -148,6 +149,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
|||||||
value: ActionValue::Transfer(*value),
|
value: ActionValue::Transfer(*value),
|
||||||
code: Some(code.to_vec()),
|
code: Some(code.to_vec()),
|
||||||
data: None,
|
data: None,
|
||||||
|
call_type: CallType::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.state.inc_nonce(&self.origin_info.address);
|
self.state.inc_nonce(&self.origin_info.address);
|
||||||
@ -170,7 +172,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
|||||||
value: Option<U256>,
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
output: &mut [u8]
|
output: &mut [u8],
|
||||||
|
call_type: CallType
|
||||||
) -> MessageCallResult {
|
) -> MessageCallResult {
|
||||||
trace!(target: "externalities", "call");
|
trace!(target: "externalities", "call");
|
||||||
|
|
||||||
@ -184,6 +187,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
|||||||
gas_price: self.origin_info.gas_price,
|
gas_price: self.origin_info.gas_price,
|
||||||
code: self.state.code(code_address),
|
code: self.state.code(code_address),
|
||||||
data: Some(data.to_vec()),
|
data: Some(data.to_vec()),
|
||||||
|
call_type: call_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
@ -262,6 +266,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
|||||||
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
||||||
self.state.transfer_balance(&address, refund_address, &balance);
|
self.state.transfer_balance(&address, refund_address, &balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.tracer.trace_suicide(address, balance, refund_address.clone());
|
||||||
self.substate.suicides.insert(address);
|
self.substate.suicides.insert(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,6 +306,7 @@ mod tests {
|
|||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use trace::{NoopTracer, NoopVMTracer};
|
use trace::{NoopTracer, NoopVMTracer};
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
fn get_test_origin() -> OriginInfo {
|
fn get_test_origin() -> OriginInfo {
|
||||||
OriginInfo {
|
OriginInfo {
|
||||||
@ -418,7 +425,9 @@ mod tests {
|
|||||||
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
|
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
|
||||||
&[],
|
&[],
|
||||||
&Address::new(),
|
&Address::new(),
|
||||||
&mut output);
|
&mut output,
|
||||||
|
CallType::Call
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -22,6 +22,7 @@ use evm;
|
|||||||
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
|
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use substate::*;
|
use substate::*;
|
||||||
|
use types::executed::CallType;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use trace::{Tracer, NoopTracer};
|
use trace::{Tracer, NoopTracer};
|
||||||
@ -109,13 +110,15 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: &U256,
|
gas: &U256,
|
||||||
_sender_address: &Address,
|
_sender_address: &Address,
|
||||||
receive_address: &Address,
|
receive_address: &Address,
|
||||||
value: Option<U256>,
|
value: Option<U256>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
_code_address: &Address,
|
_code_address: &Address,
|
||||||
_output: &mut [u8]) -> MessageCallResult {
|
_output: &mut [u8],
|
||||||
|
_call_type: CallType
|
||||||
|
) -> MessageCallResult {
|
||||||
self.callcreates.push(CallCreate {
|
self.callcreates.push(CallCreate {
|
||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
destination: Some(receive_address.clone()),
|
destination: Some(receive_address.clone()),
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
@ -87,15 +86,20 @@ impl Default for MinerOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SealingWork {
|
||||||
|
queue: UsingQueue<ClosedBlock>,
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Keeps track of transactions using priority queue and holds currently mined block.
|
/// Keeps track of transactions using priority queue and holds currently mined block.
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
// NOTE [ToDr] When locking always lock in this order!
|
// NOTE [ToDr] When locking always lock in this order!
|
||||||
transaction_queue: Mutex<TransactionQueue>,
|
transaction_queue: Mutex<TransactionQueue>,
|
||||||
sealing_work: Mutex<UsingQueue<ClosedBlock>>,
|
sealing_work: Mutex<SealingWork>,
|
||||||
|
|
||||||
// for sealing...
|
// for sealing...
|
||||||
options: MinerOptions,
|
options: MinerOptions,
|
||||||
sealing_enabled: AtomicBool,
|
|
||||||
next_allowed_reseal: Mutex<Instant>,
|
next_allowed_reseal: Mutex<Instant>,
|
||||||
sealing_block_last_request: Mutex<u64>,
|
sealing_block_last_request: Mutex<u64>,
|
||||||
gas_range_target: RwLock<(U256, U256)>,
|
gas_range_target: RwLock<(U256, U256)>,
|
||||||
@ -113,10 +117,9 @@ impl Miner {
|
|||||||
Miner {
|
Miner {
|
||||||
transaction_queue: Mutex::new(TransactionQueue::new()),
|
transaction_queue: Mutex::new(TransactionQueue::new()),
|
||||||
options: Default::default(),
|
options: Default::default(),
|
||||||
sealing_enabled: AtomicBool::new(false),
|
|
||||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||||
sealing_block_last_request: Mutex::new(0),
|
sealing_block_last_request: Mutex::new(0),
|
||||||
sealing_work: Mutex::new(UsingQueue::new(20)),
|
sealing_work: Mutex::new(SealingWork{queue: UsingQueue::new(20), enabled: false}),
|
||||||
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
|
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
|
||||||
author: RwLock::new(Address::default()),
|
author: RwLock::new(Address::default()),
|
||||||
extra_data: RwLock::new(Vec::new()),
|
extra_data: RwLock::new(Vec::new()),
|
||||||
@ -131,10 +134,9 @@ impl Miner {
|
|||||||
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
|
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
|
||||||
Arc::new(Miner {
|
Arc::new(Miner {
|
||||||
transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)),
|
transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)),
|
||||||
sealing_enabled: AtomicBool::new(options.force_sealing || !options.new_work_notify.is_empty()),
|
|
||||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||||
sealing_block_last_request: Mutex::new(0),
|
sealing_block_last_request: Mutex::new(0),
|
||||||
sealing_work: Mutex::new(UsingQueue::new(options.work_queue_size)),
|
sealing_work: Mutex::new(SealingWork{queue: UsingQueue::new(options.work_queue_size), enabled: options.force_sealing || !options.new_work_notify.is_empty()}),
|
||||||
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
|
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
|
||||||
author: RwLock::new(Address::default()),
|
author: RwLock::new(Address::default()),
|
||||||
extra_data: RwLock::new(Vec::new()),
|
extra_data: RwLock::new(Vec::new()),
|
||||||
@ -155,12 +157,12 @@ impl Miner {
|
|||||||
|
|
||||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||||
pub fn pending_state(&self) -> Option<State> {
|
pub fn pending_state(&self) -> Option<State> {
|
||||||
self.sealing_work.lock().unwrap().peek_last_ref().map(|b| b.block().fields().state.clone())
|
self.sealing_work.lock().unwrap().queue.peek_last_ref().map(|b| b.block().fields().state.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||||
pub fn pending_block(&self) -> Option<Block> {
|
pub fn pending_block(&self) -> Option<Block> {
|
||||||
self.sealing_work.lock().unwrap().peek_last_ref().map(|b| b.base().clone())
|
self.sealing_work.lock().unwrap().queue.peek_last_ref().map(|b| b.base().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares new block for sealing including top transactions from queue.
|
/// Prepares new block for sealing including top transactions from queue.
|
||||||
@ -172,7 +174,7 @@ impl Miner {
|
|||||||
let (transactions, mut open_block, original_work_hash) = {
|
let (transactions, mut open_block, original_work_hash) = {
|
||||||
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
|
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
|
||||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||||
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
||||||
let best_hash = chain.best_block_header().sha3();
|
let best_hash = chain.best_block_header().sha3();
|
||||||
/*
|
/*
|
||||||
// check to see if last ClosedBlock in would_seals is actually same parent block.
|
// check to see if last ClosedBlock in would_seals is actually same parent block.
|
||||||
@ -182,7 +184,7 @@ impl Miner {
|
|||||||
// otherwise, leave everything alone.
|
// otherwise, leave everything alone.
|
||||||
// otherwise, author a fresh block.
|
// otherwise, author a fresh block.
|
||||||
*/
|
*/
|
||||||
let open_block = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
|
let open_block = match sealing_work.queue.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
|
||||||
Some(old_block) => {
|
Some(old_block) => {
|
||||||
trace!(target: "miner", "Already have previous work; updating and returning");
|
trace!(target: "miner", "Already have previous work; updating and returning");
|
||||||
// add transactions to old_block
|
// add transactions to old_block
|
||||||
@ -273,7 +275,7 @@ impl Miner {
|
|||||||
|
|
||||||
let (work, is_new) = {
|
let (work, is_new) = {
|
||||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||||
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
||||||
trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash());
|
trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash());
|
||||||
let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) {
|
let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) {
|
||||||
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
|
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
|
||||||
@ -281,16 +283,16 @@ impl Miner {
|
|||||||
let number = block.block().fields().header.number();
|
let number = block.block().fields().header.number();
|
||||||
let difficulty = *block.block().fields().header.difficulty();
|
let difficulty = *block.block().fields().header.difficulty();
|
||||||
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
|
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
|
||||||
sealing_work.push(block);
|
sealing_work.queue.push(block);
|
||||||
// If push notifications are enabled we assume all work items are used.
|
// If push notifications are enabled we assume all work items are used.
|
||||||
if self.work_poster.is_some() && is_new {
|
if self.work_poster.is_some() && is_new {
|
||||||
sealing_work.use_last_ref();
|
sealing_work.queue.use_last_ref();
|
||||||
}
|
}
|
||||||
(Some((pow_hash, difficulty, number)), is_new)
|
(Some((pow_hash, difficulty, number)), is_new)
|
||||||
} else {
|
} else {
|
||||||
(None, false)
|
(None, false)
|
||||||
};
|
};
|
||||||
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
|
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.queue.peek_last_ref().map(|b| b.block().fields().header.hash()));
|
||||||
(work, is_new)
|
(work, is_new)
|
||||||
};
|
};
|
||||||
if is_new {
|
if is_new {
|
||||||
@ -307,14 +309,22 @@ impl Miner {
|
|||||||
/// Returns true if we had to prepare new pending block
|
/// Returns true if we had to prepare new pending block
|
||||||
fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool {
|
fn enable_and_prepare_sealing(&self, chain: &MiningBlockChainClient) -> bool {
|
||||||
trace!(target: "miner", "enable_and_prepare_sealing: entering");
|
trace!(target: "miner", "enable_and_prepare_sealing: entering");
|
||||||
let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some();
|
let prepare_new = {
|
||||||
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
|
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||||
if !have_work {
|
let have_work = sealing_work.queue.peek_last_ref().is_some();
|
||||||
|
trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work);
|
||||||
|
if !have_work {
|
||||||
|
sealing_work.enabled = true;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if prepare_new {
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
||||||
// | Make sure to release the locks before calling that method. |
|
// | Make sure to release the locks before calling that method. |
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
|
||||||
self.prepare_sealing(chain);
|
self.prepare_sealing(chain);
|
||||||
}
|
}
|
||||||
let mut sealing_block_last_request = self.sealing_block_last_request.lock().unwrap();
|
let mut sealing_block_last_request = self.sealing_block_last_request.lock().unwrap();
|
||||||
@ -324,8 +334,8 @@ impl Miner {
|
|||||||
*sealing_block_last_request = best_number;
|
*sealing_block_last_request = best_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if
|
// Return if we restarted
|
||||||
!have_work
|
prepare_new
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
|
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
|
||||||
@ -364,13 +374,13 @@ impl MinerService for Miner {
|
|||||||
MinerStatus {
|
MinerStatus {
|
||||||
transactions_in_pending_queue: status.pending,
|
transactions_in_pending_queue: status.pending,
|
||||||
transactions_in_future_queue: status.future,
|
transactions_in_future_queue: status.future,
|
||||||
transactions_in_pending_block: sealing_work.peek_last_ref().map_or(0, |b| b.transactions().len()),
|
transactions_in_pending_block: sealing_work.queue.peek_last_ref().map_or(0, |b| b.transactions().len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
||||||
let sealing_work = self.sealing_work.lock().unwrap();
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
match sealing_work.peek_last_ref() {
|
match sealing_work.queue.peek_last_ref() {
|
||||||
Some(work) => {
|
Some(work) => {
|
||||||
let block = work.block();
|
let block = work.block();
|
||||||
|
|
||||||
@ -417,7 +427,7 @@ impl MinerService for Miner {
|
|||||||
|
|
||||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||||
let sealing_work = self.sealing_work.lock().unwrap();
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
sealing_work.peek_last_ref().map_or_else(
|
sealing_work.queue.peek_last_ref().map_or_else(
|
||||||
|| chain.latest_balance(address),
|
|| chain.latest_balance(address),
|
||||||
|b| b.block().fields().state.balance(address)
|
|b| b.block().fields().state.balance(address)
|
||||||
)
|
)
|
||||||
@ -425,7 +435,7 @@ impl MinerService for Miner {
|
|||||||
|
|
||||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||||
let sealing_work = self.sealing_work.lock().unwrap();
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
sealing_work.peek_last_ref().map_or_else(
|
sealing_work.queue.peek_last_ref().map_or_else(
|
||||||
|| chain.latest_storage_at(address, position),
|
|| chain.latest_storage_at(address, position),
|
||||||
|b| b.block().fields().state.storage_at(address, position)
|
|b| b.block().fields().state.storage_at(address, position)
|
||||||
)
|
)
|
||||||
@ -433,12 +443,12 @@ impl MinerService for Miner {
|
|||||||
|
|
||||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||||
let sealing_work = self.sealing_work.lock().unwrap();
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
sealing_work.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
|
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
||||||
let sealing_work = self.sealing_work.lock().unwrap();
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
|
sealing_work.queue.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_author(&self, author: Address) {
|
fn set_author(&self, author: Address) {
|
||||||
@ -582,8 +592,8 @@ impl MinerService for Miner {
|
|||||||
let queue = self.transaction_queue.lock().unwrap();
|
let queue = self.transaction_queue.lock().unwrap();
|
||||||
let sw = self.sealing_work.lock().unwrap();
|
let sw = self.sealing_work.lock().unwrap();
|
||||||
// TODO: should only use the sealing_work when it's current (it could be an old block)
|
// TODO: should only use the sealing_work when it's current (it could be an old block)
|
||||||
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
let sealing_set = match sw.enabled {
|
||||||
true => sw.peek_last_ref(),
|
true => sw.queue.peek_last_ref(),
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
match (&self.options.pending_set, sealing_set) {
|
match (&self.options.pending_set, sealing_set) {
|
||||||
@ -595,8 +605,8 @@ impl MinerService for Miner {
|
|||||||
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
||||||
let queue = self.transaction_queue.lock().unwrap();
|
let queue = self.transaction_queue.lock().unwrap();
|
||||||
let sw = self.sealing_work.lock().unwrap();
|
let sw = self.sealing_work.lock().unwrap();
|
||||||
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
let sealing_set = match sw.enabled {
|
||||||
true => sw.peek_last_ref(),
|
true => sw.queue.peek_last_ref(),
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
match (&self.options.pending_set, sealing_set) {
|
match (&self.options.pending_set, sealing_set) {
|
||||||
@ -608,8 +618,8 @@ impl MinerService for Miner {
|
|||||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||||
let queue = self.transaction_queue.lock().unwrap();
|
let queue = self.transaction_queue.lock().unwrap();
|
||||||
let sw = self.sealing_work.lock().unwrap();
|
let sw = self.sealing_work.lock().unwrap();
|
||||||
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
let sealing_set = match sw.enabled {
|
||||||
true => sw.peek_last_ref(),
|
true => sw.queue.peek_last_ref(),
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
match (&self.options.pending_set, sealing_set) {
|
match (&self.options.pending_set, sealing_set) {
|
||||||
@ -619,7 +629,8 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||||
match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) {
|
let sealing_work = self.sealing_work.lock().unwrap();
|
||||||
|
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||||
(true, Some(pending)) => {
|
(true, Some(pending)) => {
|
||||||
let hashes = pending.transactions()
|
let hashes = pending.transactions()
|
||||||
.iter()
|
.iter()
|
||||||
@ -638,27 +649,43 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
||||||
if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
trace!(target: "miner", "update_sealing");
|
||||||
let current_no = chain.chain_info().best_block_number;
|
let requires_reseal = {
|
||||||
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
|
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||||
let last_request = *self.sealing_block_last_request.lock().unwrap();
|
if sealing_work.enabled {
|
||||||
let should_disable_sealing = !self.forced_sealing()
|
trace!(target: "miner", "update_sealing: sealing enabled");
|
||||||
&& !has_local_transactions
|
let current_no = chain.chain_info().best_block_number;
|
||||||
&& current_no > last_request
|
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
|
||||||
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
|
let last_request = *self.sealing_block_last_request.lock().unwrap();
|
||||||
|
let should_disable_sealing = !self.forced_sealing()
|
||||||
|
&& !has_local_transactions
|
||||||
|
&& current_no > last_request
|
||||||
|
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
|
||||||
|
|
||||||
if should_disable_sealing {
|
trace!(target: "miner", "update_sealing: should_disable_sealing={}; current_no={}, last_request={}", should_disable_sealing, current_no, last_request);
|
||||||
trace!(target: "miner", "Miner sleeping (current {}, last {})", current_no, last_request);
|
|
||||||
self.sealing_enabled.store(false, atomic::Ordering::Relaxed);
|
if should_disable_sealing {
|
||||||
self.sealing_work.lock().unwrap().reset();
|
trace!(target: "miner", "Miner sleeping (current {}, last {})", current_no, last_request);
|
||||||
|
sealing_work.enabled = false;
|
||||||
|
sealing_work.queue.reset();
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// sealing enabled and we don't want to sleep.
|
||||||
|
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period;
|
||||||
|
true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period;
|
// sealing is disabled.
|
||||||
// --------------------------------------------------------------------------
|
false
|
||||||
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
|
||||||
// | Make sure to release the locks before calling that method. |
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
self.prepare_sealing(chain);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if requires_reseal {
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
||||||
|
// | Make sure to release the locks before calling that method. |
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
self.prepare_sealing(chain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,13 +694,13 @@ impl MinerService for Miner {
|
|||||||
self.enable_and_prepare_sealing(chain);
|
self.enable_and_prepare_sealing(chain);
|
||||||
trace!(target: "miner", "map_sealing_work: sealing prepared");
|
trace!(target: "miner", "map_sealing_work: sealing prepared");
|
||||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||||
let ret = sealing_work.use_last_ref();
|
let ret = sealing_work.queue.use_last_ref();
|
||||||
trace!(target: "miner", "map_sealing_work: leaving use_last_ref={:?}", ret.as_ref().map(|b| b.block().fields().header.hash()));
|
trace!(target: "miner", "map_sealing_work: leaving use_last_ref={:?}", ret.as_ref().map(|b| b.block().fields().header.hash()));
|
||||||
ret.map(f)
|
ret.map(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
let result = if let Some(b) = self.sealing_work.lock().unwrap().get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) {
|
let result = if let Some(b) = self.sealing_work.lock().unwrap().queue.get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) {
|
||||||
b.lock().try_seal(self.engine(), seal).or_else(|_| {
|
b.lock().try_seal(self.engine(), seal).or_else(|_| {
|
||||||
warn!(target: "miner", "Mined solution rejected: Invalid.");
|
warn!(target: "miner", "Mined solution rejected: Invalid.");
|
||||||
Err(Error::PowInvalid)
|
Err(Error::PowInvalid)
|
||||||
@ -692,6 +719,8 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
||||||
|
trace!(target: "miner", "chain_new_blocks");
|
||||||
|
|
||||||
fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
||||||
let block = chain
|
let block = chain
|
||||||
.block(BlockID::Hash(*hash))
|
.block(BlockID::Hash(*hash))
|
||||||
@ -746,11 +775,13 @@ impl MinerService for Miner {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
if enacted.len() > 0 {
|
||||||
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
// --------------------------------------------------------------------------
|
||||||
// | Make sure to release the locks before calling that method. |
|
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
||||||
// --------------------------------------------------------------------------
|
// | Make sure to release the locks before calling that method. |
|
||||||
self.update_sealing(chain);
|
// --------------------------------------------------------------------------
|
||||||
|
self.update_sealing(chain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ pub struct PodAccount {
|
|||||||
pub balance: U256,
|
pub balance: U256,
|
||||||
/// The nonce of the account.
|
/// The nonce of the account.
|
||||||
pub nonce: U256,
|
pub nonce: U256,
|
||||||
/// The code of the account.
|
/// The code of the account or `None` in the special case that it is unknown.
|
||||||
pub code: Bytes,
|
pub code: Option<Bytes>,
|
||||||
/// The storage of the account.
|
/// The storage of the account.
|
||||||
pub storage: BTreeMap<H256, H256>,
|
pub storage: BTreeMap<H256, H256>,
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ impl PodAccount {
|
|||||||
/// Construct new object.
|
/// Construct new object.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount {
|
pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount {
|
||||||
PodAccount { balance: balance, nonce: nonce, code: code, storage: storage }
|
PodAccount { balance: balance, nonce: nonce, code: Some(code), storage: storage }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Account to a PodAccount.
|
/// Convert Account to a PodAccount.
|
||||||
@ -48,7 +48,7 @@ impl PodAccount {
|
|||||||
balance: *acc.balance(),
|
balance: *acc.balance(),
|
||||||
nonce: *acc.nonce(),
|
nonce: *acc.nonce(),
|
||||||
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
|
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
|
||||||
code: acc.code().unwrap().to_vec(),
|
code: acc.code().map(|x| x.to_vec()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,14 +58,15 @@ impl PodAccount {
|
|||||||
stream.append(&self.nonce);
|
stream.append(&self.nonce);
|
||||||
stream.append(&self.balance);
|
stream.append(&self.balance);
|
||||||
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
|
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
|
||||||
stream.append(&self.code.sha3());
|
stream.append(&self.code.as_ref().unwrap_or(&vec![]).sha3());
|
||||||
stream.out()
|
stream.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Place additional data into given hash DB.
|
/// Place additional data into given hash DB.
|
||||||
pub fn insert_additional(&self, db: &mut AccountDBMut) {
|
pub fn insert_additional(&self, db: &mut AccountDBMut) {
|
||||||
if !self.code.is_empty() {
|
match self.code {
|
||||||
db.insert(&self.code);
|
Some(ref c) if !c.is_empty() => { db.insert(c); }
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
let mut r = H256::new();
|
let mut r = H256::new();
|
||||||
let mut t = SecTrieDBMut::new(db, &mut r);
|
let mut t = SecTrieDBMut::new(db, &mut r);
|
||||||
@ -80,7 +81,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
|
|||||||
PodAccount {
|
PodAccount {
|
||||||
balance: a.balance.into(),
|
balance: a.balance.into(),
|
||||||
nonce: a.nonce.into(),
|
nonce: a.nonce.into(),
|
||||||
code: a.code.into(),
|
code: Some(a.code.into()),
|
||||||
storage: a.storage.into_iter().map(|(key, value)| {
|
storage: a.storage.into_iter().map(|(key, value)| {
|
||||||
let key: U256 = key.into();
|
let key: U256 = key.into();
|
||||||
let value: U256 = value.into();
|
let value: U256 = value.into();
|
||||||
@ -95,7 +96,7 @@ impl From<ethjson::spec::Account> for PodAccount {
|
|||||||
PodAccount {
|
PodAccount {
|
||||||
balance: a.balance.map_or_else(U256::zero, Into::into),
|
balance: a.balance.map_or_else(U256::zero, Into::into),
|
||||||
nonce: a.nonce.map_or_else(U256::zero, Into::into),
|
nonce: a.nonce.map_or_else(U256::zero, Into::into),
|
||||||
code: vec![],
|
code: Some(vec![]),
|
||||||
storage: BTreeMap::new()
|
storage: BTreeMap::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +104,13 @@ impl From<ethjson::spec::Account> for PodAccount {
|
|||||||
|
|
||||||
impl fmt::Display for PodAccount {
|
impl fmt::Display for PodAccount {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len())
|
write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)",
|
||||||
|
self.balance,
|
||||||
|
self.nonce,
|
||||||
|
self.code.as_ref().map_or(0, |c| c.len()),
|
||||||
|
self.code.as_ref().map_or_else(H256::new, |c| c.sha3()),
|
||||||
|
self.storage.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,13 +121,13 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
|
|||||||
(None, Some(x)) => Some(AccountDiff {
|
(None, Some(x)) => Some(AccountDiff {
|
||||||
balance: Diff::Born(x.balance),
|
balance: Diff::Born(x.balance),
|
||||||
nonce: Diff::Born(x.nonce),
|
nonce: Diff::Born(x.nonce),
|
||||||
code: Diff::Born(x.code.clone()),
|
code: Diff::Born(x.code.as_ref().expect("account is newly created; newly created accounts must be given code; all caches should remain in place; qed").clone()),
|
||||||
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
|
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
|
||||||
}),
|
}),
|
||||||
(Some(x), None) => Some(AccountDiff {
|
(Some(x), None) => Some(AccountDiff {
|
||||||
balance: Diff::Died(x.balance),
|
balance: Diff::Died(x.balance),
|
||||||
nonce: Diff::Died(x.nonce),
|
nonce: Diff::Died(x.nonce),
|
||||||
code: Diff::Died(x.code.clone()),
|
code: Diff::Died(x.code.as_ref().expect("account is deleted; only way to delete account is running SUICIDE; account must have had own code cached to make operation; all caches should remain in place; qed").clone()),
|
||||||
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
|
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
|
||||||
}),
|
}),
|
||||||
(Some(pre), Some(post)) => {
|
(Some(pre), Some(post)) => {
|
||||||
@ -130,7 +137,10 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
|
|||||||
let r = AccountDiff {
|
let r = AccountDiff {
|
||||||
balance: Diff::new(pre.balance, post.balance),
|
balance: Diff::new(pre.balance, post.balance),
|
||||||
nonce: Diff::new(pre.nonce, post.nonce),
|
nonce: Diff::new(pre.nonce, post.nonce),
|
||||||
code: Diff::new(pre.code.clone(), post.code.clone()),
|
code: match (pre.code.clone(), post.code.clone()) {
|
||||||
|
(Some(pre_code), Some(post_code)) => Diff::new(pre_code, post_code),
|
||||||
|
_ => Diff::Same,
|
||||||
|
},
|
||||||
storage: storage.into_iter().map(|k|
|
storage: storage.into_iter().map(|k|
|
||||||
(k.clone(), Diff::new(
|
(k.clone(), Diff::new(
|
||||||
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
|
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
|
||||||
@ -156,7 +166,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn existence() {
|
fn existence() {
|
||||||
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
||||||
assert_eq!(diff_pod(Some(&a), Some(&a)), None);
|
assert_eq!(diff_pod(Some(&a), Some(&a)), None);
|
||||||
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
|
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
|
||||||
balance: Diff::Born(69.into()),
|
balance: Diff::Born(69.into()),
|
||||||
@ -168,8 +178,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic() {
|
||||||
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
||||||
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]};
|
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]};
|
||||||
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||||
balance: Diff::Changed(69.into(), 42.into()),
|
balance: Diff::Changed(69.into(), 42.into()),
|
||||||
nonce: Diff::Changed(0.into(), 1.into()),
|
nonce: Diff::Changed(0.into(), 1.into()),
|
||||||
@ -180,8 +190,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn code() {
|
fn code() {
|
||||||
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]};
|
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]};
|
||||||
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]};
|
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]};
|
||||||
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||||
balance: Diff::Same,
|
balance: Diff::Same,
|
||||||
nonce: Diff::Changed(0.into(), 1.into()),
|
nonce: Diff::Changed(0.into(), 1.into()),
|
||||||
@ -195,13 +205,13 @@ mod test {
|
|||||||
let a = PodAccount {
|
let a = PodAccount {
|
||||||
balance: 0.into(),
|
balance: 0.into(),
|
||||||
nonce: 0.into(),
|
nonce: 0.into(),
|
||||||
code: vec![],
|
code: Some(vec![]),
|
||||||
storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
|
storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
|
||||||
};
|
};
|
||||||
let b = PodAccount {
|
let b = PodAccount {
|
||||||
balance: 0.into(),
|
balance: 0.into(),
|
||||||
nonce: 0.into(),
|
nonce: 0.into(),
|
||||||
code: vec![],
|
code: Some(vec![]),
|
||||||
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
|
storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
|
||||||
};
|
};
|
||||||
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
|
||||||
|
@ -38,6 +38,8 @@ pub struct CommonParams {
|
|||||||
pub network_id: U256,
|
pub network_id: U256,
|
||||||
/// Minimum gas limit.
|
/// Minimum gas limit.
|
||||||
pub min_gas_limit: U256,
|
pub min_gas_limit: U256,
|
||||||
|
/// Fork block to check.
|
||||||
|
pub fork_block: Option<(BlockNumber, H256)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::Params> for CommonParams {
|
impl From<ethjson::spec::Params> for CommonParams {
|
||||||
@ -47,6 +49,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||||
network_id: p.network_id.into(),
|
network_id: p.network_id.into(),
|
||||||
min_gas_limit: p.min_gas_limit.into(),
|
min_gas_limit: p.min_gas_limit.into(),
|
||||||
|
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +151,9 @@ impl Spec {
|
|||||||
/// Get the configured Network ID.
|
/// Get the configured Network ID.
|
||||||
pub fn network_id(&self) -> U256 { self.params.network_id }
|
pub fn network_id(&self) -> U256 { self.params.network_id }
|
||||||
|
|
||||||
|
/// Get the configured network fork block.
|
||||||
|
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params.fork_block }
|
||||||
|
|
||||||
/// Get the header of the genesis block.
|
/// Get the header of the genesis block.
|
||||||
pub fn genesis_header(&self) -> Header {
|
pub fn genesis_header(&self) -> Header {
|
||||||
Header {
|
Header {
|
||||||
|
@ -19,7 +19,7 @@ use engine::Engine;
|
|||||||
use executive::{Executive, TransactOptions};
|
use executive::{Executive, TransactOptions};
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
use account_db::*;
|
use account_db::*;
|
||||||
use trace::Trace;
|
use trace::FlatTrace;
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use pod_state::{self, PodState};
|
use pod_state::{self, PodState};
|
||||||
use types::state_diff::StateDiff;
|
use types::state_diff::StateDiff;
|
||||||
@ -29,7 +29,7 @@ pub struct ApplyOutcome {
|
|||||||
/// The receipt for the applied transaction.
|
/// The receipt for the applied transaction.
|
||||||
pub receipt: Receipt,
|
pub receipt: Receipt,
|
||||||
/// The trace for the applied transaction, if None if tracing is disabled.
|
/// The trace for the applied transaction, if None if tracing is disabled.
|
||||||
pub trace: Option<Trace>,
|
pub trace: Vec<FlatTrace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type for the execution ("application") of a transaction.
|
/// Result type for the execution ("application") of a transaction.
|
||||||
@ -389,7 +389,8 @@ use spec::*;
|
|||||||
use transaction::*;
|
use transaction::*;
|
||||||
use util::log::init_log;
|
use util::log::init_log;
|
||||||
use trace::trace;
|
use trace::trace;
|
||||||
use trace::trace::{Trace};
|
use trace::FlatTrace;
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_apply_create_transaction() {
|
fn should_apply_create_transaction() {
|
||||||
@ -414,8 +415,9 @@ fn should_apply_create_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 0,
|
||||||
action: trace::Action::Create(trace::Create {
|
action: trace::Action::Create(trace::Create {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
@ -427,8 +429,7 @@ fn should_apply_create_transaction() {
|
|||||||
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
|
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
|
||||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
}];
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -475,8 +476,8 @@ fn should_trace_failed_create_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Create(trace::Create {
|
action: trace::Action::Create(trace::Create {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
@ -484,8 +485,8 @@ fn should_trace_failed_create_transaction() {
|
|||||||
init: vec![91, 96, 0, 86],
|
init: vec![91, 96, 0, 86],
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCreate,
|
result: trace::Res::FailedCreate,
|
||||||
subs: vec![]
|
subtraces: 0
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -514,21 +515,22 @@ fn should_trace_call_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(3),
|
gas_used: U256::from(3),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
subtraces: 0,
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -556,21 +558,22 @@ fn should_trace_basic_call_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(0),
|
gas_used: U256::from(0),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
subtraces: 0,
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -598,21 +601,24 @@ fn should_trace_call_transaction_to_builtin() {
|
|||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
assert_eq!(result.trace, Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: "0000000000000000000000000000000000000001".into(),
|
to: "0000000000000000000000000000000000000001".into(),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
gas: 79_000.into(),
|
gas: 79_000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(3000),
|
gas_used: U256::from(3000),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
subtraces: 0,
|
||||||
}));
|
}];
|
||||||
|
|
||||||
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -639,21 +645,23 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
|||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(28_061),
|
gas_used: U256::from(28_061),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
subtraces: 0,
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,21 +690,38 @@ fn should_not_trace_callcode() {
|
|||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(64),
|
gas_used: 64.into(),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
}, FlatTrace {
|
||||||
});
|
trace_address: vec![0].into_iter().collect(),
|
||||||
|
subtraces: 0,
|
||||||
|
action: trace::Action::Call(trace::Call {
|
||||||
|
from: 0xa.into(),
|
||||||
|
to: 0xa.into(),
|
||||||
|
value: 0.into(),
|
||||||
|
gas: 4096.into(),
|
||||||
|
input: vec![],
|
||||||
|
call_type: CallType::CallCode,
|
||||||
|
}),
|
||||||
|
result: trace::Res::Call(trace::CallResult {
|
||||||
|
gas_used: 3.into(),
|
||||||
|
output: vec![],
|
||||||
|
}),
|
||||||
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,21 +753,38 @@ fn should_not_trace_delegatecall() {
|
|||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(61),
|
gas_used: U256::from(61),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
}, FlatTrace {
|
||||||
});
|
trace_address: vec![0].into_iter().collect(),
|
||||||
|
subtraces: 0,
|
||||||
|
action: trace::Action::Call(trace::Call {
|
||||||
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
|
to: 0xa.into(),
|
||||||
|
value: 0.into(),
|
||||||
|
gas: 32768.into(),
|
||||||
|
input: vec![],
|
||||||
|
call_type: CallType::DelegateCall,
|
||||||
|
}),
|
||||||
|
result: trace::Res::Call(trace::CallResult {
|
||||||
|
gas_used: 3.into(),
|
||||||
|
output: vec![],
|
||||||
|
}),
|
||||||
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,20 +812,19 @@ fn should_trace_failed_call_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCall,
|
result: trace::Res::FailedCall,
|
||||||
subs: vec![]
|
subtraces: 0,
|
||||||
});
|
}];
|
||||||
|
|
||||||
println!("trace: {:?}", result.trace);
|
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -813,35 +854,38 @@ fn should_trace_call_with_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
|
||||||
depth: 0,
|
let expected_trace = vec![FlatTrace {
|
||||||
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(69),
|
gas_used: U256::from(69),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
action: trace::Action::Call(trace::Call {
|
subtraces: 0,
|
||||||
from: 0xa.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
to: 0xb.into(),
|
from: 0xa.into(),
|
||||||
value: 0.into(),
|
to: 0xb.into(),
|
||||||
gas: 78934.into(),
|
value: 0.into(),
|
||||||
input: vec![],
|
gas: 78934.into(),
|
||||||
}),
|
input: vec![],
|
||||||
result: trace::Res::Call(trace::CallResult {
|
call_type: CallType::Call,
|
||||||
gas_used: U256::from(3),
|
}),
|
||||||
output: vec![]
|
result: trace::Res::Call(trace::CallResult {
|
||||||
}),
|
gas_used: U256::from(3),
|
||||||
subs: vec![]
|
output: vec![]
|
||||||
}]
|
}),
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -870,32 +914,34 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(31761),
|
gas_used: U256::from(31761),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
action: trace::Action::Call(trace::Call {
|
subtraces: 0,
|
||||||
from: 0xa.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
to: 0xb.into(),
|
from: 0xa.into(),
|
||||||
value: 69.into(),
|
to: 0xb.into(),
|
||||||
gas: 2300.into(),
|
value: 69.into(),
|
||||||
input: vec![],
|
gas: 2300.into(),
|
||||||
}),
|
input: vec![],
|
||||||
result: trace::Res::Call(trace::CallResult::default()),
|
call_type: CallType::Call,
|
||||||
subs: vec![]
|
}),
|
||||||
}]
|
result: trace::Res::Call(trace::CallResult::default()),
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -924,21 +970,22 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 0,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(31761),
|
gas_used: U256::from(31761),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
}];
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -968,32 +1015,34 @@ fn should_trace_failed_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(79_000),
|
gas_used: U256::from(79_000),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
action: trace::Action::Call(trace::Call {
|
subtraces: 0,
|
||||||
from: 0xa.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
to: 0xb.into(),
|
from: 0xa.into(),
|
||||||
value: 0.into(),
|
to: 0xb.into(),
|
||||||
gas: 78934.into(),
|
value: 0.into(),
|
||||||
input: vec![],
|
gas: 78934.into(),
|
||||||
}),
|
input: vec![],
|
||||||
result: trace::Res::FailedCall,
|
call_type: CallType::Call,
|
||||||
subs: vec![]
|
}),
|
||||||
}]
|
result: trace::Res::FailedCall,
|
||||||
});
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -1024,49 +1073,52 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
let expected_trace = vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(135),
|
gas_used: U256::from(135),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
}),
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
action: trace::Action::Call(trace::Call {
|
subtraces: 1,
|
||||||
from: 0xa.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
to: 0xb.into(),
|
from: 0xa.into(),
|
||||||
value: 0.into(),
|
to: 0xb.into(),
|
||||||
gas: 78934.into(),
|
value: 0.into(),
|
||||||
input: vec![],
|
gas: 78934.into(),
|
||||||
}),
|
input: vec![],
|
||||||
result: trace::Res::Call(trace::CallResult {
|
call_type: CallType::Call,
|
||||||
gas_used: U256::from(69),
|
}),
|
||||||
output: vec![]
|
result: trace::Res::Call(trace::CallResult {
|
||||||
}),
|
gas_used: U256::from(69),
|
||||||
subs: vec![Trace {
|
output: vec![]
|
||||||
depth: 2,
|
}),
|
||||||
action: trace::Action::Call(trace::Call {
|
}, FlatTrace {
|
||||||
from: 0xb.into(),
|
trace_address: vec![0, 0].into_iter().collect(),
|
||||||
to: 0xc.into(),
|
subtraces: 0,
|
||||||
value: 0.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
gas: 78868.into(),
|
from: 0xb.into(),
|
||||||
input: vec![],
|
to: 0xc.into(),
|
||||||
}),
|
value: 0.into(),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
gas: 78868.into(),
|
||||||
gas_used: U256::from(3),
|
input: vec![],
|
||||||
output: vec![]
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
subs: vec![]
|
result: trace::Res::Call(trace::CallResult {
|
||||||
}]
|
gas_used: U256::from(3),
|
||||||
}]
|
output: vec![]
|
||||||
});
|
}),
|
||||||
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
@ -1097,46 +1149,104 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
|||||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
let expected_trace = Some(Trace {
|
|
||||||
depth: 0,
|
let expected_trace = vec![FlatTrace {
|
||||||
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
to: 0xa.into(),
|
to: 0xa.into(),
|
||||||
value: 100.into(),
|
value: 100.into(),
|
||||||
gas: 79000.into(),
|
gas: 79000.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
result: trace::Res::Call(trace::CallResult {
|
||||||
gas_used: U256::from(79_000),
|
gas_used: U256::from(79_000),
|
||||||
output: vec![]
|
output: vec![]
|
||||||
}),
|
})
|
||||||
subs: vec![Trace {
|
}, FlatTrace {
|
||||||
depth: 1,
|
trace_address: vec![0].into_iter().collect(),
|
||||||
|
subtraces: 1,
|
||||||
action: trace::Action::Call(trace::Call {
|
action: trace::Action::Call(trace::Call {
|
||||||
from: 0xa.into(),
|
from: 0xa.into(),
|
||||||
to: 0xb.into(),
|
to: 0xb.into(),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
gas: 78934.into(),
|
gas: 78934.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
}),
|
call_type: CallType::Call,
|
||||||
result: trace::Res::FailedCall,
|
}),
|
||||||
subs: vec![Trace {
|
result: trace::Res::FailedCall,
|
||||||
depth: 2,
|
}, FlatTrace {
|
||||||
action: trace::Action::Call(trace::Call {
|
trace_address: vec![0, 0].into_iter().collect(),
|
||||||
from: 0xb.into(),
|
subtraces: 0,
|
||||||
to: 0xc.into(),
|
action: trace::Action::Call(trace::Call {
|
||||||
value: 0.into(),
|
from: 0xb.into(),
|
||||||
gas: 78868.into(),
|
to: 0xc.into(),
|
||||||
input: vec![],
|
value: 0.into(),
|
||||||
}),
|
gas: 78868.into(),
|
||||||
result: trace::Res::Call(trace::CallResult {
|
call_type: CallType::Call,
|
||||||
gas_used: U256::from(3),
|
input: vec![],
|
||||||
output: vec![]
|
}),
|
||||||
}),
|
result: trace::Res::Call(trace::CallResult {
|
||||||
subs: vec![]
|
gas_used: U256::from(3),
|
||||||
}]
|
output: vec![]
|
||||||
}]
|
}),
|
||||||
});
|
}];
|
||||||
|
|
||||||
|
assert_eq!(result.trace, expected_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_trace_suicide() {
|
||||||
|
init_log();
|
||||||
|
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let mut state = get_temp_state_in(temp.as_path());
|
||||||
|
|
||||||
|
let mut info = EnvInfo::default();
|
||||||
|
info.gas_limit = 1_000_000.into();
|
||||||
|
let engine = TestEngine::new(5);
|
||||||
|
|
||||||
|
let t = Transaction {
|
||||||
|
nonce: 0.into(),
|
||||||
|
gas_price: 0.into(),
|
||||||
|
gas: 100_000.into(),
|
||||||
|
action: Action::Call(0xa.into()),
|
||||||
|
value: 100.into(),
|
||||||
|
data: vec![],
|
||||||
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
|
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
|
||||||
|
state.add_balance(&0xa.into(), &50.into());
|
||||||
|
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
|
||||||
|
let vm_factory = Default::default();
|
||||||
|
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||||
|
let expected_trace = vec![FlatTrace {
|
||||||
|
trace_address: Default::default(),
|
||||||
|
subtraces: 1,
|
||||||
|
action: trace::Action::Call(trace::Call {
|
||||||
|
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
|
||||||
|
to: 0xa.into(),
|
||||||
|
value: 100.into(),
|
||||||
|
gas: 79000.into(),
|
||||||
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
|
}),
|
||||||
|
result: trace::Res::Call(trace::CallResult {
|
||||||
|
gas_used: 3.into(),
|
||||||
|
output: vec![]
|
||||||
|
}),
|
||||||
|
}, FlatTrace {
|
||||||
|
trace_address: vec![0].into_iter().collect(),
|
||||||
|
subtraces: 0,
|
||||||
|
action: trace::Action::Suicide(trace::Suicide {
|
||||||
|
address: 0xa.into(),
|
||||||
|
refund_address: 0xb.into(),
|
||||||
|
balance: 150.into(),
|
||||||
|
}),
|
||||||
|
result: trace::Res::None,
|
||||||
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
}
|
}
|
||||||
|
@ -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 bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
|
||||||
use util::{H256, H264, Database, DatabaseConfig, DBTransaction};
|
use util::{H256, H264, Database, DatabaseConfig, DBTransaction};
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
|
use trace::{LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
|
||||||
DatabaseExtras, Error};
|
DatabaseExtras, Error};
|
||||||
use db::{Key, Writable, Readable, CacheUpdatePolicy};
|
use db::{Key, Writable, Readable, CacheUpdatePolicy};
|
||||||
use blooms;
|
use blooms;
|
||||||
@ -40,7 +40,7 @@ enum TraceDBIndex {
|
|||||||
BloomGroups = 1,
|
BloomGroups = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Key<BlockTraces> for H256 {
|
impl Key<FlatBlockTraces> for H256 {
|
||||||
type Target = H264;
|
type Target = H264;
|
||||||
|
|
||||||
fn key(&self) -> H264 {
|
fn key(&self) -> H264 {
|
||||||
@ -91,7 +91,7 @@ impl Key<blooms::BloomGroup> for TraceGroupPosition {
|
|||||||
/// Trace database.
|
/// Trace database.
|
||||||
pub struct TraceDB<T> where T: DatabaseExtras {
|
pub struct TraceDB<T> where T: DatabaseExtras {
|
||||||
// cache
|
// cache
|
||||||
traces: RwLock<HashMap<H256, BlockTraces>>,
|
traces: RwLock<HashMap<H256, FlatBlockTraces>>,
|
||||||
blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>,
|
blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>,
|
||||||
// db
|
// db
|
||||||
tracesdb: Database,
|
tracesdb: Database,
|
||||||
@ -153,15 +153,13 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns traces for block with hash.
|
/// Returns traces for block with hash.
|
||||||
fn traces(&self, block_hash: &H256) -> Option<BlockTraces> {
|
fn traces(&self, block_hash: &H256) -> Option<FlatBlockTraces> {
|
||||||
self.tracesdb.read_with_cache(&self.traces, block_hash)
|
self.tracesdb.read_with_cache(&self.traces, block_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns vector of transaction traces for given block.
|
/// Returns vector of transaction traces for given block.
|
||||||
fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> {
|
fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> {
|
||||||
self.traces(block_hash)
|
self.traces(block_hash).map(Into::into)
|
||||||
.map(FlatBlockTraces::from)
|
|
||||||
.map(Into::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matching_block_traces(
|
fn matching_block_traces(
|
||||||
@ -199,7 +197,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
action: trace.action,
|
action: trace.action,
|
||||||
result: trace.result,
|
result: trace.result,
|
||||||
subtraces: trace.subtraces,
|
subtraces: trace.subtraces,
|
||||||
trace_address: trace.trace_address,
|
trace_address: trace.trace_address.into_iter().collect(),
|
||||||
transaction_number: tx_number,
|
transaction_number: tx_number,
|
||||||
transaction_hash: tx_hash.clone(),
|
transaction_hash: tx_hash.clone(),
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
@ -265,12 +263,13 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
||||||
|
let trace_position_deq = trace_position.into_iter().collect();
|
||||||
self.extras.block_hash(block_number)
|
self.extras.block_hash(block_number)
|
||||||
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
||||||
.and_then(|traces| traces.into_iter().nth(tx_position))
|
.and_then(|traces| traces.into_iter().nth(tx_position))
|
||||||
.map(Into::<Vec<FlatTrace>>::into)
|
.map(Into::<Vec<FlatTrace>>::into)
|
||||||
// this may and should be optimized
|
// this may and should be optimized
|
||||||
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position))
|
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq))
|
||||||
.map(|trace| {
|
.map(|trace| {
|
||||||
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
|
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
|
||||||
.expect("Expected to find transaction hash. Database is probably corrupted");
|
.expect("Expected to find transaction hash. Database is probably corrupted");
|
||||||
@ -279,7 +278,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
action: trace.action,
|
action: trace.action,
|
||||||
result: trace.result,
|
result: trace.result,
|
||||||
subtraces: trace.subtraces,
|
subtraces: trace.subtraces,
|
||||||
trace_address: trace.trace_address,
|
trace_address: trace.trace_address.into_iter().collect(),
|
||||||
transaction_number: tx_position,
|
transaction_number: tx_position,
|
||||||
transaction_hash: tx_hash,
|
transaction_hash: tx_hash,
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
@ -303,7 +302,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
action: trace.action,
|
action: trace.action,
|
||||||
result: trace.result,
|
result: trace.result,
|
||||||
subtraces: trace.subtraces,
|
subtraces: trace.subtraces,
|
||||||
trace_address: trace.trace_address,
|
trace_address: trace.trace_address.into_iter().collect(),
|
||||||
transaction_number: tx_position,
|
transaction_number: tx_position,
|
||||||
transaction_hash: tx_hash.clone(),
|
transaction_hash: tx_hash.clone(),
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
@ -330,7 +329,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
action: trace.action,
|
action: trace.action,
|
||||||
result: trace.result,
|
result: trace.result,
|
||||||
subtraces: trace.subtraces,
|
subtraces: trace.subtraces,
|
||||||
trace_address: trace.trace_address,
|
trace_address: trace.trace_address.into_iter().collect(),
|
||||||
transaction_number: tx_position,
|
transaction_number: tx_position,
|
||||||
transaction_hash: tx_hash.clone(),
|
transaction_hash: tx_hash.clone(),
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
@ -353,8 +352,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
.expect("Expected to find block hash. Extras db is probably corrupted");
|
.expect("Expected to find block hash. Extras db is probably corrupted");
|
||||||
let traces = self.traces(&hash)
|
let traces = self.traces(&hash)
|
||||||
.expect("Expected to find a trace. Db is probably corrupted.");
|
.expect("Expected to find a trace. Db is probably corrupted.");
|
||||||
let flat_block = FlatBlockTraces::from(traces);
|
self.matching_block_traces(filter, traces, hash, number)
|
||||||
self.matching_block_traces(filter, flat_block, hash, number)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -368,8 +366,10 @@ mod tests {
|
|||||||
use devtools::RandomTempPath;
|
use devtools::RandomTempPath;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
|
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
|
||||||
use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter};
|
use trace::{Filter, LocalizedTrace, AddressesFilter};
|
||||||
use trace::trace::{Call, Action, Res};
|
use trace::trace::{Call, Action, Res};
|
||||||
|
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
struct NoopExtras;
|
struct NoopExtras;
|
||||||
|
|
||||||
@ -487,18 +487,19 @@ mod tests {
|
|||||||
|
|
||||||
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
|
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
|
||||||
ImportRequest {
|
ImportRequest {
|
||||||
traces: BlockTraces::from(vec![Trace {
|
traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace {
|
||||||
depth: 0,
|
trace_address: Default::default(),
|
||||||
|
subtraces: 0,
|
||||||
action: Action::Call(Call {
|
action: Action::Call(Call {
|
||||||
from: Address::from(1),
|
from: 1.into(),
|
||||||
to: Address::from(2),
|
to: 2.into(),
|
||||||
value: U256::from(3),
|
value: 3.into(),
|
||||||
gas: U256::from(4),
|
gas: 4.into(),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall,
|
||||||
subs: vec![],
|
}])]),
|
||||||
}]),
|
|
||||||
block_hash: block_hash.clone(),
|
block_hash: block_hash.clone(),
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
enacted: vec![block_hash],
|
enacted: vec![block_hash],
|
||||||
@ -514,6 +515,7 @@ mod tests {
|
|||||||
value: U256::from(3),
|
value: U256::from(3),
|
||||||
gas: U256::from(4),
|
gas: U256::from(4),
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall,
|
||||||
trace_address: vec![],
|
trace_address: vec![],
|
||||||
|
@ -18,13 +18,53 @@
|
|||||||
|
|
||||||
use util::{Bytes, Address, U256};
|
use util::{Bytes, Address, U256};
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
|
||||||
use trace::{Tracer, VMTracer};
|
use trace::{Tracer, VMTracer, FlatTrace};
|
||||||
|
|
||||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ExecutiveTracer {
|
pub struct ExecutiveTracer {
|
||||||
traces: Vec<Trace>,
|
traces: Vec<FlatTrace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn top_level_subtraces(traces: &[FlatTrace]) -> usize {
|
||||||
|
traces.iter().filter(|t| t.trace_address.is_empty()).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_trace_address(traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
|
||||||
|
// input traces are expected to be ordered like
|
||||||
|
// []
|
||||||
|
// [0]
|
||||||
|
// [0, 0]
|
||||||
|
// [0, 1]
|
||||||
|
// []
|
||||||
|
// [0]
|
||||||
|
//
|
||||||
|
// so they can be transformed to
|
||||||
|
//
|
||||||
|
// [0]
|
||||||
|
// [0, 0]
|
||||||
|
// [0, 0, 0]
|
||||||
|
// [0, 0, 1]
|
||||||
|
// [1]
|
||||||
|
// [1, 0]
|
||||||
|
let mut top_subtrace_index = 0;
|
||||||
|
let mut subtrace_subtraces_left = 0;
|
||||||
|
traces.into_iter().map(|mut trace| {
|
||||||
|
let is_top_subtrace = trace.trace_address.is_empty();
|
||||||
|
trace.trace_address.push_front(top_subtrace_index);
|
||||||
|
|
||||||
|
if is_top_subtrace {
|
||||||
|
subtrace_subtraces_left = trace.subtraces;
|
||||||
|
} else {
|
||||||
|
subtrace_subtraces_left -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if subtrace_subtraces_left == 0 {
|
||||||
|
top_subtrace_index += 1;
|
||||||
|
}
|
||||||
|
trace
|
||||||
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tracer for ExecutiveTracer {
|
impl Tracer for ExecutiveTracer {
|
||||||
@ -40,59 +80,67 @@ impl Tracer for ExecutiveTracer {
|
|||||||
Some(vec![])
|
Some(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, subs: Vec<FlatTrace>) {
|
||||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
let trace = FlatTrace {
|
||||||
if delegate_call {
|
trace_address: Default::default(),
|
||||||
return;
|
subtraces: top_level_subtraces(&subs),
|
||||||
}
|
|
||||||
|
|
||||||
let trace = Trace {
|
|
||||||
depth: depth,
|
|
||||||
subs: subs,
|
|
||||||
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::Call(CallResult {
|
result: Res::Call(CallResult {
|
||||||
gas_used: gas_used,
|
gas_used: gas_used,
|
||||||
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
|
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
|
self.traces.extend(update_trace_address(subs));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, depth: usize, subs: Vec<Trace>) {
|
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, subs: Vec<FlatTrace>) {
|
||||||
let trace = Trace {
|
let trace = FlatTrace {
|
||||||
depth: depth,
|
subtraces: top_level_subtraces(&subs),
|
||||||
subs: subs,
|
|
||||||
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::Create(CreateResult {
|
result: Res::Create(CreateResult {
|
||||||
gas_used: gas_used,
|
gas_used: gas_used,
|
||||||
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
|
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
|
||||||
address: address
|
address: address
|
||||||
})
|
}),
|
||||||
|
trace_address: Default::default(),
|
||||||
};
|
};
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
|
self.traces.extend(update_trace_address(subs));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
|
||||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
let trace = FlatTrace {
|
||||||
if delegate_call {
|
trace_address: Default::default(),
|
||||||
return;
|
subtraces: top_level_subtraces(&subs),
|
||||||
}
|
|
||||||
|
|
||||||
let trace = Trace {
|
|
||||||
depth: depth,
|
|
||||||
subs: subs,
|
|
||||||
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall,
|
||||||
};
|
};
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
|
self.traces.extend(update_trace_address(subs));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>) {
|
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
|
||||||
let trace = Trace {
|
let trace = FlatTrace {
|
||||||
depth: depth,
|
subtraces: top_level_subtraces(&subs),
|
||||||
subs: subs,
|
|
||||||
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::FailedCreate,
|
result: Res::FailedCreate,
|
||||||
|
trace_address: Default::default(),
|
||||||
|
};
|
||||||
|
self.traces.push(trace);
|
||||||
|
self.traces.extend(update_trace_address(subs));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
|
||||||
|
let trace = FlatTrace {
|
||||||
|
subtraces: 0,
|
||||||
|
action: Action::Suicide(Suicide {
|
||||||
|
address: address,
|
||||||
|
refund_address: refund_address,
|
||||||
|
balance: balance,
|
||||||
|
}),
|
||||||
|
result: Res::None,
|
||||||
|
trace_address: Default::default(),
|
||||||
};
|
};
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
}
|
}
|
||||||
@ -101,7 +149,7 @@ impl Tracer for ExecutiveTracer {
|
|||||||
ExecutiveTracer::default()
|
ExecutiveTracer::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traces(self) -> Vec<Trace> {
|
fn traces(self) -> Vec<FlatTrace> {
|
||||||
self.traces
|
self.traces
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
//! Traces import request.
|
||||||
use util::H256;
|
use util::H256;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use trace::BlockTraces;
|
use trace::FlatBlockTraces;
|
||||||
|
|
||||||
/// Traces import request.
|
/// Traces import request.
|
||||||
pub struct ImportRequest {
|
pub struct ImportRequest {
|
||||||
/// Traces to import.
|
/// Traces to import.
|
||||||
pub traces: BlockTraces,
|
pub traces: FlatBlockTraces,
|
||||||
/// Hash of traces block.
|
/// Hash of traces block.
|
||||||
pub block_hash: H256,
|
pub block_hash: H256,
|
||||||
/// Number of traces block.
|
/// Number of traces block.
|
||||||
|
@ -16,22 +16,20 @@
|
|||||||
|
|
||||||
//! Tracing
|
//! Tracing
|
||||||
|
|
||||||
mod block;
|
|
||||||
mod bloom;
|
mod bloom;
|
||||||
mod config;
|
mod config;
|
||||||
mod db;
|
mod db;
|
||||||
mod error;
|
mod error;
|
||||||
mod executive_tracer;
|
mod executive_tracer;
|
||||||
pub mod flat;
|
|
||||||
mod import;
|
mod import;
|
||||||
mod noop_tracer;
|
mod noop_tracer;
|
||||||
|
|
||||||
pub use types::trace_types::*;
|
pub use types::trace_types::*;
|
||||||
pub use self::block::BlockTraces;
|
|
||||||
pub use self::config::{Config, Switch};
|
pub use self::config::{Config, Switch};
|
||||||
pub use self::db::TraceDB;
|
pub use self::db::TraceDB;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||||
|
pub use types::trace_types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
|
||||||
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
|
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
|
||||||
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
|
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
|
||||||
pub use types::trace_types::filter::{Filter, AddressesFilter};
|
pub use types::trace_types::filter::{Filter, AddressesFilter};
|
||||||
@ -59,9 +57,7 @@ pub trait Tracer: Send {
|
|||||||
call: Option<Call>,
|
call: Option<Call>,
|
||||||
gas_used: U256,
|
gas_used: U256,
|
||||||
output: Option<Bytes>,
|
output: Option<Bytes>,
|
||||||
depth: usize,
|
subs: Vec<FlatTrace>,
|
||||||
subs: Vec<Trace>,
|
|
||||||
delegate_call: bool
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Stores trace create info.
|
/// Stores trace create info.
|
||||||
@ -71,21 +67,23 @@ pub trait Tracer: Send {
|
|||||||
gas_used: U256,
|
gas_used: U256,
|
||||||
code: Option<Bytes>,
|
code: Option<Bytes>,
|
||||||
address: Address,
|
address: Address,
|
||||||
depth: usize,
|
subs: Vec<FlatTrace>
|
||||||
subs: Vec<Trace>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Stores failed call trace.
|
/// Stores failed call trace.
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool);
|
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>);
|
||||||
|
|
||||||
/// Stores failed create trace.
|
/// Stores failed create trace.
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);
|
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>);
|
||||||
|
|
||||||
|
/// Stores suicide info.
|
||||||
|
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
||||||
|
|
||||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||||
fn subtracer(&self) -> Self where Self: Sized;
|
fn subtracer(&self) -> Self where Self: Sized;
|
||||||
|
|
||||||
/// Consumes self and returns all traces.
|
/// Consumes self and returns all traces.
|
||||||
fn traces(self) -> Vec<Trace>;
|
fn traces(self) -> Vec<FlatTrace>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by executive to build VM traces.
|
/// Used by executive to build VM traces.
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
use util::{Bytes, Address, U256};
|
use util::{Bytes, Address, U256};
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use trace::{Tracer, VMTracer};
|
use trace::{Tracer, VMTracer, FlatTrace};
|
||||||
use trace::trace::{Trace, Call, Create, VMTrace};
|
use trace::trace::{Call, Create, VMTrace};
|
||||||
|
|
||||||
/// Nonoperative tracer. Does not trace anything.
|
/// Nonoperative tracer. Does not trace anything.
|
||||||
pub struct NoopTracer;
|
pub struct NoopTracer;
|
||||||
@ -37,29 +37,32 @@ impl Tracer for NoopTracer {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>, _: bool) {
|
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: Vec<FlatTrace>) {
|
||||||
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
||||||
assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: usize, _: Vec<Trace>) {
|
fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: Vec<FlatTrace>) {
|
||||||
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
||||||
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, _: usize, _: Vec<Trace>, _: bool) {
|
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) {
|
||||||
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, _: usize, _: Vec<Trace>) {
|
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) {
|
||||||
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) {
|
||||||
|
}
|
||||||
|
|
||||||
fn subtracer(&self) -> Self {
|
fn subtracer(&self) -> Self {
|
||||||
NoopTracer
|
NoopTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traces(self) -> Vec<Trace> {
|
fn traces(self) -> Vec<FlatTrace> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::Bytes;
|
use util::Bytes;
|
||||||
use trace::{Trace, VMTrace};
|
use util::rlp::*;
|
||||||
|
use trace::{VMTrace, FlatTrace};
|
||||||
use types::log_entry::LogEntry;
|
use types::log_entry::LogEntry;
|
||||||
use types::state_diff::StateDiff;
|
use types::state_diff::StateDiff;
|
||||||
use ipc::binary::BinaryConvertError;
|
use ipc::binary::BinaryConvertError;
|
||||||
@ -26,6 +27,43 @@ use std::fmt;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
/// The type of the call-like instruction.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Binary)]
|
||||||
|
pub enum CallType {
|
||||||
|
/// Not a CALL.
|
||||||
|
None,
|
||||||
|
/// CALL.
|
||||||
|
Call,
|
||||||
|
/// CALLCODE.
|
||||||
|
CallCode,
|
||||||
|
/// DELEGATECALL.
|
||||||
|
DelegateCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for CallType {
|
||||||
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
|
let v = match *self {
|
||||||
|
CallType::None => 0u32,
|
||||||
|
CallType::Call => 1,
|
||||||
|
CallType::CallCode => 2,
|
||||||
|
CallType::DelegateCall => 3,
|
||||||
|
};
|
||||||
|
s.append(&v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for CallType {
|
||||||
|
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||||
|
decoder.as_rlp().as_val().and_then(|v| Ok(match v {
|
||||||
|
0u32 => CallType::None,
|
||||||
|
1 => CallType::Call,
|
||||||
|
2 => CallType::CallCode,
|
||||||
|
3 => CallType::DelegateCall,
|
||||||
|
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Transaction execution receipt.
|
/// Transaction execution receipt.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Executed {
|
pub struct Executed {
|
||||||
@ -59,7 +97,7 @@ pub struct Executed {
|
|||||||
/// Transaction output.
|
/// Transaction output.
|
||||||
pub output: Bytes,
|
pub output: Bytes,
|
||||||
/// The trace of this transaction.
|
/// The trace of this transaction.
|
||||||
pub trace: Option<Trace>,
|
pub trace: Vec<FlatTrace>,
|
||||||
/// The VM trace of this transaction.
|
/// The VM trace of this transaction.
|
||||||
pub vm_trace: Option<VMTrace>,
|
pub vm_trace: Option<VMTrace>,
|
||||||
/// The state diff, if we traced it.
|
/// The state diff, if we traced it.
|
||||||
@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError {
|
|||||||
|
|
||||||
/// Transaction execution result.
|
/// Transaction execution result.
|
||||||
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_encode_and_decode_call_type() {
|
||||||
|
use util::rlp;
|
||||||
|
let original = CallType::Call;
|
||||||
|
let encoded = rlp::encode(&original);
|
||||||
|
let decoded = rlp::decode(&encoded);
|
||||||
|
assert_eq!(original, decoded);
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ use util::{Address, FixedHash};
|
|||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
use trace::flat::FlatTrace;
|
use trace::flat::FlatTrace;
|
||||||
use types::trace_types::trace::Action;
|
use types::trace_types::trace::{Action, Res};
|
||||||
use ipc::binary::BinaryConvertError;
|
use ipc::binary::BinaryConvertError;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@ -58,7 +58,7 @@ impl AddressesFilter {
|
|||||||
true => vec![LogBloom::new()],
|
true => vec![LogBloom::new()],
|
||||||
false => self.list.iter()
|
false => self.list.iter()
|
||||||
.map(|address| LogBloom::from_bloomed(&address.sha3()))
|
.map(|address| LogBloom::from_bloomed(&address.sha3()))
|
||||||
.collect()
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ impl AddressesFilter {
|
|||||||
.flat_map(|bloom| self.list.iter()
|
.flat_map(|bloom| self.list.iter()
|
||||||
.map(|address| bloom.with_bloomed(&address.sha3()))
|
.map(|address| bloom.with_bloomed(&address.sha3()))
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
.collect()
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,29 +110,40 @@ impl Filter {
|
|||||||
|
|
||||||
/// Returns true if given trace matches the filter.
|
/// Returns true if given trace matches the filter.
|
||||||
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
||||||
match trace.action {
|
let action = match trace.action {
|
||||||
Action::Call(ref call) => {
|
Action::Call(ref call) => {
|
||||||
let from_matches = self.from_address.matches(&call.from);
|
let from_matches = self.from_address.matches(&call.from);
|
||||||
let to_matches = self.to_address.matches(&call.to);
|
let to_matches = self.to_address.matches(&call.to);
|
||||||
from_matches && to_matches
|
from_matches && to_matches
|
||||||
},
|
}
|
||||||
Action::Create(ref create) => {
|
Action::Create(ref create) => {
|
||||||
let from_matches = self.from_address.matches(&create.from);
|
let from_matches = self.from_address.matches(&create.from);
|
||||||
let to_matches = self.to_address.matches_all();
|
let to_matches = self.to_address.matches_all();
|
||||||
from_matches && to_matches
|
from_matches && to_matches
|
||||||
|
},
|
||||||
|
Action::Suicide(ref suicide) => {
|
||||||
|
let from_matches = self.from_address.matches(&suicide.address);
|
||||||
|
let to_matches = self.to_address.matches(&suicide.refund_address);
|
||||||
|
from_matches && to_matches
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
action || match trace.result {
|
||||||
|
Res::Create(ref create) => self.to_address.matches(&create.address),
|
||||||
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use util::{FixedHash, Address, U256};
|
use util::{FixedHash, Address};
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use trace::trace::{Action, Call, Res};
|
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
|
||||||
use trace::flat::FlatTrace;
|
use trace::flat::FlatTrace;
|
||||||
use trace::{Filter, AddressesFilter};
|
use trace::{Filter, AddressesFilter};
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_trace_filter_bloom_possibilities() {
|
fn empty_trace_filter_bloom_possibilities() {
|
||||||
@ -270,14 +281,15 @@ mod tests {
|
|||||||
|
|
||||||
let trace = FlatTrace {
|
let trace = FlatTrace {
|
||||||
action: Action::Call(Call {
|
action: Action::Call(Call {
|
||||||
from: Address::from(1),
|
from: 1.into(),
|
||||||
to: Address::from(2),
|
to: 2.into(),
|
||||||
value: U256::from(3),
|
value: 3.into(),
|
||||||
gas: U256::from(4),
|
gas: 4.into(),
|
||||||
input: vec![0x5],
|
input: vec![0x5],
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall,
|
||||||
trace_address: vec![0],
|
trace_address: vec![0].into_iter().collect(),
|
||||||
subtraces: 0,
|
subtraces: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -288,5 +300,48 @@ mod tests {
|
|||||||
assert!(f4.matches(&trace));
|
assert!(f4.matches(&trace));
|
||||||
assert!(f5.matches(&trace));
|
assert!(f5.matches(&trace));
|
||||||
assert!(!f6.matches(&trace));
|
assert!(!f6.matches(&trace));
|
||||||
|
|
||||||
|
let trace = FlatTrace {
|
||||||
|
action: Action::Create(Create {
|
||||||
|
from: 1.into(),
|
||||||
|
value: 3.into(),
|
||||||
|
gas: 4.into(),
|
||||||
|
init: vec![0x5],
|
||||||
|
}),
|
||||||
|
result: Res::Create(CreateResult {
|
||||||
|
gas_used: 10.into(),
|
||||||
|
code: vec![],
|
||||||
|
address: 2.into(),
|
||||||
|
}),
|
||||||
|
trace_address: vec![0].into_iter().collect(),
|
||||||
|
subtraces: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(f0.matches(&trace));
|
||||||
|
assert!(f1.matches(&trace));
|
||||||
|
assert!(f2.matches(&trace));
|
||||||
|
assert!(f3.matches(&trace));
|
||||||
|
assert!(f4.matches(&trace));
|
||||||
|
assert!(f5.matches(&trace));
|
||||||
|
assert!(!f6.matches(&trace));
|
||||||
|
|
||||||
|
let trace = FlatTrace {
|
||||||
|
action: Action::Suicide(Suicide {
|
||||||
|
address: 1.into(),
|
||||||
|
refund_address: 2.into(),
|
||||||
|
balance: 3.into(),
|
||||||
|
}),
|
||||||
|
result: Res::None,
|
||||||
|
trace_address: vec![].into_iter().collect(),
|
||||||
|
subtraces: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(f0.matches(&trace));
|
||||||
|
assert!(f1.matches(&trace));
|
||||||
|
assert!(f2.matches(&trace));
|
||||||
|
assert!(f3.matches(&trace));
|
||||||
|
assert!(f4.matches(&trace));
|
||||||
|
assert!(f5.matches(&trace));
|
||||||
|
assert!(!f6.matches(&trace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
//! Types used in the public api
|
||||||
|
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
|
pub mod flat;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
pub mod localized;
|
pub mod localized;
|
||||||
|
@ -21,6 +21,7 @@ use util::rlp::*;
|
|||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
|
use types::executed::CallType;
|
||||||
use ipc::binary::BinaryConvertError;
|
use ipc::binary::BinaryConvertError;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@ -87,6 +88,13 @@ impl Decodable for CreateResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CreateResult {
|
||||||
|
/// Returns bloom.
|
||||||
|
pub fn bloom(&self) -> LogBloom {
|
||||||
|
LogBloom::from_bloomed(&self.address.sha3())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Description of a _call_ action, either a `CALL` operation or a message transction.
|
/// Description of a _call_ action, either a `CALL` operation or a message transction.
|
||||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
@ -100,6 +108,8 @@ pub struct Call {
|
|||||||
pub gas: U256,
|
pub gas: U256,
|
||||||
/// The input data provided to the call.
|
/// The input data provided to the call.
|
||||||
pub input: Bytes,
|
pub input: Bytes,
|
||||||
|
/// The type of the call.
|
||||||
|
pub call_type: CallType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ActionParams> for Call {
|
impl From<ActionParams> for Call {
|
||||||
@ -110,18 +120,20 @@ impl From<ActionParams> for Call {
|
|||||||
value: p.value.value(),
|
value: p.value.value(),
|
||||||
gas: p.gas,
|
gas: p.gas,
|
||||||
input: p.data.unwrap_or_else(Vec::new),
|
input: p.data.unwrap_or_else(Vec::new),
|
||||||
|
call_type: p.call_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for Call {
|
impl Encodable for Call {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
s.begin_list(5);
|
s.begin_list(6);
|
||||||
s.append(&self.from);
|
s.append(&self.from);
|
||||||
s.append(&self.to);
|
s.append(&self.to);
|
||||||
s.append(&self.value);
|
s.append(&self.value);
|
||||||
s.append(&self.gas);
|
s.append(&self.gas);
|
||||||
s.append(&self.input);
|
s.append(&self.input);
|
||||||
|
s.append(&self.call_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +146,7 @@ impl Decodable for Call {
|
|||||||
value: try!(d.val_at(2)),
|
value: try!(d.val_at(2)),
|
||||||
gas: try!(d.val_at(3)),
|
gas: try!(d.val_at(3)),
|
||||||
input: try!(d.val_at(4)),
|
input: try!(d.val_at(4)),
|
||||||
|
call_type: try!(d.val_at(5)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@ -205,6 +218,48 @@ impl Create {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suicide action.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||||
|
pub struct Suicide {
|
||||||
|
/// Suicided address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Suicided contract heir.
|
||||||
|
pub refund_address: Address,
|
||||||
|
/// Balance of the contract just before suicide.
|
||||||
|
pub balance: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Suicide {
|
||||||
|
/// Return suicide action bloom.
|
||||||
|
pub fn bloom(&self) -> LogBloom {
|
||||||
|
LogBloom::from_bloomed(&self.address.sha3())
|
||||||
|
.with_bloomed(&self.refund_address.sha3())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Suicide {
|
||||||
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
|
s.begin_list(3);
|
||||||
|
s.append(&self.address);
|
||||||
|
s.append(&self.refund_address);
|
||||||
|
s.append(&self.balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Suicide {
|
||||||
|
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||||
|
let d = decoder.as_rlp();
|
||||||
|
let res = Suicide {
|
||||||
|
address: try!(d.val_at(0)),
|
||||||
|
refund_address: try!(d.val_at(1)),
|
||||||
|
balance: try!(d.val_at(3)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Description of an action that we trace; will be either a call or a create.
|
/// Description of an action that we trace; will be either a call or a create.
|
||||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
@ -212,6 +267,8 @@ pub enum Action {
|
|||||||
Call(Call),
|
Call(Call),
|
||||||
/// It's a create action.
|
/// It's a create action.
|
||||||
Create(Create),
|
Create(Create),
|
||||||
|
/// Suicide.
|
||||||
|
Suicide(Suicide),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for Action {
|
impl Encodable for Action {
|
||||||
@ -225,6 +282,10 @@ impl Encodable for Action {
|
|||||||
Action::Create(ref create) => {
|
Action::Create(ref create) => {
|
||||||
s.append(&1u8);
|
s.append(&1u8);
|
||||||
s.append(create);
|
s.append(create);
|
||||||
|
},
|
||||||
|
Action::Suicide(ref suicide) => {
|
||||||
|
s.append(&2u8);
|
||||||
|
s.append(suicide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,6 +298,7 @@ impl Decodable for Action {
|
|||||||
match action_type {
|
match action_type {
|
||||||
0 => d.val_at(1).map(Action::Call),
|
0 => d.val_at(1).map(Action::Call),
|
||||||
1 => d.val_at(1).map(Action::Create),
|
1 => d.val_at(1).map(Action::Create),
|
||||||
|
2 => d.val_at(2).map(Action::Suicide),
|
||||||
_ => Err(DecoderError::Custom("Invalid action type.")),
|
_ => Err(DecoderError::Custom("Invalid action type.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,6 +310,7 @@ impl Action {
|
|||||||
match *self {
|
match *self {
|
||||||
Action::Call(ref call) => call.bloom(),
|
Action::Call(ref call) => call.bloom(),
|
||||||
Action::Create(ref create) => create.bloom(),
|
Action::Create(ref create) => create.bloom(),
|
||||||
|
Action::Suicide(ref suicide) => suicide.bloom(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,6 +326,8 @@ pub enum Res {
|
|||||||
FailedCall,
|
FailedCall,
|
||||||
/// Failed create.
|
/// Failed create.
|
||||||
FailedCreate,
|
FailedCreate,
|
||||||
|
/// None
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for Res {
|
impl Encodable for Res {
|
||||||
@ -285,6 +350,10 @@ impl Encodable for Res {
|
|||||||
Res::FailedCreate => {
|
Res::FailedCreate => {
|
||||||
s.begin_list(1);
|
s.begin_list(1);
|
||||||
s.append(&3u8);
|
s.append(&3u8);
|
||||||
|
},
|
||||||
|
Res::None => {
|
||||||
|
s.begin_list(1);
|
||||||
|
s.append(&4u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,53 +368,19 @@ impl Decodable for Res {
|
|||||||
1 => d.val_at(1).map(Res::Create),
|
1 => d.val_at(1).map(Res::Create),
|
||||||
2 => Ok(Res::FailedCall),
|
2 => Ok(Res::FailedCall),
|
||||||
3 => Ok(Res::FailedCreate),
|
3 => Ok(Res::FailedCreate),
|
||||||
|
4 => Ok(Res::None),
|
||||||
_ => Err(DecoderError::Custom("Invalid result type.")),
|
_ => Err(DecoderError::Custom("Invalid result type.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
impl Res {
|
||||||
/// A trace; includes a description of the action being traced and sub traces of each interior action.
|
/// Returns result bloom.
|
||||||
pub struct Trace {
|
|
||||||
/// The number of EVM execution environments active when this action happened; 0 if it's
|
|
||||||
/// the outer action of the transaction.
|
|
||||||
pub depth: usize,
|
|
||||||
/// The action being performed.
|
|
||||||
pub action: Action,
|
|
||||||
/// The sub traces for each interior action performed as part of this call.
|
|
||||||
pub subs: Vec<Trace>,
|
|
||||||
/// The result of the performed action.
|
|
||||||
pub result: Res,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encodable for Trace {
|
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
|
||||||
s.begin_list(4);
|
|
||||||
s.append(&self.depth);
|
|
||||||
s.append(&self.action);
|
|
||||||
s.append(&self.subs);
|
|
||||||
s.append(&self.result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decodable for Trace {
|
|
||||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
|
||||||
let d = decoder.as_rlp();
|
|
||||||
let res = Trace {
|
|
||||||
depth: try!(d.val_at(0)),
|
|
||||||
action: try!(d.val_at(1)),
|
|
||||||
subs: try!(d.val_at(2)),
|
|
||||||
result: try!(d.val_at(3)),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Trace {
|
|
||||||
/// Returns trace bloom.
|
|
||||||
pub fn bloom(&self) -> LogBloom {
|
pub fn bloom(&self) -> LogBloom {
|
||||||
self.subs.iter().fold(self.action.bloom(), |b, s| b | s.bloom())
|
match *self {
|
||||||
|
Res::Create(ref create) => create.bloom(),
|
||||||
|
Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,84 +548,3 @@ impl Decodable for VMTrace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use util::{Address, U256, FixedHash};
|
|
||||||
use util::rlp::{encode, decode};
|
|
||||||
use util::sha3::Hashable;
|
|
||||||
use trace::trace::{Call, CallResult, Create, Res, Action, Trace};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn traces_rlp() {
|
|
||||||
let trace = Trace {
|
|
||||||
depth: 2,
|
|
||||||
action: Action::Call(Call {
|
|
||||||
from: Address::from(1),
|
|
||||||
to: Address::from(2),
|
|
||||||
value: U256::from(3),
|
|
||||||
gas: U256::from(4),
|
|
||||||
input: vec![0x5]
|
|
||||||
}),
|
|
||||||
subs: vec![
|
|
||||||
Trace {
|
|
||||||
depth: 3,
|
|
||||||
action: Action::Create(Create {
|
|
||||||
from: Address::from(6),
|
|
||||||
value: U256::from(7),
|
|
||||||
gas: U256::from(8),
|
|
||||||
init: vec![0x9]
|
|
||||||
}),
|
|
||||||
subs: vec![],
|
|
||||||
result: Res::FailedCreate
|
|
||||||
}
|
|
||||||
],
|
|
||||||
result: Res::Call(CallResult {
|
|
||||||
gas_used: U256::from(10),
|
|
||||||
output: vec![0x11, 0x12]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let encoded = encode(&trace);
|
|
||||||
let decoded: Trace = decode(&encoded);
|
|
||||||
assert_eq!(trace, decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn traces_bloom() {
|
|
||||||
let trace = Trace {
|
|
||||||
depth: 2,
|
|
||||||
action: Action::Call(Call {
|
|
||||||
from: Address::from(1),
|
|
||||||
to: Address::from(2),
|
|
||||||
value: U256::from(3),
|
|
||||||
gas: U256::from(4),
|
|
||||||
input: vec![0x5]
|
|
||||||
}),
|
|
||||||
subs: vec![
|
|
||||||
Trace {
|
|
||||||
depth: 3,
|
|
||||||
action: Action::Create(Create {
|
|
||||||
from: Address::from(6),
|
|
||||||
value: U256::from(7),
|
|
||||||
gas: U256::from(8),
|
|
||||||
init: vec![0x9]
|
|
||||||
}),
|
|
||||||
subs: vec![],
|
|
||||||
result: Res::FailedCreate
|
|
||||||
}
|
|
||||||
],
|
|
||||||
result: Res::Call(CallResult {
|
|
||||||
gas_used: U256::from(10),
|
|
||||||
output: vec![0x11, 0x12]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let bloom = trace.bloom();
|
|
||||||
|
|
||||||
// right now only addresses are bloomed
|
|
||||||
assert!(bloom.contains_bloomed(&Address::from(1).sha3()));
|
|
||||||
assert!(bloom.contains_bloomed(&Address::from(2).sha3()));
|
|
||||||
assert!(!bloom.contains_bloomed(&Address::from(20).sha3()));
|
|
||||||
assert!(bloom.contains_bloomed(&Address::from(6).sha3()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -139,6 +139,74 @@ impl<R: BinaryConvertable, E: BinaryConvertable> BinaryConvertable for Result<R,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> BinaryConvertable for VecDeque<T> where T: BinaryConvertable {
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
match T::len_params() {
|
||||||
|
0 => mem::size_of::<T>() * self.len(),
|
||||||
|
_ => self.iter().fold(0usize, |acc, t| acc + t.size()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
|
||||||
|
let mut offset = 0usize;
|
||||||
|
for item in self.iter() {
|
||||||
|
let next_size = match T::len_params() {
|
||||||
|
0 => mem::size_of::<T>(),
|
||||||
|
_ => { let size = item.size(); length_stack.push_back(size); size },
|
||||||
|
};
|
||||||
|
if next_size > 0 {
|
||||||
|
let item_end = offset + next_size;
|
||||||
|
try!(item.to_bytes(&mut buffer[offset..item_end], length_stack));
|
||||||
|
offset = item_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
|
||||||
|
let mut index = 0;
|
||||||
|
let mut result = Self::with_capacity(
|
||||||
|
match T::len_params() {
|
||||||
|
0 => buffer.len() / mem::size_of::<T>(),
|
||||||
|
_ => 128,
|
||||||
|
});
|
||||||
|
|
||||||
|
if buffer.len() == 0 { return Ok(result); }
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let next_size = match T::len_params() {
|
||||||
|
0 => mem::size_of::<T>(),
|
||||||
|
_ => try!(length_stack.pop_front().ok_or(BinaryConvertError)),
|
||||||
|
};
|
||||||
|
let item = if next_size == 0 {
|
||||||
|
try!(T::from_empty_bytes())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try!(T::from_bytes(&buffer[index..index+next_size], length_stack))
|
||||||
|
};
|
||||||
|
result.push_back(item);
|
||||||
|
|
||||||
|
index = index + next_size;
|
||||||
|
if index == buffer.len() { break; }
|
||||||
|
if index + next_size > buffer.len() {
|
||||||
|
return Err(BinaryConvertError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
|
||||||
|
Ok(Self::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len_params() -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable {
|
impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable {
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
match T::len_params() {
|
match T::len_params() {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
//! Spec params deserialization.
|
//! Spec params deserialization.
|
||||||
|
|
||||||
use uint::Uint;
|
use uint::Uint;
|
||||||
|
use hash::H256;
|
||||||
|
|
||||||
/// Spec params.
|
/// Spec params.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
@ -33,6 +34,12 @@ pub struct Params {
|
|||||||
/// Minimum gas limit.
|
/// Minimum gas limit.
|
||||||
#[serde(rename="minGasLimit")]
|
#[serde(rename="minGasLimit")]
|
||||||
pub min_gas_limit: Uint,
|
pub min_gas_limit: Uint,
|
||||||
|
/// Option fork block number to check.
|
||||||
|
#[serde(rename="forkBlock")]
|
||||||
|
pub fork_block: Option<Uint>,
|
||||||
|
/// Expected fork block hash.
|
||||||
|
#[serde(rename="forkCanonHash")]
|
||||||
|
pub fork_hash: Option<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -75,7 +75,9 @@ mod tests {
|
|||||||
"frontierCompatibilityModeLimit": "0x789b0",
|
"frontierCompatibilityModeLimit": "0x789b0",
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x2",
|
||||||
|
"forkBlock": "0xffffffffffffffff",
|
||||||
|
"forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
|
@ -335,6 +335,7 @@ impl Configuration {
|
|||||||
sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
|
sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
|
||||||
U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
|
U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
|
||||||
});
|
});
|
||||||
|
sync_config.fork_block = spec.fork_block().clone();
|
||||||
sync_config
|
sync_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
|
|
||||||
use std::sync::{Weak, Arc};
|
use std::sync::{Weak, Arc};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use util::H256;
|
use util::H256;
|
||||||
|
use util::rlp::{UntrustedRlp, View};
|
||||||
use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
|
use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
||||||
use v1::traits::Traces;
|
use v1::traits::Traces;
|
||||||
use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace};
|
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults};
|
||||||
|
|
||||||
/// Traces api implementation.
|
/// Traces api implementation.
|
||||||
pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService {
|
pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService {
|
||||||
@ -113,22 +113,30 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
|
|||||||
state_diffing: flags.contains(&("stateDiff".to_owned())),
|
state_diffing: flags.contains(&("stateDiff".to_owned())),
|
||||||
};
|
};
|
||||||
let signed = try!(self.sign_call(request));
|
let signed = try!(self.sign_call(request));
|
||||||
let r = take_weak!(self.client).call(&signed, analytics);
|
match take_weak!(self.client).call(&signed, analytics) {
|
||||||
if let Ok(executed) = r {
|
Ok(e) => to_value(&TraceResults::from(e)),
|
||||||
// TODO maybe add other stuff to this?
|
_ => Ok(Value::Null),
|
||||||
let mut ret = map!["output".to_owned() => to_value(&Bytes(executed.output)).unwrap()];
|
}
|
||||||
if let Some(trace) = executed.trace {
|
})
|
||||||
ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap());
|
}
|
||||||
}
|
|
||||||
if let Some(vm_trace) = executed.vm_trace {
|
fn raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap());
|
trace!(target: "jsonrpc", "call: {:?}", params);
|
||||||
}
|
from_params::<(Bytes, Vec<String>)>(params)
|
||||||
if let Some(state_diff) = executed.state_diff {
|
.and_then(|(raw_transaction, flags)| {
|
||||||
ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap());
|
let raw_transaction = raw_transaction.to_vec();
|
||||||
}
|
let analytics = CallAnalytics {
|
||||||
return Ok(Value::Object(ret))
|
transaction_tracing: flags.contains(&("trace".to_owned())),
|
||||||
|
vm_tracing: flags.contains(&("vmTrace".to_owned())),
|
||||||
|
state_diffing: flags.contains(&("stateDiff".to_owned())),
|
||||||
|
};
|
||||||
|
match UntrustedRlp::new(&raw_transaction).as_val() {
|
||||||
|
Ok(signed) => match take_weak!(self.client).call(&signed, analytics) {
|
||||||
|
Ok(e) => to_value(&TraceResults::from(e)),
|
||||||
|
_ => Ok(Value::Null),
|
||||||
|
},
|
||||||
|
Err(_) => Err(Error::invalid_params()),
|
||||||
}
|
}
|
||||||
Ok(Value::Null)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,7 +366,7 @@ fn rpc_eth_pending_transaction_by_hash() {
|
|||||||
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
|
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
|
||||||
let request = r#"{
|
let request = r#"{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "eth_getTransactionByHash",
|
"method": "eth_getTransactionByHash",
|
||||||
@ -430,7 +430,7 @@ fn rpc_eth_call() {
|
|||||||
logs: vec![],
|
logs: vec![],
|
||||||
contracts_created: vec![],
|
contracts_created: vec![],
|
||||||
output: vec![0x12, 0x34, 0xff],
|
output: vec![0x12, 0x34, 0xff],
|
||||||
trace: None,
|
trace: vec![],
|
||||||
vm_trace: None,
|
vm_trace: None,
|
||||||
state_diff: None,
|
state_diff: None,
|
||||||
});
|
});
|
||||||
@ -465,7 +465,7 @@ fn rpc_eth_call_default_block() {
|
|||||||
logs: vec![],
|
logs: vec![],
|
||||||
contracts_created: vec![],
|
contracts_created: vec![],
|
||||||
output: vec![0x12, 0x34, 0xff],
|
output: vec![0x12, 0x34, 0xff],
|
||||||
trace: None,
|
trace: vec![],
|
||||||
vm_trace: None,
|
vm_trace: None,
|
||||||
state_diff: None,
|
state_diff: None,
|
||||||
});
|
});
|
||||||
@ -499,7 +499,7 @@ fn rpc_eth_estimate_gas() {
|
|||||||
logs: vec![],
|
logs: vec![],
|
||||||
contracts_created: vec![],
|
contracts_created: vec![],
|
||||||
output: vec![0x12, 0x34, 0xff],
|
output: vec![0x12, 0x34, 0xff],
|
||||||
trace: None,
|
trace: vec![],
|
||||||
vm_trace: None,
|
vm_trace: None,
|
||||||
state_diff: None,
|
state_diff: None,
|
||||||
});
|
});
|
||||||
@ -534,7 +534,7 @@ fn rpc_eth_estimate_gas_default_block() {
|
|||||||
logs: vec![],
|
logs: vec![],
|
||||||
contracts_created: vec![],
|
contracts_created: vec![],
|
||||||
output: vec![0x12, 0x34, 0xff],
|
output: vec![0x12, 0x34, 0xff],
|
||||||
trace: None,
|
trace: vec![],
|
||||||
vm_trace: None,
|
vm_trace: None,
|
||||||
state_diff: None,
|
state_diff: None,
|
||||||
});
|
});
|
||||||
|
@ -35,6 +35,9 @@ pub trait Traces: Sized + Send + Sync + 'static {
|
|||||||
/// Executes the given call and returns a number of possible traces for it.
|
/// Executes the given call and returns a number of possible traces for it.
|
||||||
fn call(&self, _: Params) -> Result<Value, Error>;
|
fn call(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Executes the given raw transaction and returns a number of possible traces for it.
|
||||||
|
fn raw_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Should be used to convert object to io delegate.
|
/// Should be used to convert object to io delegate.
|
||||||
fn to_delegate(self) -> IoDelegate<Self> {
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
@ -43,6 +46,7 @@ pub trait Traces: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("trace_transaction", Traces::transaction_traces);
|
delegate.add_method("trace_transaction", Traces::transaction_traces);
|
||||||
delegate.add_method("trace_block", Traces::block_traces);
|
delegate.add_method("trace_block", Traces::block_traces);
|
||||||
delegate.add_method("trace_call", Traces::call);
|
delegate.add_method("trace_call", Traces::call);
|
||||||
|
delegate.add_method("trace_rawTransaction", Traces::raw_transaction);
|
||||||
|
|
||||||
delegate
|
delegate
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ mod tests {
|
|||||||
fn test_serialize_block_transactions() {
|
fn test_serialize_block_transactions() {
|
||||||
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#);
|
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}]"#);
|
||||||
|
|
||||||
let t = BlockTransactions::Hashes(vec![H256::default()]);
|
let t = BlockTransactions::Hashes(vec![H256::default()]);
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
@ -41,5 +41,5 @@ pub use self::transaction::Transaction;
|
|||||||
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
||||||
pub use self::call_request::CallRequest;
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace};
|
pub use self::trace::{LocalizedTrace, TraceResults};
|
||||||
pub use self::trace_filter::TraceFilter;
|
pub use self::trace_filter::TraceFilter;
|
||||||
|
@ -18,11 +18,13 @@ use std::collections::BTreeMap;
|
|||||||
use util::{Address, U256, H256, Uint};
|
use util::{Address, U256, H256, Uint};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use ethcore::trace::trace;
|
use ethcore::trace::trace;
|
||||||
use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace};
|
use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace};
|
||||||
use ethcore::trace as et;
|
use ethcore::trace as et;
|
||||||
use ethcore::state_diff;
|
use ethcore::state_diff;
|
||||||
use ethcore::account_diff;
|
use ethcore::account_diff;
|
||||||
use v1::types::Bytes;
|
use v1::types::{Bytes};
|
||||||
|
use ethcore::client::Executed;
|
||||||
|
use ethcore::executed;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
/// A diff of some chunk of memory.
|
/// A diff of some chunk of memory.
|
||||||
@ -193,6 +195,7 @@ impl From<account_diff::AccountDiff> for AccountDiff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
/// Serde-friendly `StateDiff` shadow.
|
/// Serde-friendly `StateDiff` shadow.
|
||||||
pub struct StateDiff(BTreeMap<Address, AccountDiff>);
|
pub struct StateDiff(BTreeMap<Address, AccountDiff>);
|
||||||
|
|
||||||
@ -233,6 +236,34 @@ impl From<trace::Create> for Create {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call type.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum CallType {
|
||||||
|
/// None
|
||||||
|
#[serde(rename="none")]
|
||||||
|
None,
|
||||||
|
/// Call
|
||||||
|
#[serde(rename="call")]
|
||||||
|
Call,
|
||||||
|
/// Call code
|
||||||
|
#[serde(rename="callcode")]
|
||||||
|
CallCode,
|
||||||
|
/// Delegate call
|
||||||
|
#[serde(rename="delegatecall")]
|
||||||
|
DelegateCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<executed::CallType> for CallType {
|
||||||
|
fn from(c: executed::CallType) -> Self {
|
||||||
|
match c {
|
||||||
|
executed::CallType::None => CallType::None,
|
||||||
|
executed::CallType::Call => CallType::Call,
|
||||||
|
executed::CallType::CallCode => CallType::CallCode,
|
||||||
|
executed::CallType::DelegateCall => CallType::DelegateCall,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call response
|
/// Call response
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
@ -246,6 +277,9 @@ pub struct Call {
|
|||||||
gas: U256,
|
gas: U256,
|
||||||
/// Input data
|
/// Input data
|
||||||
input: Bytes,
|
input: Bytes,
|
||||||
|
/// The type of the call.
|
||||||
|
#[serde(rename="callType")]
|
||||||
|
call_type: CallType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<trace::Call> for Call {
|
impl From<trace::Call> for Call {
|
||||||
@ -256,6 +290,29 @@ impl From<trace::Call> for Call {
|
|||||||
value: c.value,
|
value: c.value,
|
||||||
gas: c.gas,
|
gas: c.gas,
|
||||||
input: Bytes::new(c.input),
|
input: Bytes::new(c.input),
|
||||||
|
call_type: c.call_type.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suicide
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Suicide {
|
||||||
|
/// Address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Refund address.
|
||||||
|
#[serde(rename="refundAddress")]
|
||||||
|
pub refund_address: Address,
|
||||||
|
/// Balance.
|
||||||
|
pub balance: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<trace::Suicide> for Suicide {
|
||||||
|
fn from(s: trace::Suicide) -> Self {
|
||||||
|
Suicide {
|
||||||
|
address: s.address.into(),
|
||||||
|
refund_address: s.refund_address.into(),
|
||||||
|
balance: s.balance.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,13 +326,17 @@ pub enum Action {
|
|||||||
/// Create
|
/// Create
|
||||||
#[serde(rename="create")]
|
#[serde(rename="create")]
|
||||||
Create(Create),
|
Create(Create),
|
||||||
|
/// Suicide
|
||||||
|
#[serde(rename="suicide")]
|
||||||
|
Suicide(Suicide),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<trace::Action> for Action {
|
impl From<trace::Action> for Action {
|
||||||
fn from(c: trace::Action) -> Self {
|
fn from(c: trace::Action) -> Self {
|
||||||
match c {
|
match c {
|
||||||
trace::Action::Call(call) => Action::Call(Call::from(call)),
|
trace::Action::Call(call) => Action::Call(call.into()),
|
||||||
trace::Action::Create(create) => Action::Create(Create::from(create)),
|
trace::Action::Create(create) => Action::Create(create.into()),
|
||||||
|
trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,6 +397,9 @@ pub enum Res {
|
|||||||
/// Creation failure
|
/// Creation failure
|
||||||
#[serde(rename="failedCreate")]
|
#[serde(rename="failedCreate")]
|
||||||
FailedCreate,
|
FailedCreate,
|
||||||
|
/// None
|
||||||
|
#[serde(rename="none")]
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<trace::Res> for Res {
|
impl From<trace::Res> for Res {
|
||||||
@ -345,6 +409,7 @@ impl From<trace::Res> for Res {
|
|||||||
trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
|
trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
|
||||||
trace::Res::FailedCall => Res::FailedCall,
|
trace::Res::FailedCall => Res::FailedCall,
|
||||||
trace::Res::FailedCreate => Res::FailedCreate,
|
trace::Res::FailedCreate => Res::FailedCreate,
|
||||||
|
trace::Res::None => Res::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,23 +458,50 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
|
|||||||
/// Trace
|
/// Trace
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Trace {
|
pub struct Trace {
|
||||||
/// Depth within the call trace tree.
|
/// Trace address
|
||||||
depth: usize,
|
#[serde(rename="traceAddress")]
|
||||||
|
trace_address: Vec<U256>,
|
||||||
|
/// Subtraces
|
||||||
|
subtraces: U256,
|
||||||
/// Action
|
/// Action
|
||||||
action: Action,
|
action: Action,
|
||||||
/// Result
|
/// Result
|
||||||
result: Res,
|
result: Res,
|
||||||
/// Subtraces
|
|
||||||
subtraces: Vec<Trace>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EthTrace> for Trace {
|
impl From<FlatTrace> for Trace {
|
||||||
fn from(t: EthTrace) -> Self {
|
fn from(t: FlatTrace) -> Self {
|
||||||
Trace {
|
Trace {
|
||||||
depth: t.depth.into(),
|
trace_address: t.trace_address.into_iter().map(Into::into).collect(),
|
||||||
|
subtraces: t.subtraces.into(),
|
||||||
action: t.action.into(),
|
action: t.action.into(),
|
||||||
result: t.result.into(),
|
result: t.result.into(),
|
||||||
subtraces: t.subs.into_iter().map(From::from).collect(),
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
/// A diff of some chunk of memory.
|
||||||
|
pub struct TraceResults {
|
||||||
|
/// The output of the call/create
|
||||||
|
pub output: Vec<u8>,
|
||||||
|
/// The transaction trace.
|
||||||
|
pub trace: Vec<Trace>,
|
||||||
|
/// The transaction trace.
|
||||||
|
#[serde(rename="vmTrace")]
|
||||||
|
pub vm_trace: Option<VMTrace>,
|
||||||
|
/// The transaction trace.
|
||||||
|
#[serde(rename="stateDiff")]
|
||||||
|
pub state_diff: Option<StateDiff>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Executed> for TraceResults {
|
||||||
|
fn from(t: Executed) -> Self {
|
||||||
|
TraceResults {
|
||||||
|
output: t.output.into(),
|
||||||
|
trace: t.trace.into_iter().map(Into::into).collect(),
|
||||||
|
vm_trace: t.vm_trace.map(Into::into),
|
||||||
|
state_diff: t.state_diff.map(Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,6 +514,18 @@ mod tests {
|
|||||||
use v1::types::Bytes;
|
use v1::types::Bytes;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serialize_trace_results() {
|
||||||
|
let r = TraceResults {
|
||||||
|
output: vec![0x60],
|
||||||
|
trace: vec![],
|
||||||
|
vm_trace: None,
|
||||||
|
state_diff: None,
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_string(&r).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"output":[96],"trace":[],"vmTrace":null,"stateDiff":null}"#);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trace_serialize() {
|
fn test_trace_serialize() {
|
||||||
let t = LocalizedTrace {
|
let t = LocalizedTrace {
|
||||||
@ -431,6 +535,7 @@ mod tests {
|
|||||||
value: U256::from(6),
|
value: U256::from(6),
|
||||||
gas: U256::from(7),
|
gas: U256::from(7),
|
||||||
input: Bytes::new(vec![0x12, 0x34]),
|
input: Bytes::new(vec![0x12, 0x34]),
|
||||||
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::Call(CallResult {
|
result: Res::Call(CallResult {
|
||||||
gas_used: U256::from(8),
|
gas_used: U256::from(8),
|
||||||
@ -444,7 +549,7 @@ mod tests {
|
|||||||
block_hash: H256::from(14),
|
block_hash: H256::from(14),
|
||||||
};
|
};
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234","callType":{"call":[]}}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -520,6 +625,7 @@ mod tests {
|
|||||||
value: U256::from(3),
|
value: U256::from(3),
|
||||||
gas: U256::from(4),
|
gas: U256::from(4),
|
||||||
input: Bytes::new(vec![0x12, 0x34]),
|
input: Bytes::new(vec![0x12, 0x34]),
|
||||||
|
call_type: CallType::Call,
|
||||||
}), Action::Create(Create {
|
}), Action::Create(Create {
|
||||||
from: Address::from(5),
|
from: Address::from(5),
|
||||||
value: U256::from(6),
|
value: U256::from(6),
|
||||||
@ -528,7 +634,7 @@ mod tests {
|
|||||||
})];
|
})];
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&actions).unwrap();
|
let serialized = serde_json::to_string(&actions).unwrap();
|
||||||
assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#);
|
assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234","callType":{"call":[]}}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
|
use util::rlp::encode;
|
||||||
use ethcore::contract_address;
|
use ethcore::contract_address;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
||||||
use v1::types::{Bytes, OptionalValue};
|
use v1::types::{Bytes, OptionalValue};
|
||||||
@ -50,6 +51,8 @@ pub struct Transaction {
|
|||||||
pub input: Bytes,
|
pub input: Bytes,
|
||||||
/// Creates contract
|
/// Creates contract
|
||||||
pub creates: OptionalValue<Address>,
|
pub creates: OptionalValue<Address>,
|
||||||
|
/// Raw transaction data
|
||||||
|
pub raw: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LocalizedTransaction> for Transaction {
|
impl From<LocalizedTransaction> for Transaction {
|
||||||
@ -73,6 +76,7 @@ impl From<LocalizedTransaction> for Transaction {
|
|||||||
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
|
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
|
||||||
Action::Call(_) => OptionalValue::Null,
|
Action::Call(_) => OptionalValue::Null,
|
||||||
},
|
},
|
||||||
|
raw: encode(&t.signed).to_vec().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,6 +102,7 @@ impl From<SignedTransaction> for Transaction {
|
|||||||
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
|
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
|
||||||
Action::Call(_) => OptionalValue::Null,
|
Action::Call(_) => OptionalValue::Null,
|
||||||
},
|
},
|
||||||
|
raw: encode(&t).to_vec().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +116,7 @@ mod tests {
|
|||||||
fn test_transaction_serialize() {
|
fn test_transaction_serialize() {
|
||||||
let t = Transaction::default();
|
let t = Transaction::default();
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#);
|
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,13 +120,22 @@ impl BlockCollection {
|
|||||||
if let Some(head) = head {
|
if let Some(head) = head {
|
||||||
match self.blocks.get(&head) {
|
match self.blocks.get(&head) {
|
||||||
Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => {
|
Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => {
|
||||||
|
self.downloading_bodies.insert(head.clone());
|
||||||
needed_bodies.push(head.clone());
|
needed_bodies.push(head.clone());
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.downloading_bodies.extend(needed_bodies.iter());
|
for h in self.header_ids.values() {
|
||||||
|
if needed_bodies.len() >= count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if !self.downloading_bodies.contains(h) {
|
||||||
|
needed_bodies.push(h.clone());
|
||||||
|
self.downloading_bodies.insert(h.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
needed_bodies
|
needed_bodies
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +295,7 @@ impl BlockCollection {
|
|||||||
|
|
||||||
self.parents.insert(info.parent_hash.clone(), hash.clone());
|
self.parents.insert(info.parent_hash.clone(), hash.clone());
|
||||||
self.blocks.insert(hash.clone(), block);
|
self.blocks.insert(hash.clone(), block);
|
||||||
|
trace!(target: "sync", "New header: {}", hash.hex());
|
||||||
Ok(hash)
|
Ok(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,14 +113,15 @@ const MAX_NODE_DATA_TO_SEND: usize = 1024;
|
|||||||
const MAX_RECEIPTS_TO_SEND: usize = 1024;
|
const MAX_RECEIPTS_TO_SEND: usize = 1024;
|
||||||
const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256;
|
const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256;
|
||||||
const MAX_HEADERS_TO_REQUEST: usize = 128;
|
const MAX_HEADERS_TO_REQUEST: usize = 128;
|
||||||
const MAX_BODIES_TO_REQUEST: usize = 64;
|
const MAX_BODIES_TO_REQUEST: usize = 128;
|
||||||
const MIN_PEERS_PROPAGATION: usize = 4;
|
const MIN_PEERS_PROPAGATION: usize = 4;
|
||||||
const MAX_PEERS_PROPAGATION: usize = 128;
|
const MAX_PEERS_PROPAGATION: usize = 128;
|
||||||
const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20;
|
const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20;
|
||||||
const SUBCHAIN_SIZE: usize = 64;
|
const SUBCHAIN_SIZE: usize = 256;
|
||||||
const MAX_ROUND_PARENTS: usize = 32;
|
const MAX_ROUND_PARENTS: usize = 32;
|
||||||
const MAX_NEW_HASHES: usize = 64;
|
const MAX_NEW_HASHES: usize = 64;
|
||||||
const MAX_TX_TO_IMPORT: usize = 512;
|
const MAX_TX_TO_IMPORT: usize = 512;
|
||||||
|
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
|
||||||
|
|
||||||
const STATUS_PACKET: u8 = 0x00;
|
const STATUS_PACKET: u8 = 0x00;
|
||||||
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
||||||
@ -136,7 +137,9 @@ const NODE_DATA_PACKET: u8 = 0x0e;
|
|||||||
const GET_RECEIPTS_PACKET: u8 = 0x0f;
|
const GET_RECEIPTS_PACKET: u8 = 0x0f;
|
||||||
const RECEIPTS_PACKET: u8 = 0x10;
|
const RECEIPTS_PACKET: u8 = 0x10;
|
||||||
|
|
||||||
const CONNECTION_TIMEOUT_SEC: f64 = 15f64;
|
const HEADERS_TIMEOUT_SEC: f64 = 15f64;
|
||||||
|
const BODIES_TIMEOUT_SEC: f64 = 5f64;
|
||||||
|
const FORK_HEADER_TIMEOUT_SEC: f64 = 3f64;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
/// Sync state
|
/// Sync state
|
||||||
@ -184,6 +187,7 @@ pub struct SyncStatus {
|
|||||||
/// Peer data type requested
|
/// Peer data type requested
|
||||||
enum PeerAsking {
|
enum PeerAsking {
|
||||||
Nothing,
|
Nothing,
|
||||||
|
ForkHeader,
|
||||||
BlockHeaders,
|
BlockHeaders,
|
||||||
BlockBodies,
|
BlockBodies,
|
||||||
Heads,
|
Heads,
|
||||||
@ -212,6 +216,16 @@ struct PeerInfo {
|
|||||||
asking_hash: Option<H256>,
|
asking_hash: Option<H256>,
|
||||||
/// Request timestamp
|
/// Request timestamp
|
||||||
ask_time: f64,
|
ask_time: f64,
|
||||||
|
/// Pending request is expird and result should be ignored
|
||||||
|
expired: bool,
|
||||||
|
/// Peer fork confirmed
|
||||||
|
confirmed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PeerInfo {
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.confirmed && !self.expired
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blockchain sync handler.
|
/// Blockchain sync handler.
|
||||||
@ -245,6 +259,8 @@ pub struct ChainSync {
|
|||||||
round_parents: VecDeque<(H256, H256)>,
|
round_parents: VecDeque<(H256, H256)>,
|
||||||
/// Network ID
|
/// Network ID
|
||||||
network_id: U256,
|
network_id: U256,
|
||||||
|
/// Optional fork block to check
|
||||||
|
fork_block: Option<(BlockNumber, H256)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||||
@ -268,6 +284,7 @@ impl ChainSync {
|
|||||||
round_parents: VecDeque::new(),
|
round_parents: VecDeque::new(),
|
||||||
_max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
_max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
||||||
network_id: config.network_id,
|
network_id: config.network_id,
|
||||||
|
fork_block: config.fork_block,
|
||||||
};
|
};
|
||||||
sync.reset();
|
sync.reset();
|
||||||
sync
|
sync
|
||||||
@ -284,8 +301,8 @@ impl ChainSync {
|
|||||||
highest_block_number: self.highest_block.map(|n| max(n, self.last_imported_block)),
|
highest_block_number: self.highest_block.map(|n| max(n, self.last_imported_block)),
|
||||||
blocks_received: if self.last_imported_block > self.starting_block { self.last_imported_block - self.starting_block } else { 0 },
|
blocks_received: if self.last_imported_block > self.starting_block { self.last_imported_block - self.starting_block } else { 0 },
|
||||||
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
||||||
num_peers: self.peers.len(),
|
num_peers: self.peers.values().filter(|p| p.confirmed).count(),
|
||||||
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
|
num_active_peers: self.peers.values().filter(|p| p.confirmed && p.asking != PeerAsking::Nothing).count(),
|
||||||
mem_used:
|
mem_used:
|
||||||
self.blocks.heap_size()
|
self.blocks.heap_size()
|
||||||
+ self.peers.heap_size_of_children()
|
+ self.peers.heap_size_of_children()
|
||||||
@ -306,6 +323,10 @@ impl ChainSync {
|
|||||||
for (_, ref mut p) in &mut self.peers {
|
for (_, ref mut p) in &mut self.peers {
|
||||||
p.asking_blocks.clear();
|
p.asking_blocks.clear();
|
||||||
p.asking_hash = None;
|
p.asking_hash = None;
|
||||||
|
// mark any pending requests as expired
|
||||||
|
if p.asking != PeerAsking::Nothing && p.confirmed {
|
||||||
|
p.expired = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.syncing_difficulty = From::from(0u64);
|
self.syncing_difficulty = From::from(0u64);
|
||||||
self.state = SyncState::Idle;
|
self.state = SyncState::Idle;
|
||||||
@ -356,6 +377,8 @@ impl ChainSync {
|
|||||||
asking_blocks: Vec::new(),
|
asking_blocks: Vec::new(),
|
||||||
asking_hash: None,
|
asking_hash: None,
|
||||||
ask_time: 0f64,
|
ask_time: 0f64,
|
||||||
|
expired: false,
|
||||||
|
confirmed: self.fork_block.is_none(),
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis);
|
trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis);
|
||||||
@ -383,18 +406,43 @@ impl ChainSync {
|
|||||||
self.peers.insert(peer_id.clone(), peer);
|
self.peers.insert(peer_id.clone(), peer);
|
||||||
self.active_peers.insert(peer_id.clone());
|
self.active_peers.insert(peer_id.clone());
|
||||||
debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id));
|
debug!(target: "sync", "Connected {}:{}", peer_id, io.peer_info(peer_id));
|
||||||
self.sync_peer(io, peer_id, false);
|
if let Some((fork_block, _)) = self.fork_block {
|
||||||
|
self.request_headers_by_number(io, peer_id, fork_block, 1, 0, false, PeerAsking::ForkHeader);
|
||||||
|
} else {
|
||||||
|
self.sync_peer(io, peer_id, false);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||||
/// Called by peer once it has new block headers during sync
|
/// Called by peer once it has new block headers during sync
|
||||||
fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||||
|
let confirmed = match self.peers.get_mut(&peer_id) {
|
||||||
|
Some(ref mut peer) if peer.asking == PeerAsking::ForkHeader => {
|
||||||
|
let item_count = r.item_count();
|
||||||
|
if item_count == 0 || (item_count == 1 && try!(r.at(0)).as_raw().sha3() == self.fork_block.unwrap().1) {
|
||||||
|
trace!(target: "sync", "{}: Confirmed peer", peer_id);
|
||||||
|
peer.asking = PeerAsking::Nothing;
|
||||||
|
peer.confirmed = true;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
trace!(target: "sync", "{}: Fork mismatch", peer_id);
|
||||||
|
io.disconnect_peer(peer_id);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if confirmed {
|
||||||
|
self.sync_peer(io, peer_id, false);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
self.clear_peer_download(peer_id);
|
self.clear_peer_download(peer_id);
|
||||||
let expected_hash = self.peers.get(&peer_id).and_then(|p| p.asking_hash);
|
|
||||||
let expected_asking = if self.state == SyncState::ChainHead { PeerAsking::Heads } else { PeerAsking::BlockHeaders };
|
let expected_asking = if self.state == SyncState::ChainHead { PeerAsking::Heads } else { PeerAsking::BlockHeaders };
|
||||||
|
let expected_hash = self.peers.get(&peer_id).and_then(|p| p.asking_hash);
|
||||||
if !self.reset_peer_asking(peer_id, expected_asking) || expected_hash.is_none() {
|
if !self.reset_peer_asking(peer_id, expected_asking) || expected_hash.is_none() {
|
||||||
trace!(target: "sync", "Ignored unexpected headers");
|
trace!(target: "sync", "{}: Ignored unexpected headers", peer_id);
|
||||||
self.continue_sync(io);
|
self.continue_sync(io);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -460,14 +508,14 @@ impl ChainSync {
|
|||||||
|
|
||||||
// Disable the peer for this syncing round if it gives invalid chain
|
// Disable the peer for this syncing round if it gives invalid chain
|
||||||
if !valid_response {
|
if !valid_response {
|
||||||
trace!(target: "sync", "{} Deactivated for invalid headers response", peer_id);
|
trace!(target: "sync", "{} Disabled for invalid headers response", peer_id);
|
||||||
self.deactivate_peer(io, peer_id);
|
io.disable_peer(peer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if headers.is_empty() {
|
if headers.is_empty() {
|
||||||
// Peer does not have any new subchain heads, deactivate it nd try with another
|
// Peer does not have any new subchain heads, deactivate it nd try with another
|
||||||
trace!(target: "sync", "{} Deactivated for no data", peer_id);
|
trace!(target: "sync", "{} Disabled for no data", peer_id);
|
||||||
self.deactivate_peer(io, peer_id);
|
io.disable_peer(peer_id);
|
||||||
}
|
}
|
||||||
match self.state {
|
match self.state {
|
||||||
SyncState::ChainHead => {
|
SyncState::ChainHead => {
|
||||||
@ -491,6 +539,9 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.collect_blocks(io);
|
self.collect_blocks(io);
|
||||||
|
// give a task to the same peer first if received valuable headers.
|
||||||
|
self.sync_peer(io, peer_id, false);
|
||||||
|
// give tasks to other peers
|
||||||
self.continue_sync(io);
|
self.continue_sync(io);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -543,6 +594,11 @@ impl ChainSync {
|
|||||||
peer.latest_hash = header.hash();
|
peer.latest_hash = header.hash();
|
||||||
peer.latest_number = Some(header.number());
|
peer.latest_number = Some(header.number());
|
||||||
}
|
}
|
||||||
|
if self.last_imported_block > header.number() && self.last_imported_block - header.number() > MAX_NEW_BLOCK_AGE {
|
||||||
|
trace!(target: "sync", "Ignored ancient new block {:?}", h);
|
||||||
|
io.disable_peer(peer_id);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
match io.chain().import_block(block_rlp.as_raw().to_vec()) {
|
match io.chain().import_block(block_rlp.as_raw().to_vec()) {
|
||||||
Err(Error::Import(ImportError::AlreadyInChain)) => {
|
Err(Error::Import(ImportError::AlreadyInChain)) => {
|
||||||
trace!(target: "sync", "New block already in chain {:?}", h);
|
trace!(target: "sync", "New block already in chain {:?}", h);
|
||||||
@ -600,34 +656,39 @@ impl ChainSync {
|
|||||||
let hashes = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1)));
|
let hashes = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1)));
|
||||||
let mut max_height: BlockNumber = 0;
|
let mut max_height: BlockNumber = 0;
|
||||||
let mut new_hashes = Vec::new();
|
let mut new_hashes = Vec::new();
|
||||||
for (rh, rd) in hashes {
|
for (rh, rn) in hashes {
|
||||||
let h = try!(rh);
|
let hash = try!(rh);
|
||||||
let d = try!(rd);
|
let number = try!(rn);
|
||||||
if d > self.highest_block.unwrap_or(0) {
|
if number > self.highest_block.unwrap_or(0) {
|
||||||
self.highest_block = Some(d);
|
self.highest_block = Some(number);
|
||||||
}
|
}
|
||||||
if self.blocks.is_downloading(&h) {
|
if self.blocks.is_downloading(&hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match io.chain().block_status(BlockID::Hash(h.clone())) {
|
if self.last_imported_block > number && self.last_imported_block - number > MAX_NEW_BLOCK_AGE {
|
||||||
|
trace!(target: "sync", "Ignored ancient new block hash {:?}", hash);
|
||||||
|
io.disable_peer(peer_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match io.chain().block_status(BlockID::Hash(hash.clone())) {
|
||||||
BlockStatus::InChain => {
|
BlockStatus::InChain => {
|
||||||
trace!(target: "sync", "New block hash already in chain {:?}", h);
|
trace!(target: "sync", "New block hash already in chain {:?}", hash);
|
||||||
},
|
},
|
||||||
BlockStatus::Queued => {
|
BlockStatus::Queued => {
|
||||||
trace!(target: "sync", "New hash block already queued {:?}", h);
|
trace!(target: "sync", "New hash block already queued {:?}", hash);
|
||||||
},
|
},
|
||||||
BlockStatus::Unknown => {
|
BlockStatus::Unknown => {
|
||||||
new_hashes.push(h.clone());
|
new_hashes.push(hash.clone());
|
||||||
if d > max_height {
|
if number > max_height {
|
||||||
trace!(target: "sync", "New unknown block hash {:?}", h);
|
trace!(target: "sync", "New unknown block hash {:?}", hash);
|
||||||
let peer = self.peers.get_mut(&peer_id).unwrap();
|
let peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
peer.latest_hash = h.clone();
|
peer.latest_hash = hash.clone();
|
||||||
peer.latest_number = Some(d);
|
peer.latest_number = Some(number);
|
||||||
max_height = d;
|
max_height = number;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BlockStatus::Bad => {
|
BlockStatus::Bad => {
|
||||||
debug!(target: "sync", "Bad new block hash {:?}", h);
|
debug!(target: "sync", "Bad new block hash {:?}", hash);
|
||||||
io.disable_peer(peer_id);
|
io.disable_peer(peer_id);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -665,7 +726,8 @@ impl ChainSync {
|
|||||||
|
|
||||||
/// Resume downloading
|
/// Resume downloading
|
||||||
fn continue_sync(&mut self, io: &mut SyncIo) {
|
fn continue_sync(&mut self, io: &mut SyncIo) {
|
||||||
let mut peers: Vec<(PeerId, U256)> = self.peers.iter().map(|(k, p)| (*k, p.difficulty.unwrap_or_else(U256::zero))).collect();
|
let mut peers: Vec<(PeerId, U256)> = self.peers.iter().filter_map(|(k, p)|
|
||||||
|
if p.is_available() { Some((*k, p.difficulty.unwrap_or_else(U256::zero))) } else { None }).collect();
|
||||||
thread_rng().shuffle(&mut peers); //TODO: sort by rating
|
thread_rng().shuffle(&mut peers); //TODO: sort by rating
|
||||||
trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len());
|
trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len());
|
||||||
for (p, _) in peers {
|
for (p, _) in peers {
|
||||||
@ -673,7 +735,7 @@ impl ChainSync {
|
|||||||
self.sync_peer(io, p, false);
|
self.sync_peer(io, p, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.state != SyncState::Waiting && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing) {
|
if self.state != SyncState::Waiting && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.is_available()) {
|
||||||
self.complete_sync();
|
self.complete_sync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -699,7 +761,7 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
let (peer_latest, peer_difficulty) = {
|
let (peer_latest, peer_difficulty) = {
|
||||||
let peer = self.peers.get_mut(&peer_id).unwrap();
|
let peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
if peer.asking != PeerAsking::Nothing {
|
if peer.asking != PeerAsking::Nothing || !peer.is_available() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.state == SyncState::Waiting {
|
if self.state == SyncState::Waiting {
|
||||||
@ -727,7 +789,9 @@ impl ChainSync {
|
|||||||
// Request subchain headers
|
// Request subchain headers
|
||||||
trace!(target: "sync", "Starting sync with better chain");
|
trace!(target: "sync", "Starting sync with better chain");
|
||||||
let last = self.last_imported_hash.clone();
|
let last = self.last_imported_hash.clone();
|
||||||
self.request_headers_by_hash(io, peer_id, &last, SUBCHAIN_SIZE, MAX_HEADERS_TO_REQUEST - 1, false, PeerAsking::Heads);
|
// Request MAX_HEADERS_TO_REQUEST - 2 headers apart so that
|
||||||
|
// MAX_HEADERS_TO_REQUEST would include headers for neighbouring subchains
|
||||||
|
self.request_headers_by_hash(io, peer_id, &last, SUBCHAIN_SIZE, MAX_HEADERS_TO_REQUEST - 2, false, PeerAsking::Heads);
|
||||||
},
|
},
|
||||||
SyncState::Blocks | SyncState::NewBlocks => {
|
SyncState::Blocks | SyncState::NewBlocks => {
|
||||||
if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown {
|
if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown {
|
||||||
@ -895,6 +959,17 @@ impl ChainSync {
|
|||||||
.asking_hash = Some(h.clone());
|
.asking_hash = Some(h.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request headers from a peer by block number
|
||||||
|
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||||
|
fn request_headers_by_number(&mut self, sync: &mut SyncIo, peer_id: PeerId, n: BlockNumber, count: usize, skip: usize, reverse: bool, asking: PeerAsking) {
|
||||||
|
trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}", peer_id, count, n);
|
||||||
|
let mut rlp = RlpStream::new_list(4);
|
||||||
|
rlp.append(&n);
|
||||||
|
rlp.append(&count);
|
||||||
|
rlp.append(&skip);
|
||||||
|
rlp.append(&if reverse {1u32} else {0u32});
|
||||||
|
self.send_request(sync, peer_id, asking, GET_BLOCK_HEADERS_PACKET, rlp.out());
|
||||||
|
}
|
||||||
/// Request block bodies from a peer
|
/// Request block bodies from a peer
|
||||||
fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>) {
|
fn request_bodies(&mut self, sync: &mut SyncIo, peer_id: PeerId, hashes: Vec<H256>) {
|
||||||
let mut rlp = RlpStream::new_list(hashes.len());
|
let mut rlp = RlpStream::new_list(hashes.len());
|
||||||
@ -908,6 +983,7 @@ impl ChainSync {
|
|||||||
/// Reset peer status after request is complete.
|
/// Reset peer status after request is complete.
|
||||||
fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool {
|
fn reset_peer_asking(&mut self, peer_id: PeerId, asking: PeerAsking) -> bool {
|
||||||
let peer = self.peers.get_mut(&peer_id).unwrap();
|
let peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
|
peer.expired = false;
|
||||||
if peer.asking != asking {
|
if peer.asking != asking {
|
||||||
trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking);
|
trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking);
|
||||||
peer.asking = PeerAsking::Nothing;
|
peer.asking = PeerAsking::Nothing;
|
||||||
@ -947,6 +1023,9 @@ impl ChainSync {
|
|||||||
if !io.is_chain_queue_empty() {
|
if !io.is_chain_queue_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
if self.peers.get(&peer_id).map_or(false, |p| p.confirmed) {
|
||||||
|
trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id);
|
||||||
|
}
|
||||||
|
|
||||||
let mut item_count = r.item_count();
|
let mut item_count = r.item_count();
|
||||||
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
||||||
@ -1178,7 +1257,13 @@ impl ChainSync {
|
|||||||
let tick = time::precise_time_s();
|
let tick = time::precise_time_s();
|
||||||
let mut aborting = Vec::new();
|
let mut aborting = Vec::new();
|
||||||
for (peer_id, peer) in &self.peers {
|
for (peer_id, peer) in &self.peers {
|
||||||
if peer.asking != PeerAsking::Nothing && (tick - peer.ask_time) > CONNECTION_TIMEOUT_SEC {
|
let timeout = match peer.asking {
|
||||||
|
PeerAsking::BlockHeaders | PeerAsking::Heads => (tick - peer.ask_time) > HEADERS_TIMEOUT_SEC,
|
||||||
|
PeerAsking::BlockBodies => (tick - peer.ask_time) > BODIES_TIMEOUT_SEC,
|
||||||
|
PeerAsking::Nothing => false,
|
||||||
|
PeerAsking::ForkHeader => (tick - peer.ask_time) > FORK_HEADER_TIMEOUT_SEC,
|
||||||
|
};
|
||||||
|
if timeout {
|
||||||
trace!(target:"sync", "Timeout {}", peer_id);
|
trace!(target:"sync", "Timeout {}", peer_id);
|
||||||
io.disconnect_peer(*peer_id);
|
io.disconnect_peer(*peer_id);
|
||||||
aborting.push(*peer_id);
|
aborting.push(*peer_id);
|
||||||
@ -1593,6 +1678,8 @@ mod tests {
|
|||||||
asking_blocks: Vec::new(),
|
asking_blocks: Vec::new(),
|
||||||
asking_hash: None,
|
asking_hash: None,
|
||||||
ask_time: 0f64,
|
ask_time: 0f64,
|
||||||
|
expired: false,
|
||||||
|
confirmed: false,
|
||||||
});
|
});
|
||||||
sync
|
sync
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,9 @@ use std::ops::*;
|
|||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
|
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
|
||||||
use util::TimerToken;
|
use util::TimerToken;
|
||||||
use util::{U256, ONE_U256};
|
use util::{U256, ONE_U256, H256};
|
||||||
use ethcore::client::Client;
|
use ethcore::client::Client;
|
||||||
|
use ethcore::header::BlockNumber;
|
||||||
use ethcore::service::{SyncMessage, NetSyncMessage};
|
use ethcore::service::{SyncMessage, NetSyncMessage};
|
||||||
use io::NetSyncIo;
|
use io::NetSyncIo;
|
||||||
use util::io::IoChannel;
|
use util::io::IoChannel;
|
||||||
@ -93,6 +94,8 @@ pub struct SyncConfig {
|
|||||||
pub max_download_ahead_blocks: usize,
|
pub max_download_ahead_blocks: usize,
|
||||||
/// Network ID
|
/// Network ID
|
||||||
pub network_id: U256,
|
pub network_id: U256,
|
||||||
|
/// Optional fork block id
|
||||||
|
pub fork_block: Option<(BlockNumber, H256)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SyncConfig {
|
impl Default for SyncConfig {
|
||||||
@ -100,6 +103,7 @@ impl Default for SyncConfig {
|
|||||||
SyncConfig {
|
SyncConfig {
|
||||||
max_download_ahead_blocks: 20000,
|
max_download_ahead_blocks: 20000,
|
||||||
network_id: ONE_U256,
|
network_id: ONE_U256,
|
||||||
|
fork_block: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethcore::client::{BlockChainClient, BlockID, EachBlockWith};
|
use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith};
|
||||||
use chain::{SyncState};
|
use chain::{SyncState};
|
||||||
use super::helpers::*;
|
use super::helpers::*;
|
||||||
|
|
||||||
@ -95,6 +95,25 @@ fn forked() {
|
|||||||
assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain);
|
assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn net_hard_fork() {
|
||||||
|
::env_logger::init().ok();
|
||||||
|
let ref_client = TestBlockChainClient::new();
|
||||||
|
ref_client.add_blocks(50, EachBlockWith::Uncle);
|
||||||
|
{
|
||||||
|
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap())));
|
||||||
|
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||||
|
net.sync();
|
||||||
|
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap())));
|
||||||
|
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing);
|
||||||
|
net.sync();
|
||||||
|
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn restart() {
|
fn restart() {
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
@ -108,7 +127,7 @@ fn restart() {
|
|||||||
net.restart_peer(0);
|
net.restart_peer(0);
|
||||||
|
|
||||||
let status = net.peer(0).sync.read().unwrap().status();
|
let status = net.peer(0).sync.read().unwrap().status();
|
||||||
assert_eq!(status.state, SyncState::ChainHead);
|
assert_eq!(status.state, SyncState::Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethcore::client::{TestBlockChainClient, BlockChainClient};
|
use ethcore::client::{TestBlockChainClient, BlockChainClient};
|
||||||
|
use ethcore::header::BlockNumber;
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
use chain::ChainSync;
|
use chain::ChainSync;
|
||||||
use ::SyncConfig;
|
use ::SyncConfig;
|
||||||
@ -89,13 +90,19 @@ pub struct TestNet {
|
|||||||
|
|
||||||
impl TestNet {
|
impl TestNet {
|
||||||
pub fn new(n: usize) -> TestNet {
|
pub fn new(n: usize) -> TestNet {
|
||||||
|
Self::new_with_fork(n, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet {
|
||||||
let mut net = TestNet {
|
let mut net = TestNet {
|
||||||
peers: Vec::new(),
|
peers: Vec::new(),
|
||||||
started: false,
|
started: false,
|
||||||
};
|
};
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
let chain = TestBlockChainClient::new();
|
let chain = TestBlockChainClient::new();
|
||||||
let sync = ChainSync::new(SyncConfig::default(), &chain);
|
let mut config = SyncConfig::default();
|
||||||
|
config.fork_block = fork;
|
||||||
|
let sync = ChainSync::new(config, &chain);
|
||||||
net.peers.push(TestPeer {
|
net.peers.push(TestPeer {
|
||||||
sync: RwLock::new(sync),
|
sync: RwLock::new(sync),
|
||||||
chain: chain,
|
chain: chain,
|
||||||
|
@ -77,7 +77,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
|||||||
/// Readable IO handler. Called when there is some data to be read.
|
/// Readable IO handler. Called when there is some data to be read.
|
||||||
pub fn readable(&mut self) -> io::Result<Option<Bytes>> {
|
pub fn readable(&mut self) -> io::Result<Option<Bytes>> {
|
||||||
if self.rec_size == 0 || self.rec_buf.len() >= self.rec_size {
|
if self.rec_size == 0 || self.rec_buf.len() >= self.rec_size {
|
||||||
warn!(target:"network", "Unexpected connection read");
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let sock_ref = <Socket as Read>::by_ref(&mut self.socket);
|
let sock_ref = <Socket as Read>::by_ref(&mut self.socket);
|
||||||
loop {
|
loop {
|
||||||
|
@ -128,31 +128,27 @@ impl Handshake {
|
|||||||
/// Readable IO handler. Drives the state change.
|
/// Readable IO handler. Drives the state change.
|
||||||
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone {
|
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
if !self.expired() {
|
if !self.expired() {
|
||||||
match self.state {
|
while let Some(data) = try!(self.connection.readable()) {
|
||||||
HandshakeState::New => {}
|
match self.state {
|
||||||
HandshakeState::ReadingAuth => {
|
HandshakeState::New => {},
|
||||||
if let Some(data) = try!(self.connection.readable()) {
|
HandshakeState::StartSession => {},
|
||||||
|
HandshakeState::ReadingAuth => {
|
||||||
try!(self.read_auth(io, host.secret(), &data));
|
try!(self.read_auth(io, host.secret(), &data));
|
||||||
};
|
},
|
||||||
},
|
HandshakeState::ReadingAuthEip8 => {
|
||||||
HandshakeState::ReadingAuthEip8 => {
|
|
||||||
if let Some(data) = try!(self.connection.readable()) {
|
|
||||||
try!(self.read_auth_eip8(io, host.secret(), &data));
|
try!(self.read_auth_eip8(io, host.secret(), &data));
|
||||||
};
|
},
|
||||||
},
|
HandshakeState::ReadingAck => {
|
||||||
HandshakeState::ReadingAck => {
|
|
||||||
if let Some(data) = try!(self.connection.readable()) {
|
|
||||||
try!(self.read_ack(host.secret(), &data));
|
try!(self.read_ack(host.secret(), &data));
|
||||||
};
|
},
|
||||||
},
|
HandshakeState::ReadingAckEip8 => {
|
||||||
HandshakeState::ReadingAckEip8 => {
|
|
||||||
if let Some(data) = try!(self.connection.readable()) {
|
|
||||||
try!(self.read_ack_eip8(host.secret(), &data));
|
try!(self.read_ack_eip8(host.secret(), &data));
|
||||||
};
|
},
|
||||||
},
|
}
|
||||||
HandshakeState::StartSession => {
|
if self.state == HandshakeState::StartSession {
|
||||||
io.clear_timer(self.connection.token).ok();
|
io.clear_timer(self.connection.token).ok();
|
||||||
},
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -178,9 +174,9 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse, validate and confirm auth message
|
/// Parse, validate and confirm auth message
|
||||||
fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
|
fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
trace!(target:"network", "Received handshake auth from {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Received handshake auth from {:?}", self.connection.remote_addr_str());
|
||||||
if data.len() != V4_AUTH_PACKET_SIZE {
|
if data.len() != V4_AUTH_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong auth packet size");
|
debug!(target: "network", "Wrong auth packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
}
|
}
|
||||||
self.auth_cipher = data.to_vec();
|
self.auth_cipher = data.to_vec();
|
||||||
@ -197,7 +193,7 @@ impl Handshake {
|
|||||||
// Try to interpret as EIP-8 packet
|
// Try to interpret as EIP-8 packet
|
||||||
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
|
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
|
||||||
if total < V4_AUTH_PACKET_SIZE {
|
if total < V4_AUTH_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong EIP8 auth packet size");
|
debug!(target: "network", "Wrong EIP8 auth packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
}
|
}
|
||||||
let rest = total - data.len();
|
let rest = total - data.len();
|
||||||
@ -209,7 +205,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
|
fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
|
||||||
self.auth_cipher.extend_from_slice(data);
|
self.auth_cipher.extend_from_slice(data);
|
||||||
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&auth);
|
let rlp = UntrustedRlp::new(&auth);
|
||||||
@ -224,9 +220,9 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse and validate ack message
|
/// Parse and validate ack message
|
||||||
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"network", "Received handshake ack from {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Received handshake ack from {:?}", self.connection.remote_addr_str());
|
||||||
if data.len() != V4_ACK_PACKET_SIZE {
|
if data.len() != V4_ACK_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong ack packet size");
|
debug!(target: "network", "Wrong ack packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
}
|
}
|
||||||
self.ack_cipher = data.to_vec();
|
self.ack_cipher = data.to_vec();
|
||||||
@ -240,7 +236,7 @@ impl Handshake {
|
|||||||
// Try to interpret as EIP-8 packet
|
// Try to interpret as EIP-8 packet
|
||||||
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
|
let total = (((data[0] as u16) << 8 | (data[1] as u16)) as usize) + 2;
|
||||||
if total < V4_ACK_PACKET_SIZE {
|
if total < V4_ACK_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong EIP8 ack packet size");
|
debug!(target: "network", "Wrong EIP8 ack packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
}
|
}
|
||||||
let rest = total - data.len();
|
let rest = total - data.len();
|
||||||
@ -252,7 +248,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
|
||||||
self.ack_cipher.extend_from_slice(data);
|
self.ack_cipher.extend_from_slice(data);
|
||||||
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&ack);
|
let rlp = UntrustedRlp::new(&ack);
|
||||||
@ -265,7 +261,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends auth message
|
/// Sends auth message
|
||||||
fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), UtilError> where Message: Send + Clone {
|
fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
trace!(target:"network", "Sending handshake auth to {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Sending handshake auth to {:?}", self.connection.remote_addr_str());
|
||||||
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -292,7 +288,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends ack message
|
/// Sends ack message
|
||||||
fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
|
fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
trace!(target:"network", "Sending handshake ack to {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Sending handshake ack to {:?}", self.connection.remote_addr_str());
|
||||||
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -311,7 +307,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends EIP8 ack message
|
/// Sends EIP8 ack message
|
||||||
fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
|
fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), UtilError> where Message: Send + Clone {
|
||||||
trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str());
|
trace!(target: "network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str());
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(self.ecdhe.public());
|
rlp.append(self.ecdhe.public());
|
||||||
rlp.append(&self.nonce);
|
rlp.append(&self.nonce);
|
||||||
|
Loading…
Reference in New Issue
Block a user