Merge branch 'master' into light-poa
This commit is contained in:
@@ -748,7 +748,7 @@ impl Client {
|
||||
self.factories.clone(),
|
||||
).expect("state known to be available for just-imported block; qed");
|
||||
|
||||
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
||||
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||
let res = Executive::new(&mut state, &env_info, &*self.engine)
|
||||
.transact(&transaction, options);
|
||||
|
||||
@@ -1113,18 +1113,39 @@ impl Client {
|
||||
}.fake_sign(from)
|
||||
}
|
||||
|
||||
fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
fn call<E, V, T>(
|
||||
state: &mut State<StateDB>,
|
||||
env_info: &EnvInfo,
|
||||
engine: &E,
|
||||
state_diff: bool,
|
||||
transaction: &SignedTransaction,
|
||||
options: TransactOptions<T, V>,
|
||||
) -> Result<Executed, CallError> where
|
||||
E: Engine + ?Sized,
|
||||
T: trace::Tracer,
|
||||
V: trace::VMTracer,
|
||||
{
|
||||
let options = options.dont_check_nonce();
|
||||
let original_state = if state_diff { Some(state.clone()) } else { None };
|
||||
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;
|
||||
let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?;
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
if let Some(original) = original_state {
|
||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
||||
if let Some(original) = original_state {
|
||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
let state_diff = analytics.state_diffing;
|
||||
let engine = &*self.engine;
|
||||
|
||||
match (analytics.transaction_tracing, analytics.vm_tracing) {
|
||||
(true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
|
||||
(true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()),
|
||||
(false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()),
|
||||
(false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,7 +1178,7 @@ impl BlockChainClient for Client {
|
||||
// that's just a copy of the state.
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
|
||||
self.do_call(&env_info, &mut state, transaction, analytics)
|
||||
self.do_virtual_call(&env_info, &mut state, transaction, analytics)
|
||||
}
|
||||
|
||||
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
||||
@@ -1169,7 +1190,7 @@ impl BlockChainClient for Client {
|
||||
let mut results = Vec::with_capacity(transactions.len());
|
||||
|
||||
for &(ref t, analytics) in transactions {
|
||||
let ret = self.do_call(&env_info, &mut state, t, analytics)?;
|
||||
let ret = self.do_virtual_call(&env_info, &mut state, t, analytics)?;
|
||||
env_info.gas_used = ret.cumulative_gas_used;
|
||||
results.push(ret);
|
||||
}
|
||||
@@ -1189,7 +1210,7 @@ impl BlockChainClient for Client {
|
||||
// that's just a copy of the state.
|
||||
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let sender = t.sender();
|
||||
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
|
||||
let options = || TransactOptions::with_tracing();
|
||||
|
||||
let cond = |gas| {
|
||||
let mut tx = t.as_unsigned().clone();
|
||||
@@ -1198,7 +1219,7 @@ impl BlockChainClient for Client {
|
||||
|
||||
let mut state = original_state.clone();
|
||||
Ok(Executive::new(&mut state, &env_info, &*self.engine)
|
||||
.transact_virtual(&tx, options.clone())
|
||||
.transact_virtual(&tx, options())
|
||||
.map(|r| r.exception.is_none())
|
||||
.unwrap_or(false))
|
||||
};
|
||||
@@ -1254,22 +1275,17 @@ impl BlockChainClient for Client {
|
||||
return Err(CallError::TransactionNotFound);
|
||||
}
|
||||
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
|
||||
let rest = txs.split_off(address.index);
|
||||
for t in txs {
|
||||
let t = SignedTransaction::new(t).expect(PROOF);
|
||||
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, Default::default())?;
|
||||
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?;
|
||||
env_info.gas_used = env_info.gas_used + x.gas_used;
|
||||
}
|
||||
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
|
||||
let t = SignedTransaction::new(first).expect(PROOF);
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, options)?;
|
||||
if let Some(original) = original_state {
|
||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
|
||||
}
|
||||
Ok(ret)
|
||||
|
||||
self.do_virtual_call(&env_info, &mut state, &t, analytics)
|
||||
}
|
||||
|
||||
fn mode(&self) -> IpcMode {
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use util::{self, U256, journaldb, trie};
|
||||
use util::{self, U256, H256, journaldb, trie};
|
||||
use util::kvdb::{self, KeyValueDB};
|
||||
use {state, state_db, client, executive, trace, db, spec};
|
||||
use {state, state_db, client, executive, trace, transaction, db, spec, pod_state};
|
||||
use factory::Factories;
|
||||
use evm::{self, VMType};
|
||||
use vm::{self, ActionParams};
|
||||
@@ -33,9 +33,17 @@ pub enum EvmTestError {
|
||||
/// EVM error.
|
||||
Evm(vm::Error),
|
||||
/// Initialization error.
|
||||
Initialization(::error::Error),
|
||||
ClientError(::error::Error),
|
||||
/// Low-level database error.
|
||||
Database(String),
|
||||
/// Post-condition failure,
|
||||
PostCondition(String),
|
||||
}
|
||||
|
||||
impl<E: Into<::error::Error>> From<E> for EvmTestError {
|
||||
fn from(err: E) -> Self {
|
||||
EvmTestError::ClientError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EvmTestError {
|
||||
@@ -45,52 +53,114 @@ impl fmt::Display for EvmTestError {
|
||||
match *self {
|
||||
Trie(ref err) => write!(fmt, "Trie: {}", err),
|
||||
Evm(ref err) => write!(fmt, "EVM: {}", err),
|
||||
Initialization(ref err) => write!(fmt, "Initialization: {}", err),
|
||||
ClientError(ref err) => write!(fmt, "{}", err),
|
||||
Database(ref err) => write!(fmt, "DB: {}", err),
|
||||
PostCondition(ref err) => write!(fmt, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified, single-block EVM test client.
|
||||
pub struct EvmTestClient {
|
||||
state_db: state_db::StateDB,
|
||||
factories: Factories,
|
||||
spec: spec::Spec,
|
||||
use ethereum;
|
||||
use ethjson::state::test::ForkSpec;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref FRONTIER: spec::Spec = ethereum::new_frontier_test();
|
||||
pub static ref HOMESTEAD: spec::Spec = ethereum::new_homestead_test();
|
||||
pub static ref EIP150: spec::Spec = ethereum::new_eip150_test();
|
||||
pub static ref EIP161: spec::Spec = ethereum::new_eip161_test();
|
||||
pub static ref _METROPOLIS: spec::Spec = ethereum::new_metropolis_test();
|
||||
}
|
||||
|
||||
impl EvmTestClient {
|
||||
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
|
||||
pub fn new(spec: spec::Spec) -> Result<Self, EvmTestError> {
|
||||
let factories = Factories {
|
||||
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
|
||||
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
|
||||
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
|
||||
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
|
||||
state_db = spec.ensure_db_good(state_db, &factories).map_err(EvmTestError::Initialization)?;
|
||||
// Write DB
|
||||
{
|
||||
let mut batch = kvdb::DBTransaction::new();
|
||||
state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()).map_err(|e| EvmTestError::Initialization(e.into()))?;
|
||||
db.write(batch).map_err(EvmTestError::Database)?;
|
||||
/// Simplified, single-block EVM test client.
|
||||
pub struct EvmTestClient<'a> {
|
||||
state: state::State<state_db::StateDB>,
|
||||
spec: &'a spec::Spec,
|
||||
}
|
||||
|
||||
impl<'a> EvmTestClient<'a> {
|
||||
/// Converts a json spec definition into spec.
|
||||
pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> {
|
||||
match *spec {
|
||||
ForkSpec::Frontier => Some(&*FRONTIER),
|
||||
ForkSpec::Homestead => Some(&*HOMESTEAD),
|
||||
ForkSpec::EIP150 => Some(&*EIP150),
|
||||
ForkSpec::EIP158 => Some(&*EIP161),
|
||||
ForkSpec::Metropolis | ForkSpec::Byzantium | ForkSpec::Constantinople => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
|
||||
pub fn new(spec: &'a spec::Spec) -> Result<Self, EvmTestError> {
|
||||
let factories = Self::factories();
|
||||
let state = Self::state_from_spec(spec, &factories)?;
|
||||
|
||||
Ok(EvmTestClient {
|
||||
state_db,
|
||||
factories,
|
||||
state,
|
||||
spec,
|
||||
})
|
||||
}
|
||||
|
||||
/// Call given contract.
|
||||
/// Creates new EVM test client with in-memory DB initialized with given PodState.
|
||||
pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result<Self, EvmTestError> {
|
||||
let factories = Self::factories();
|
||||
let state = Self::state_from_pod(spec, &factories, pod_state)?;
|
||||
|
||||
Ok(EvmTestClient {
|
||||
state,
|
||||
spec,
|
||||
})
|
||||
}
|
||||
|
||||
fn factories() -> Factories {
|
||||
Factories {
|
||||
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
|
||||
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
|
||||
accountdb: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result<state::State<state_db::StateDB>, EvmTestError> {
|
||||
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
|
||||
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
|
||||
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
|
||||
state_db = spec.ensure_db_good(state_db, factories)?;
|
||||
|
||||
let genesis = spec.genesis_header();
|
||||
// Write DB
|
||||
{
|
||||
let mut batch = kvdb::DBTransaction::new();
|
||||
state_db.journal_under(&mut batch, 0, &genesis.hash())?;
|
||||
db.write(batch).map_err(EvmTestError::Database)?;
|
||||
}
|
||||
|
||||
state::State::from_existing(
|
||||
state_db,
|
||||
*genesis.state_root(),
|
||||
spec.engine.account_start_nonce(0),
|
||||
factories.clone()
|
||||
).map_err(EvmTestError::Trie)
|
||||
}
|
||||
|
||||
fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result<state::State<state_db::StateDB>, EvmTestError> {
|
||||
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
|
||||
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
|
||||
let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
|
||||
let mut state = state::State::new(
|
||||
state_db,
|
||||
spec.engine.account_start_nonce(0),
|
||||
factories.clone(),
|
||||
);
|
||||
state.populate_from(pod_state);
|
||||
state.commit()?;
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Execute the VM given ActionParams and tracer.
|
||||
/// Returns amount of gas left and the output.
|
||||
pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
|
||||
-> Result<(U256, Vec<u8>), EvmTestError>
|
||||
{
|
||||
let genesis = self.spec.genesis_header();
|
||||
let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(0), self.factories.clone())
|
||||
.map_err(EvmTestError::Trie)?;
|
||||
let info = client::EnvInfo {
|
||||
number: genesis.number(),
|
||||
author: *genesis.author(),
|
||||
@@ -103,7 +173,7 @@ impl EvmTestClient {
|
||||
let mut substate = state::Substate::new();
|
||||
let mut tracer = trace::NoopTracer;
|
||||
let mut output = vec![];
|
||||
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
|
||||
let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine);
|
||||
let (gas_left, _) = executive.call(
|
||||
params,
|
||||
&mut substate,
|
||||
@@ -114,4 +184,59 @@ impl EvmTestClient {
|
||||
|
||||
Ok((gas_left, output))
|
||||
}
|
||||
|
||||
/// Executes a SignedTransaction within context of the provided state and `EnvInfo`.
|
||||
/// Returns the state root, gas left and the output.
|
||||
pub fn transact<T: trace::VMTracer>(
|
||||
&mut self,
|
||||
env_info: &client::EnvInfo,
|
||||
transaction: transaction::SignedTransaction,
|
||||
vm_tracer: T,
|
||||
) -> TransactResult {
|
||||
let initial_gas = transaction.gas;
|
||||
// Verify transaction
|
||||
let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition);
|
||||
if let Err(error) = is_ok {
|
||||
return TransactResult::Err {
|
||||
state_root: *self.state.root(),
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
// Apply transaction
|
||||
let tracer = trace::NoopTracer;
|
||||
let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer);
|
||||
|
||||
match result {
|
||||
Ok(result) => TransactResult::Ok {
|
||||
state_root: *self.state.root(),
|
||||
gas_left: initial_gas - result.receipt.gas_used,
|
||||
output: result.output
|
||||
},
|
||||
Err(error) => TransactResult::Err {
|
||||
state_root: *self.state.root(),
|
||||
error,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A result of applying transaction to the state.
|
||||
pub enum TransactResult {
|
||||
/// Successful execution
|
||||
Ok {
|
||||
/// State root
|
||||
state_root: H256,
|
||||
/// Amount of gas left
|
||||
gas_left: U256,
|
||||
/// Output
|
||||
output: Vec<u8>,
|
||||
},
|
||||
/// Transaction failed to run
|
||||
Err {
|
||||
/// State root
|
||||
state_root: H256,
|
||||
/// Execution error
|
||||
error: ::error::Error,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ mod client;
|
||||
pub use self::client::*;
|
||||
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
|
||||
pub use self::error::Error;
|
||||
pub use self::evm_test_client::{EvmTestClient, EvmTestError};
|
||||
pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult};
|
||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use self::chain_notify::ChainNotify;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
|
||||
|
||||
@@ -187,7 +187,14 @@ impl Engine for Arc<Ethash> {
|
||||
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
|
||||
map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())]
|
||||
if header.seal().len() == self.seal_fields() {
|
||||
map![
|
||||
"nonce".to_owned() => format!("0x{}", header.nonce().hex()),
|
||||
"mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())
|
||||
]
|
||||
} else {
|
||||
BTreeMap::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
||||
|
||||
@@ -26,7 +26,7 @@ use evm::{CallType, Factory, Finalize, FinalizationResult};
|
||||
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
|
||||
use wasm;
|
||||
use externalities::*;
|
||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||
use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer};
|
||||
use transaction::{Action, SignedTransaction};
|
||||
use crossbeam;
|
||||
pub use executed::{Executed, ExecutionResult};
|
||||
@@ -66,16 +66,77 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address,
|
||||
}
|
||||
|
||||
/// Transaction execution options.
|
||||
#[derive(Default, Copy, Clone, PartialEq)]
|
||||
pub struct TransactOptions {
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct TransactOptions<T, V> {
|
||||
/// Enable call tracing.
|
||||
pub tracing: bool,
|
||||
pub tracer: T,
|
||||
/// Enable VM tracing.
|
||||
pub vm_tracing: bool,
|
||||
pub vm_tracer: V,
|
||||
/// Check transaction nonce before execution.
|
||||
pub check_nonce: bool,
|
||||
}
|
||||
|
||||
impl<T, V> TransactOptions<T, V> {
|
||||
/// Create new `TransactOptions` with given tracer and VM tracer.
|
||||
pub fn new(tracer: T, vm_tracer: V) -> Self {
|
||||
TransactOptions {
|
||||
tracer,
|
||||
vm_tracer,
|
||||
check_nonce: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the nonce check
|
||||
pub fn dont_check_nonce(mut self) -> Self {
|
||||
self.check_nonce = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactOptions<trace::ExecutiveTracer, trace::ExecutiveVMTracer> {
|
||||
/// Creates new `TransactOptions` with default tracing and VM tracing.
|
||||
pub fn with_tracing_and_vm_tracing() -> Self {
|
||||
TransactOptions {
|
||||
tracer: trace::ExecutiveTracer::default(),
|
||||
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||
check_nonce: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactOptions<trace::ExecutiveTracer, trace::NoopVMTracer> {
|
||||
/// Creates new `TransactOptions` with default tracing and no VM tracing.
|
||||
pub fn with_tracing() -> Self {
|
||||
TransactOptions {
|
||||
tracer: trace::ExecutiveTracer::default(),
|
||||
vm_tracer: trace::NoopVMTracer,
|
||||
check_nonce: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactOptions<trace::NoopTracer, trace::ExecutiveVMTracer> {
|
||||
/// Creates new `TransactOptions` with no tracing and default VM tracing.
|
||||
pub fn with_vm_tracing() -> Self {
|
||||
TransactOptions {
|
||||
tracer: trace::NoopTracer,
|
||||
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||
check_nonce: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactOptions<trace::NoopTracer, trace::NoopVMTracer> {
|
||||
/// Creates new `TransactOptions` without any tracing.
|
||||
pub fn with_no_tracing() -> Self {
|
||||
TransactOptions {
|
||||
tracer: trace::NoopTracer,
|
||||
vm_tracer: trace::NoopVMTracer,
|
||||
check_nonce: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn executor<E>(engine: &E, vm_factory: &Factory, params: &ActionParams)
|
||||
-> Box<vm::Vm> where E: Engine + ?Sized
|
||||
{
|
||||
@@ -137,24 +198,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
}
|
||||
|
||||
/// This function should be used to execute transaction.
|
||||
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
||||
let check = options.check_nonce;
|
||||
match options.tracing {
|
||||
true => match options.vm_tracing {
|
||||
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::toplevel()),
|
||||
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
|
||||
},
|
||||
false => match options.vm_tracing {
|
||||
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::toplevel()),
|
||||
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
|
||||
},
|
||||
}
|
||||
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
||||
{
|
||||
self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer)
|
||||
}
|
||||
|
||||
/// Execute a transaction in a "virtual" context.
|
||||
/// This will ensure the caller has enough balance to execute the desired transaction.
|
||||
/// Used for extra-block executions for things like consensus contracts and RPCs
|
||||
pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
||||
pub fn transact_virtual<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
||||
{
|
||||
let sender = t.sender();
|
||||
let balance = self.state.balance(&sender)?;
|
||||
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
|
||||
@@ -167,7 +222,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
}
|
||||
|
||||
/// Execute transaction/call with tracing enabled
|
||||
pub fn transact_with_tracer<T, V>(
|
||||
fn transact_with_tracer<T, V>(
|
||||
&'a mut self,
|
||||
t: &SignedTransaction,
|
||||
check_nonce: bool,
|
||||
@@ -261,7 +316,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
};
|
||||
|
||||
// finalize here!
|
||||
Ok(self.finalize(t, substate, result, output, tracer.traces(), vm_tracer.drain())?)
|
||||
Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?)
|
||||
}
|
||||
|
||||
fn exec_vm<T, V>(
|
||||
@@ -399,7 +454,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
|
||||
trace!(target: "executive", "res={:?}", res);
|
||||
|
||||
let traces = subtracer.traces();
|
||||
let traces = subtracer.drain();
|
||||
match res {
|
||||
Ok(ref res) => tracer.trace_call(
|
||||
trace_info,
|
||||
@@ -484,9 +539,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
gas - res.gas_left,
|
||||
trace_output,
|
||||
created,
|
||||
subtracer.traces()
|
||||
subtracer.drain()
|
||||
),
|
||||
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
|
||||
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.drain(), e.into())
|
||||
};
|
||||
|
||||
self.enact_result(&res, substate, unconfirmed_substate);
|
||||
@@ -794,7 +849,7 @@ mod tests {
|
||||
}),
|
||||
}];
|
||||
|
||||
assert_eq!(tracer.traces(), expected_trace);
|
||||
assert_eq!(tracer.drain(), expected_trace);
|
||||
|
||||
let expected_vm_trace = VMTrace {
|
||||
parent_step: 0,
|
||||
@@ -887,7 +942,7 @@ mod tests {
|
||||
}),
|
||||
}];
|
||||
|
||||
assert_eq!(tracer.traces(), expected_trace);
|
||||
assert_eq!(tracer.drain(), expected_trace);
|
||||
|
||||
let expected_vm_trace = VMTrace {
|
||||
parent_step: 0,
|
||||
@@ -1138,7 +1193,7 @@ mod tests {
|
||||
|
||||
let executed = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
let opts = TransactOptions::with_no_tracing();
|
||||
ex.transact(&t, opts).unwrap()
|
||||
};
|
||||
|
||||
@@ -1175,7 +1230,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
let opts = TransactOptions::with_no_tracing();
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@@ -1208,7 +1263,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
let opts = TransactOptions::with_no_tracing();
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
@@ -1241,7 +1296,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
|
||||
let opts = TransactOptions::with_no_tracing();
|
||||
ex.transact(&t, opts)
|
||||
};
|
||||
|
||||
|
||||
@@ -15,23 +15,13 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use pod_state::PodState;
|
||||
use ethereum;
|
||||
use spec::Spec;
|
||||
use trace;
|
||||
use client::{EvmTestClient, EvmTestError, TransactResult};
|
||||
use ethjson;
|
||||
use ethjson::state::test::ForkSpec;
|
||||
use transaction::SignedTransaction;
|
||||
use vm::EnvInfo;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref FRONTIER: Spec = ethereum::new_frontier_test();
|
||||
pub static ref HOMESTEAD: Spec = ethereum::new_homestead_test();
|
||||
pub static ref EIP150: Spec = ethereum::new_eip150_test();
|
||||
pub static ref EIP161: Spec = ethereum::new_eip161_test();
|
||||
pub static ref _METROPOLIS: Spec = ethereum::new_metropolis_test();
|
||||
}
|
||||
|
||||
pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
|
||||
::ethcore_logger::init_log();
|
||||
let tests = ethjson::state::test::Test::load(json_data).unwrap();
|
||||
@@ -43,35 +33,49 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
|
||||
let env: EnvInfo = test.env.into();
|
||||
let pre: PodState = test.pre_state.into();
|
||||
|
||||
for (spec, states) in test.post_states {
|
||||
for (spec_name, states) in test.post_states {
|
||||
let total = states.len();
|
||||
let engine = match spec {
|
||||
ForkSpec::Frontier => &FRONTIER.engine,
|
||||
ForkSpec::Homestead => &HOMESTEAD.engine,
|
||||
ForkSpec::EIP150 => &EIP150.engine,
|
||||
ForkSpec::EIP158 => &EIP161.engine,
|
||||
ForkSpec::Metropolis => continue,
|
||||
let spec = match EvmTestClient::spec_from_json(&spec_name) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for (i, state) in states.into_iter().enumerate() {
|
||||
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total);
|
||||
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total);
|
||||
|
||||
let post_root: H256 = state.hash.into();
|
||||
let transaction: SignedTransaction = multitransaction.select(&state.indexes).into();
|
||||
let mut state = get_temp_state();
|
||||
state.populate_from(pre.clone());
|
||||
if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() {
|
||||
state.commit().expect(&format!("State test {} failed due to internal error.", name));
|
||||
let _res = state.apply(&env, &**engine, &transaction, false);
|
||||
} else {
|
||||
let _rest = state.commit();
|
||||
}
|
||||
if state.root() != &post_root {
|
||||
println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root);
|
||||
flushln!("{} fail", info);
|
||||
failed.push(name.clone());
|
||||
} else {
|
||||
flushln!("{} ok", info);
|
||||
|
||||
let result = || -> Result<_, EvmTestError> {
|
||||
Ok(EvmTestClient::from_pod_state(spec, pre.clone())?
|
||||
.transact(&env, transaction, trace::NoopVMTracer))
|
||||
};
|
||||
match result() {
|
||||
Err(err) => {
|
||||
println!("{} !!! Unexpected internal error: {:?}", info, err);
|
||||
flushln!("{} fail", info);
|
||||
failed.push(name.clone());
|
||||
},
|
||||
Ok(TransactResult::Ok { state_root, .. }) if state_root != post_root => {
|
||||
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
|
||||
flushln!("{} fail", info);
|
||||
failed.push(name.clone());
|
||||
},
|
||||
Ok(TransactResult::Err { state_root, ref error }) if state_root != post_root => {
|
||||
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
|
||||
println!("{} !!! Execution error: {:?}", info, error);
|
||||
flushln!("{} fail", info);
|
||||
failed.push(name.clone());
|
||||
},
|
||||
Ok(TransactResult::Err { error, .. }) => {
|
||||
flushln!("{} ok ({:?})", info, error);
|
||||
},
|
||||
Ok(_) => {
|
||||
flushln!("{} ok", info);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,8 +506,6 @@ pub struct AccountDetails {
|
||||
pub balance: U256,
|
||||
}
|
||||
|
||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||
const GAS_LIMIT_HYSTERESIS: usize = 200; // (100/GAS_LIMIT_HYSTERESIS) %
|
||||
/// Transaction with the same (sender, nonce) can be replaced only if
|
||||
/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT`
|
||||
const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25%
|
||||
@@ -570,8 +568,8 @@ pub struct TransactionQueue {
|
||||
minimal_gas_price: U256,
|
||||
/// The maximum amount of gas any individual transaction may use.
|
||||
tx_gas_limit: U256,
|
||||
/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
|
||||
total_gas_limit: U256,
|
||||
/// Current gas limit (block gas limit). Transactions above the limit will not be accepted (default to !0)
|
||||
block_gas_limit: U256,
|
||||
/// Maximal time transaction may occupy the queue.
|
||||
/// When we reach `max_time_in_queue / 2^3` we re-validate
|
||||
/// account balance.
|
||||
@@ -631,7 +629,7 @@ impl TransactionQueue {
|
||||
TransactionQueue {
|
||||
strategy,
|
||||
minimal_gas_price: U256::zero(),
|
||||
total_gas_limit: !U256::zero(),
|
||||
block_gas_limit: !U256::zero(),
|
||||
tx_gas_limit,
|
||||
max_time_in_queue: DEFAULT_QUEUING_PERIOD,
|
||||
current,
|
||||
@@ -674,16 +672,10 @@ impl TransactionQueue {
|
||||
self.current.gas_price_entry_limit()
|
||||
}
|
||||
|
||||
/// Sets new gas limit. Transactions with gas slightly (`GAS_LIMIT_HYSTERESIS`) above the limit won't be imported.
|
||||
/// Sets new gas limit. Transactions with gas over the limit will not be accepted.
|
||||
/// Any transaction already imported to the queue is not affected.
|
||||
pub fn set_gas_limit(&mut self, gas_limit: U256) {
|
||||
let extra = gas_limit / U256::from(GAS_LIMIT_HYSTERESIS);
|
||||
|
||||
let total_gas_limit = match gas_limit.overflowing_add(extra) {
|
||||
(_, true) => !U256::zero(),
|
||||
(val, false) => val,
|
||||
};
|
||||
self.total_gas_limit = total_gas_limit;
|
||||
self.block_gas_limit = gas_limit;
|
||||
}
|
||||
|
||||
/// Sets new total gas limit.
|
||||
@@ -819,13 +811,13 @@ impl TransactionQueue {
|
||||
}));
|
||||
}
|
||||
|
||||
let gas_limit = cmp::min(self.tx_gas_limit, self.total_gas_limit);
|
||||
let gas_limit = cmp::min(self.tx_gas_limit, self.block_gas_limit);
|
||||
if tx.gas > gas_limit {
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
|
||||
tx.hash(),
|
||||
tx.gas,
|
||||
self.total_gas_limit,
|
||||
self.block_gas_limit,
|
||||
self.tx_gas_limit
|
||||
);
|
||||
return Err(Error::Transaction(TransactionError::GasLimitExceeded {
|
||||
@@ -1922,13 +1914,13 @@ pub mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
txq.set_gas_limit(U256::zero());
|
||||
assert_eq!(txq.total_gas_limit, U256::zero());
|
||||
assert_eq!(txq.block_gas_limit, U256::zero());
|
||||
|
||||
// when
|
||||
txq.set_gas_limit(!U256::zero());
|
||||
|
||||
// then
|
||||
assert_eq!(txq.total_gas_limit, !U256::zero());
|
||||
assert_eq!(txq.block_gas_limit, !U256::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1945,7 +1937,7 @@ pub mod test {
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
||||
limit: U256::from(50_250), // Should be 100.5% of set_gas_limit
|
||||
limit: U256::from(50_000),
|
||||
got: gas,
|
||||
});
|
||||
let stats = txq.status();
|
||||
|
||||
@@ -99,6 +99,8 @@ pub struct CommonParams {
|
||||
pub block_reward: U256,
|
||||
/// Registrar contract address.
|
||||
pub registrar: Address,
|
||||
/// Node permission managing contract address.
|
||||
pub node_permission_contract: Option<Address>,
|
||||
}
|
||||
|
||||
impl CommonParams {
|
||||
@@ -170,6 +172,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
node_permission_contract: p.node_permission_contract.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use vm::EnvInfo;
|
||||
use error::Error;
|
||||
use executive::{Executive, TransactOptions};
|
||||
use factory::Factories;
|
||||
use trace::FlatTrace;
|
||||
use trace::{self, FlatTrace, VMTrace};
|
||||
use pod_account::*;
|
||||
use pod_state::{self, PodState};
|
||||
use types::basic_account::BasicAccount;
|
||||
@@ -59,8 +59,12 @@ pub use self::substate::Substate;
|
||||
pub struct ApplyOutcome {
|
||||
/// The receipt for the applied transaction.
|
||||
pub receipt: Receipt,
|
||||
/// The trace for the applied transaction, if None if tracing is disabled.
|
||||
/// The output of the applied transaction.
|
||||
pub output: Bytes,
|
||||
/// The trace for the applied transaction, empty if tracing was not produced.
|
||||
pub trace: Vec<FlatTrace>,
|
||||
/// The VM trace for the applied transaction, None if tracing was not produced.
|
||||
pub vm_trace: Option<VMTrace>
|
||||
}
|
||||
|
||||
/// Result type for the execution ("application") of a transaction.
|
||||
@@ -205,7 +209,7 @@ pub fn check_proof(
|
||||
Err(_) => return ProvedExecution::BadProof,
|
||||
};
|
||||
|
||||
match state.execute(env_info, engine, transaction, false, true) {
|
||||
match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) {
|
||||
Ok(executed) => ProvedExecution::Complete(executed),
|
||||
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
|
||||
Err(e) => ProvedExecution::Failed(e),
|
||||
@@ -239,7 +243,8 @@ pub fn prove_transaction<H: AsHashDB + Send + Sync>(
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
match state.execute(env_info, engine, transaction, false, virt) {
|
||||
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||
match state.execute(env_info, engine, transaction, options, virt) {
|
||||
Err(ExecutionError::Internal(_)) => None,
|
||||
Err(e) => {
|
||||
trace!(target: "state", "Proved call failed: {}", e);
|
||||
@@ -327,7 +332,7 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
/// Creates new state with empty state root
|
||||
#[cfg(test)]
|
||||
/// Used for tests.
|
||||
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
|
||||
let mut root = H256::new();
|
||||
{
|
||||
@@ -660,29 +665,57 @@ impl<B: Backend> State<B> {
|
||||
/// Execute a given transaction, producing a receipt and an optional trace.
|
||||
/// This will change the state accordingly.
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
// let old = self.to_pod();
|
||||
if tracing {
|
||||
let options = TransactOptions::with_tracing();
|
||||
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
|
||||
} else {
|
||||
let options = TransactOptions::with_no_tracing();
|
||||
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace.
|
||||
/// This will change the state accordingly.
|
||||
pub fn apply_with_tracing<V, T>(
|
||||
&mut self,
|
||||
env_info: &EnvInfo,
|
||||
engine: &Engine,
|
||||
t: &SignedTransaction,
|
||||
tracer: T,
|
||||
vm_tracer: V,
|
||||
) -> ApplyResult where
|
||||
T: trace::Tracer,
|
||||
V: trace::VMTracer,
|
||||
{
|
||||
let options = TransactOptions::new(tracer, vm_tracer);
|
||||
let e = self.execute(env_info, engine, t, options, false)?;
|
||||
|
||||
let e = self.execute(env_info, engine, t, tracing, false)?;
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
|
||||
self.commit()?;
|
||||
Some(self.root().clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let output = e.output;
|
||||
let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs);
|
||||
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
||||
|
||||
Ok(ApplyOutcome {
|
||||
receipt,
|
||||
output,
|
||||
trace: e.trace,
|
||||
vm_trace: e.vm_trace,
|
||||
})
|
||||
}
|
||||
|
||||
// Execute a given transaction without committing changes.
|
||||
//
|
||||
// `virt` signals that we are executing outside of a block set and restrictions like
|
||||
// gas limits and gas costs should be lifted.
|
||||
fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool, virt: bool)
|
||||
-> Result<Executed, ExecutionError>
|
||||
fn execute<T, V>(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
|
||||
-> Result<Executed, ExecutionError> where T: trace::Tracer, V: trace::VMTracer,
|
||||
{
|
||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||
let mut e = Executive::new(self, env_info, engine);
|
||||
|
||||
match virt {
|
||||
@@ -767,9 +800,8 @@ impl<B: Backend> State<B> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
/// Populate the state from `accounts`.
|
||||
/// Used for tests.
|
||||
pub fn populate_from(&mut self, accounts: PodState) {
|
||||
assert!(self.checkpoints.borrow().is_empty());
|
||||
for (add, acc) in accounts.drain().into_iter() {
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::sync::Arc;
|
||||
use io::IoChannel;
|
||||
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId};
|
||||
use state::{self, State, CleanupMode};
|
||||
use executive::Executive;
|
||||
use executive::{Executive, TransactOptions};
|
||||
use ethereum;
|
||||
use block::IsBlock;
|
||||
use tests::helpers::*;
|
||||
@@ -361,7 +361,7 @@ fn transaction_proof() {
|
||||
|
||||
let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap();
|
||||
Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine)
|
||||
.transact(&transaction, Default::default()).unwrap();
|
||||
.transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap();
|
||||
|
||||
assert_eq!(state.balance(&Address::default()).unwrap(), 5.into());
|
||||
assert_eq!(state.balance(&address).unwrap(), 95.into());
|
||||
|
||||
@@ -167,7 +167,7 @@ impl Tracer for ExecutiveTracer {
|
||||
ExecutiveTracer::default()
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<FlatTrace> {
|
||||
fn drain(self) -> Vec<FlatTrace> {
|
||||
self.traces
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ pub trait Tracer: Send {
|
||||
fn subtracer(&self) -> Self where Self: Sized;
|
||||
|
||||
/// Consumes self and returns all traces.
|
||||
fn traces(self) -> Vec<FlatTrace>;
|
||||
fn drain(self) -> Vec<FlatTrace>;
|
||||
}
|
||||
|
||||
/// Used by executive to build VM traces.
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Tracer for NoopTracer {
|
||||
NoopTracer
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<FlatTrace> {
|
||||
fn drain(self) -> Vec<FlatTrace> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user