From 06862c7dee400e3b9f37c6cfdae52c081ee9d7f1 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 18 Jul 2017 12:14:06 +0200 Subject: [PATCH] Tracing for rewards added. Without tests for now --- ethcore/src/block.rs | 22 +++++- ethcore/src/engines/authority_round/mod.rs | 20 ++++- ethcore/src/engines/mod.rs | 12 ++- ethcore/src/engines/tendermint/mod.rs | 21 +++++- ethcore/src/ethereum/ethash.rs | 33 +++++++-- ethcore/src/trace/executive_tracer.rs | 17 ++++- ethcore/src/trace/mod.rs | 5 +- ethcore/src/trace/noop_tracer.rs | 5 +- ethcore/src/trace/types/filter.rs | 4 + ethcore/src/trace/types/trace.rs | 86 +++++++++++++++++++++- rpc/src/v1/types/trace.rs | 58 +++++++++++++++ 11 files changed, 262 insertions(+), 21 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 8cd1e22b6..de04dd8bc 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -92,6 +92,7 @@ pub struct ExecutedBlock { transactions_set: HashSet, state: State, traces: Option>>, + tracing_enabled: bool, } /// A set of references to `ExecutedBlock` fields that are publicly accessible. @@ -108,6 +109,8 @@ pub struct BlockRefMut<'a> { pub state: &'a mut State, /// Traces. pub traces: &'a Option>>, + /// Tracing enabled flag. + pub tracing_enabled: &'a mut bool, } /// A set of immutable references to `ExecutedBlock` fields that are publicly accessible. @@ -124,6 +127,8 @@ pub struct BlockRef<'a> { pub state: &'a State, /// Traces. pub traces: &'a Option>>, + /// Tracing enabled flag. + pub tracing_enabled: &'a bool, } impl ExecutedBlock { @@ -137,6 +142,7 @@ impl ExecutedBlock { transactions_set: Default::default(), state: state, traces: if tracing {Some(Vec::new())} else {None}, + tracing_enabled: tracing, } } @@ -149,6 +155,7 @@ impl ExecutedBlock { state: &mut self.state, receipts: &self.receipts, traces: &self.traces, + tracing_enabled: &mut self.tracing_enabled, } } @@ -161,6 +168,7 @@ impl ExecutedBlock { state: &self.state, receipts: &self.receipts, traces: &self.traces, + tracing_enabled: &self.tracing_enabled, } } } @@ -196,6 +204,9 @@ pub trait IsBlock { /// Get all uncles in this block. fn uncles(&self) -> &[Header] { &self.block().uncles } + + /// Get tracing enabled flag for this block. + fn tracing_enabled(&self) -> &bool { &self.block().tracing_enabled } } /// Trait for a object that has a state database. @@ -392,9 +403,16 @@ impl<'x> OpenBlock<'x> { let unclosed_state = s.block.state.clone(); - if let Err(e) = s.engine.on_close_block(&mut s.block) { - warn!("Encountered error on closing the block: {}", e); + match s.engine.on_close_block(&mut s.block) { + Ok(outcome) => { + let t = outcome.trace; + if t.is_some() { + s.block.traces.as_mut().map(|traces| traces.push(t.unwrap())); + } + } + Err(e) => warn!("Encountered error on closing the block: {}", e), } + if let Err(e) = s.block.state.commit() { warn!("Encountered error on state commit: {}", e); } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index ebe7cacea..73fb6adb8 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -24,7 +24,9 @@ use account_provider::AccountProvider; use block::*; use builtin::Builtin; use client::{Client, EngineClient}; -use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier}; +use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier, CloseOutcome}; +use trace::{Tracer, ExecutiveTracer}; +use types::trace_types::trace::{RewardType}; use error::{Error, TransactionError, BlockError}; use ethjson; use header::{Header, BlockNumber}; @@ -543,18 +545,30 @@ impl Engine for AuthorityRound { } /// Apply the block reward on finalisation of the block. - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result { let fields = block.fields_mut(); + let mut tracer = ExecutiveTracer::default(); // Bestow block reward let reward = self.params().block_reward; let res = fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty) .map_err(::error::Error::from) .and_then(|_| fields.state.commit()); + + // Trace it + let block_miner = fields.header.author().clone(); + tracer.trace_reward(block_miner, self.block_reward, RewardType::Block); + // Commit state so that we can actually figure out the state root. if let Err(ref e) = res { warn!("Encountered error on closing block: {}", e); } - res + match res { + Ok(res) => match *fields.tracing_enabled { + true => Ok(CloseOutcome{trace: Some(tracer.traces())}), + false => Ok(CloseOutcome{trace: None}) + }, + Err(e) => Err(e) + } } /// Check the number of seal fields. diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index c5fa4818e..116b98054 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -48,6 +48,7 @@ use error::Error; use evm::Schedule; use header::{Header, BlockNumber}; use receipt::Receipt; +use trace::FlatTrace; use snapshot::SnapshotComponents; use spec::CommonParams; use transaction::{UnverifiedTransaction, SignedTransaction}; @@ -81,6 +82,13 @@ pub enum EngineError { RequiresClient, } +/// Used to return information about close block operation. +#[derive(Debug)] +pub struct CloseOutcome { + /// The trace for the closing block, if None if tracing is disabled. + pub trace: Option>, +} + impl fmt::Display for EngineError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::EngineError::*; @@ -228,8 +236,8 @@ pub trait Engine : Sync + Send { } /// Block transformation functions, after the transactions. - fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { - Ok(()) + fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result { + Ok(CloseOutcome{trace: None}) } /// None means that it requires external input (e.g. PoW) to seal a block. diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 1f3dbeb4e..b7fd7e0e8 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -37,7 +37,9 @@ use ethkey::{Message, public_to_address, recover, Signature}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; -use engines::{Engine, Seal, EngineError, ConstructedVerifier}; +use engines::{Engine, Seal, EngineError, CloseOutcome, ConstructedVerifier}; +use trace::{Tracer, ExecutiveTracer}; +use types::trace_types::trace::{RewardType}; use state::CleanupMode; use io::IoService; use super::signer::EngineSigner; @@ -538,18 +540,31 @@ impl Engine for Tendermint { } /// Apply the block reward on finalisation of the block. - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{ + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result{ let fields = block.fields_mut(); + let mut tracer = ExecutiveTracer::default(); // Bestow block reward let reward = self.params().block_reward; let res = fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty) .map_err(::error::Error::from) .and_then(|_| fields.state.commit()); + + // Trace it + let block_miner = fields.header.author().clone(); + tracer.trace_reward(block_miner, self.block_reward, RewardType::Block); + // Commit state so that we can actually figure out the state root. if let Err(ref e) = res { warn!("Encountered error on closing block: {}", e); } - res + match res { + Ok(res) => match *fields.tracing_enabled { + true => Ok(CloseOutcome{trace: Some(tracer.traces())}), + false => Ok(CloseOutcome{trace: None}) + }, + Err(e) => Err(e) + } + } fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index ef6bb64cd..28c55119f 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -21,11 +21,13 @@ use block::*; use builtin::Builtin; use evm::env_info::EnvInfo; use error::{BlockError, Error, TransactionError}; +use trace::{Tracer, ExecutiveTracer}; +use types::trace_types::trace::{RewardType}; use header::{Header, BlockNumber}; use state::CleanupMode; use spec::CommonParams; use transaction::UnverifiedTransaction; -use engines::{self, Engine}; +use engines::{self, Engine, CloseOutcome}; use evm::Schedule; use ethjson; use rlp::{self, UntrustedRlp}; @@ -271,40 +273,57 @@ impl Engine for Arc { /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + fn on_close_block(&self, block: &mut ExecutedBlock) -> Result { let reward = self.params().block_reward; let fields = block.fields_mut(); let eras_rounds = self.ethash_params.ecip1017_era_rounds; let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, fields.header.number()); + let mut tracer = ExecutiveTracer::default(); // Bestow block reward + let result_block_reward = reward + reward / U256::from(32) * U256::from(fields.uncles.len()); fields.state.add_balance( fields.header.author(), - &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())), + &result_block_reward, CleanupMode::NoEmpty )?; + // Trace it + let block_miner = fields.header.author().clone(); + tracer.trace_reward(block_miner, result_block_reward, RewardType::Block); + // Bestow uncle rewards let current_number = fields.header.number(); for u in fields.uncles.iter() { + let uncle_miner = u.author().clone(); + let mut result_uncle_reward: U256 = U256::from(0); + if eras == 0 { + result_uncle_reward = reward * U256::from(8 + u.number() - current_number) / U256::from(8); fields.state.add_balance( u.author(), - &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)), + &(result_uncle_reward), CleanupMode::NoEmpty - ) + ) } else { + result_uncle_reward = reward / U256::from(32); fields.state.add_balance( u.author(), - &(reward / U256::from(32)), + &(result_uncle_reward), CleanupMode::NoEmpty ) }?; + + // Trace uncle rewards + tracer.trace_reward(uncle_miner, result_uncle_reward, RewardType::Uncle); } // Commit state so that we can actually figure out the state root. fields.state.commit()?; - Ok(()) + match *fields.tracing_enabled { + true => Ok(CloseOutcome{trace: Some(tracer.traces())}), + false => Ok(CloseOutcome{trace: None}) + } } fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index cdfe1e004..65d8ebe3e 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,7 +18,7 @@ use util::{Bytes, Address, U256}; use evm::action_params::ActionParams; -use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide}; +use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; use trace::{Tracer, VMTracer, FlatTrace, TraceError}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. @@ -162,6 +162,21 @@ impl Tracer for ExecutiveTracer { debug!(target: "trace", "Traced failed suicide {:?}", trace); self.traces.push(trace); } + + fn trace_reward(&mut self, miner: Address, value: U256, reward_type: RewardType) { + let trace = FlatTrace { + subtraces: 0, + action: Action::Reward(Reward { + miner: miner, + value: value, + reward_type: reward_type, + }), + result: Res::None, + trace_address: Default::default(), + }; + debug!(target: "trace", "Traced failed reward {:?}", trace); + self.traces.push(trace); + } fn subtracer(&self) -> Self { ExecutiveTracer::default() diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 39af8a08a..2b318a260 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -33,7 +33,7 @@ pub use self::localized::LocalizedTrace; pub use self::types::{filter, flat, localized, trace}; pub use self::types::error::Error as TraceError; -pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff}; +pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}; pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; pub use self::types::filter::{Filter, AddressesFilter}; @@ -81,6 +81,9 @@ pub trait Tracer: Send { /// Stores suicide info. fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); + /// Stores reward info. + fn trace_reward(&mut self, miner: Address, value: U256, reward_type: RewardType); + /// Spawn subtracer which will be used to trace deeper levels of execution. fn subtracer(&self) -> Self where Self: Sized; diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 5fb8a7c55..091d69547 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -19,7 +19,7 @@ use util::{Bytes, Address, U256}; use evm::action_params::ActionParams; use trace::{Tracer, VMTracer, FlatTrace, TraceError}; -use trace::trace::{Call, Create, VMTrace}; +use trace::trace::{Call, Create, VMTrace, RewardType}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -58,6 +58,9 @@ impl Tracer for NoopTracer { fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) { } + fn trace_reward(&mut self, miner: Address, value: U256, reward_type: RewardType) { + } + fn subtracer(&self) -> Self { NoopTracer } diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/src/trace/types/filter.rs index 2dc810442..35d70f84e 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/src/trace/types/filter.rs @@ -128,6 +128,10 @@ impl Filter { let from_matches = self.from_address.matches(&suicide.address); let to_matches = self.to_address.matches(&suicide.refund_address); from_matches && to_matches + }, + Action::Reward(ref reward) => { + let to_matches = self.to_address.matches(&reward.miner); + to_matches } } } diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/src/trace/types/trace.rs index 24250935f..69e351cbb 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/src/trace/types/trace.rs @@ -218,6 +218,81 @@ impl Create { } } +/// Reward type. +#[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "ipc", binary)] +pub enum RewardType { + /// None + None, + /// Block + Block, + /// Uncle + Uncle, +} + +impl Encodable for RewardType { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + RewardType::None => 0u32, + RewardType::Block => 1, + RewardType::Uncle => 2, + }; + Encodable::rlp_append(&v, s); + } +} + +impl Decodable for RewardType { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.as_val().and_then(|v| Ok(match v { + 0u32 => RewardType::None, + 1 => RewardType::Block, + 2 => RewardType::Uncle, + _ => return Err(DecoderError::Custom("Invalid value of RewardType item")), + })) + } +} + +/// Reward action +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "ipc", binary)] +pub struct Reward { + /// Miner's address. + pub miner: Address, + /// Reward amount. + pub value: U256, + /// Reward type. + pub reward_type: RewardType, +} + +impl Reward { + /// Return reward action bloom. + pub fn bloom(&self) -> LogBloom { + LogBloom::from_bloomed(&self.miner.sha3()) + } +} + +impl Encodable for Reward { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.miner); + s.append(&self.value); + s.append(&self.reward_type); + } +} + +impl Decodable for Reward { + fn decode(rlp: &UntrustedRlp) -> Result { + let res = Reward { + miner: rlp.val_at(0)?, + value: rlp.val_at(1)?, + reward_type: rlp.val_at(2)?, + }; + + Ok(res) + } +} + + /// Suicide action. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "ipc", binary)] @@ -260,7 +335,7 @@ impl Decodable for Suicide { } -/// Description of an action that we trace; will be either a call or a create. +/// Description of an action that we trace. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "ipc", binary)] pub enum Action { @@ -270,6 +345,8 @@ pub enum Action { Create(Create), /// Suicide. Suicide(Suicide), + /// Reward + Reward(Reward), } impl Encodable for Action { @@ -287,7 +364,12 @@ impl Encodable for Action { Action::Suicide(ref suicide) => { s.append(&2u8); s.append(suicide); + }, + Action::Reward(ref reward) => { + s.append(&3u8); + s.append(reward); } + } } } @@ -299,6 +381,7 @@ impl Decodable for Action { 0 => rlp.val_at(1).map(Action::Call), 1 => rlp.val_at(1).map(Action::Create), 2 => rlp.val_at(1).map(Action::Suicide), + 3 => rlp.val_at(1).map(Action::Reward), _ => Err(DecoderError::Custom("Invalid action type.")), } } @@ -311,6 +394,7 @@ impl Action { Action::Call(ref call) => call.bloom(), Action::Create(ref create) => create.bloom(), Action::Suicide(ref suicide) => suicide.bloom(), + Action::Reward(ref reward) => reward.bloom(), } } } diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index b01d04c3f..0fef8ae2b 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -299,6 +299,53 @@ impl From for Call { } } +/// Reward type. +#[derive(Debug, Serialize)] +pub enum RewardType { + /// None + #[serde(rename="none")] + None, + /// Block + #[serde(rename="block")] + Block, + /// Uncle + #[serde(rename="uncle")] + Uncle, +} + +impl From for RewardType { + fn from(c: trace::RewardType) -> Self { + match c { + trace::RewardType::None => RewardType::None, + trace::RewardType::Block => RewardType::Block, + trace::RewardType::Uncle => RewardType::Uncle, + } + } +} + + +/// Reward action +#[derive(Debug, Serialize)] +pub struct Reward { + /// Miner's address. + pub miner: H160, + /// Reward amount. + pub value: U256, + /// Reward type. + #[serde(rename="rewardType")] + pub reward_type: RewardType, +} + +impl From for Reward { + fn from(r: trace::Reward) -> Self { + Reward { + miner: r.miner.into(), + value: r.value.into(), + reward_type: r.reward_type.into(), + } + } +} + /// Suicide #[derive(Debug, Serialize)] pub struct Suicide { @@ -330,6 +377,8 @@ pub enum Action { Create(Create), /// Suicide Suicide(Suicide), + /// Reward + Reward(Reward), } impl From for Action { @@ -338,6 +387,7 @@ impl From for Action { trace::Action::Call(call) => Action::Call(call.into()), trace::Action::Create(create) => Action::Create(create.into()), trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()), + trace::Action::Reward(reward) => Action::Reward(reward.into()), } } } @@ -449,6 +499,10 @@ impl Serialize for LocalizedTrace { struc.serialize_field("type", "suicide")?; struc.serialize_field("action", suicide)?; }, + Action::Reward(ref reward) => { + struc.serialize_field("type", "reward")?; + struc.serialize_field("action", reward)?; + }, } match self.result { @@ -516,6 +570,10 @@ impl Serialize for Trace { struc.serialize_field("type", "suicide")?; struc.serialize_field("action", suicide)?; }, + Action::Reward(ref reward) => { + struc.serialize_field("type", "reward")?; + struc.serialize_field("action", reward)?; + }, } match self.result {