Suicides tracing (#1688)

* tracing suicide

* fixed #1635

* fixed typo
This commit is contained in:
Marek Kotewicz 2016-07-22 14:47:23 +02:00 committed by Gav Wood
parent ce00c13c7a
commit 63dbb527cc
8 changed files with 208 additions and 11 deletions

View File

@ -263,6 +263,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
self.state.transfer_balance(&address, refund_address, &balance);
}
self.tracer.trace_suicide(address, balance, refund_address.clone(), self.depth + 1);
self.substate.suicides.insert(address);
}

View File

@ -1154,6 +1154,58 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
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 = Some(Trace {
depth: 0,
action: trace::Action::Call(trace::Call {
from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(),
to: 0xa.into(),
value: 100.into(),
gas: 79000.into(),
input: vec![],
}),
result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(),
output: vec![]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Suicide(trace::Suicide {
address: 0xa.into(),
refund_address: 0xb.into(),
balance: 150.into(),
}),
result: trace::Res::None,
subs: vec![]
}]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn code_from_database() {
let a = Address::zero();

View File

@ -18,7 +18,7 @@
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
@ -97,6 +97,20 @@ impl Tracer for ExecutiveTracer {
self.traces.push(trace);
}
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize) {
let trace = Trace {
depth: depth,
subs: vec![],
action: Action::Suicide(Suicide {
address: address,
refund_address: refund_address,
balance: balance,
}),
result: Res::None,
};
self.traces.push(trace);
}
fn subtracer(&self) -> Self {
ExecutiveTracer::default()
}

View File

@ -81,6 +81,9 @@ pub trait Tracer: Send {
/// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);
/// Stores suicide info.
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address, depth: usize);
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized;

View File

@ -55,6 +55,9 @@ impl Tracer for NoopTracer {
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, _depth: usize) {
}
fn subtracer(&self) -> Self {
NoopTracer
}

View File

@ -121,15 +121,20 @@ impl Filter {
let to_matches = self.to_address.matches_all();
from_matches && to_matches
}
Action::Suicide(ref suicide) => {
let from_matches = self.from_address.matches(&suicide.address);
let to_matches = self.to_address.matches(&suicide.refund_address);
from_matches && to_matches
}
}
}
}
#[cfg(test)]
mod tests {
use util::{FixedHash, Address, U256};
use util::{FixedHash, Address};
use util::sha3::Hashable;
use trace::trace::{Action, Call, Res};
use trace::trace::{Action, Call, Res, Suicide};
use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter};
use basic_types::LogBloom;
@ -270,10 +275,10 @@ mod tests {
let trace = FlatTrace {
action: Action::Call(Call {
from: Address::from(1),
to: Address::from(2),
value: U256::from(3),
gas: U256::from(4),
from: 1.into(),
to: 2.into(),
value: 3.into(),
gas: 4.into(),
input: vec![0x5],
}),
result: Res::FailedCall,
@ -288,5 +293,24 @@ mod tests {
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![],
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));
}
}

View File

@ -205,6 +205,48 @@ impl Create {
}
}
/// Suicide action.
#[derive(Debug, Clone, PartialEq, Binary)]
pub struct Suicide {
/// Suicided address.
pub address: Address,
/// Suicided contract heir.
pub refund_address: Address,
/// Balance of the contract just before suicide.
pub balance: U256,
}
impl Suicide {
/// Return suicide action bloom.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.address.sha3())
.with_bloomed(&self.refund_address.sha3())
}
}
impl Encodable for Suicide {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.address);
s.append(&self.refund_address);
s.append(&self.balance);
}
}
impl Decodable for Suicide {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = Suicide {
address: try!(d.val_at(0)),
refund_address: try!(d.val_at(1)),
balance: try!(d.val_at(3)),
};
Ok(res)
}
}
/// Description of an action that we trace; will be either a call or a create.
#[derive(Debug, Clone, PartialEq, Binary)]
pub enum Action {
@ -212,6 +254,8 @@ pub enum Action {
Call(Call),
/// It's a create action.
Create(Create),
/// Suicide.
Suicide(Suicide),
}
impl Encodable for Action {
@ -225,6 +269,10 @@ impl Encodable for Action {
Action::Create(ref create) => {
s.append(&1u8);
s.append(create);
},
Action::Suicide(ref suicide) => {
s.append(&2u8);
s.append(suicide);
}
}
}
@ -237,6 +285,7 @@ impl Decodable for Action {
match action_type {
0 => d.val_at(1).map(Action::Call),
1 => d.val_at(1).map(Action::Create),
2 => d.val_at(2).map(Action::Suicide),
_ => Err(DecoderError::Custom("Invalid action type.")),
}
}
@ -248,6 +297,7 @@ impl Action {
match *self {
Action::Call(ref call) => call.bloom(),
Action::Create(ref create) => create.bloom(),
Action::Suicide(ref suicide) => suicide.bloom(),
}
}
}
@ -263,6 +313,8 @@ pub enum Res {
FailedCall,
/// Failed create.
FailedCreate,
/// None
None,
}
impl Encodable for Res {
@ -285,6 +337,10 @@ impl Encodable for Res {
Res::FailedCreate => {
s.begin_list(1);
s.append(&3u8);
},
Res::None => {
s.begin_list(1);
s.append(&4u8);
}
}
}
@ -299,6 +355,7 @@ impl Decodable for Res {
1 => d.val_at(1).map(Res::Create),
2 => Ok(Res::FailedCall),
3 => Ok(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")),
}
}
@ -518,7 +575,7 @@ 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};
use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide};
#[test]
fn traces_rlp() {
@ -577,6 +634,16 @@ mod tests {
}),
subs: vec![],
result: Res::FailedCreate
},
Trace {
depth: 3,
action: Action::Suicide(Suicide {
address: 101.into(),
refund_address: 102.into(),
balance: 0.into(),
}),
subs: vec![],
result: Res::None,
}
],
result: Res::Call(CallResult {
@ -592,5 +659,8 @@ mod tests {
assert!(bloom.contains_bloomed(&Address::from(2).sha3()));
assert!(!bloom.contains_bloomed(&Address::from(20).sha3()));
assert!(bloom.contains_bloomed(&Address::from(6).sha3()));
assert!(bloom.contains_bloomed(&Address::from(101).sha3()));
assert!(bloom.contains_bloomed(&Address::from(102).sha3()));
assert!(!bloom.contains_bloomed(&Address::from(103).sha3()));
}
}

View File

@ -260,6 +260,28 @@ impl From<trace::Call> for Call {
}
}
/// Suicide
#[derive(Debug, Serialize)]
pub struct Suicide {
/// Address.
pub address: H160,
/// Refund address.
#[serde(rename="refundAddress")]
pub refund_address: H160,
/// 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(),
}
}
}
/// Action
#[derive(Debug, Serialize)]
pub enum Action {
@ -269,13 +291,17 @@ pub enum Action {
/// Create
#[serde(rename="create")]
Create(Create),
/// Suicide
#[serde(rename="suicide")]
Suicide(Suicide),
}
impl From<trace::Action> for Action {
fn from(c: trace::Action) -> Self {
match c {
trace::Action::Call(call) => Action::Call(Call::from(call)),
trace::Action::Create(create) => Action::Create(Create::from(create)),
trace::Action::Call(call) => Action::Call(call.into()),
trace::Action::Create(create) => Action::Create(create.into()),
trace::Action::Suicide(suicide) => Action::Suicide(suicide.into()),
}
}
}
@ -336,6 +362,9 @@ pub enum Res {
/// Creation failure
#[serde(rename="failedCreate")]
FailedCreate,
/// None
#[serde(rename="none")]
None,
}
impl From<trace::Res> for Res {
@ -345,6 +374,7 @@ impl From<trace::Res> for Res {
trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
trace::Res::FailedCall => Res::FailedCall,
trace::Res::FailedCreate => Res::FailedCreate,
trace::Res::None => Res::None,
}
}
}