diff --git a/common/src/utils/io/logging.rs b/common/src/utils/io/logging.rs index 265e93ba..af325b19 100644 --- a/common/src/utils/io/logging.rs +++ b/common/src/utils/io/logging.rs @@ -31,6 +31,7 @@ pub enum TraceCategory { Call, Create, Empty, + Suicide, } /// Individual trace, which is added to the trace factory. @@ -155,7 +156,7 @@ impl TraceFactory { } println!( "{} {}: {}", - replace_last(prefix, "│ ", " │ ").bold().blue(), + replace_last(prefix, "│ ", " │ ").bold().bright_white(), "data".purple(), trace.message.last().expect("Failed to build trace.") ); @@ -225,6 +226,15 @@ impl TraceFactory { trace.message.get(1).expect("Failed to build trace.").bold().green() ) } + TraceCategory::Suicide => { + println!( + "{} {} {} selfdestruct → {}", + replace_last(prefix, "│ ", " ├─").bold().bright_white(), + format!("[{}]", trace.instruction).bold().bright_white(), + trace.message.first().expect("Failed to build trace."), + trace.message.get(1).expect("Failed to build trace.") + ); + } } } @@ -284,6 +294,26 @@ impl TraceFactory { self.add("create", parent_index, instruction, vec![contract, format!("{size} bytes")]) } + /// adds a suicide event + pub fn add_suicide( + &mut self, + parent_index: u32, + instruction: u32, + address: String, + refund_address: String, + refund_amount: f64, + ) -> u32 { + self.add( + "suicide", + parent_index, + instruction, + vec![ + address, + format!("{} {}", refund_address, format!("[{} ether]", refund_amount).dimmed()), + ], + ) + } + /// adds a known log trace pub fn add_emission( &mut self, @@ -359,6 +389,7 @@ impl Trace { "call" => TraceCategory::Call, "create" => TraceCategory::Create, "empty" => TraceCategory::Empty, + "suicide" => TraceCategory::Suicide, _ => TraceCategory::Message, }, instruction, diff --git a/core/src/inspect/core/tracing.rs b/core/src/inspect/core/tracing.rs index 461b3d1b..18c27b21 100644 --- a/core/src/inspect/core/tracing.rs +++ b/core/src/inspect/core/tracing.rs @@ -24,7 +24,7 @@ use crate::{decode::DecodeArgsBuilder, error::Error}; use async_convert::{async_trait, TryFrom}; use futures::future::try_join_all; -use super::logs::DecodedLog; +use super::{contracts::Contracts, logs::DecodedLog}; /// Decoded Trace #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] @@ -352,81 +352,115 @@ impl DecodedTransactionTrace { Ok(()) } - pub fn add_to_trace(&self, trace: &mut TraceFactory, parent_trace_index: u32) { - // iterate over traces - for decoded_trace in self.subtraces.iter() { - let parent_trace_index = match &decoded_trace.action { - DecodedAction::Call(call) => trace.add_call_with_extra( - parent_trace_index, - call.gas.as_u32(), - call.to.to_lower_hex(), - match call.resolved_function.as_ref() { - Some(f) => f.name.clone(), - None => "unknown".to_string(), - }, - match call.resolved_function.as_ref() { - Some(f) => f - .decoded_inputs - .clone() - .unwrap_or_default() + pub fn add_to_trace( + &self, + contracts: &Contracts, + trace: &mut TraceFactory, + parent_trace_index: u32, + ) { + let parent_trace_index = match &self.action { + DecodedAction::Call(call) => trace.add_call_with_extra( + parent_trace_index, + call.gas.as_u32(), + contracts.get(call.to).unwrap_or(&call.to.to_lower_hex()).clone(), + match call.resolved_function.as_ref() { + Some(f) => f.name.clone(), + None => "fallback".to_string(), + }, + match call.resolved_function.as_ref() { + Some(f) => f + .decoded_inputs + .clone() + .unwrap_or_default() + .iter() + .map(|token| token.parameterize()) + .collect(), + None => vec![], + }, + match &self.result.as_ref() { + Some(DecodedRes::Call(call_result)) => { + let outputs = call_result + .decoded_outputs .iter() .map(|token| token.parameterize()) - .collect(), - None => vec![], - }, - match decoded_trace.result.as_ref() { - Some(DecodedRes::Call(call_result)) => { - let outputs = call_result - .decoded_outputs - .iter() - .map(|token| token.parameterize()) - .collect::>(); - - if outputs.is_empty() { - [call_result.output.to_lower_hex()].join(", ") - } else { - outputs.join(", ") - } + .collect::>(); + + if outputs.is_empty() { + [call_result.output.to_lower_hex()].join(", ") + } else { + outputs.join(", ") } - _ => "".to_string(), - }, - vec![ - format!("{:?}", call.call_type).to_lowercase(), - format!("value: {} wei", wei_to_ether(call.value)), - ], - ), - DecodedAction::Create(_) => todo!(), - DecodedAction::Suicide(_) => todo!(), - DecodedAction::Reward(_) => todo!(), - }; - - // for each log, add to trace - for log in &decoded_trace.logs { - if let Some(event) = &log.resolved_event { - // TODO: ResolveLog should decode raw data - trace.add_emission( - parent_trace_index, - log.log_index.unwrap_or(U256::zero()).as_u32(), - event.name.clone(), - event.inputs.clone(), - ); - trace.add_raw_emission( - parent_trace_index, - log.log_index.unwrap_or(U256::zero()).as_u32(), - log.topics.iter().map(|topic| topic.to_lower_hex()).collect(), - log.data.to_lower_hex(), - ); - } else { - trace.add_raw_emission( - parent_trace_index, - log.log_index.unwrap_or(U256::zero()).as_u32(), - log.topics.iter().map(|topic| topic.to_lower_hex()).collect(), - log.data.to_lower_hex(), - ); - } + } + _ => "".to_string(), + }, + vec![ + format!("{:?}", call.call_type).to_lowercase(), + format!("value: {} wei", wei_to_ether(call.value)), + ], + ), + DecodedAction::Create(create) => trace.add_creation( + parent_trace_index, + create.gas.as_u32(), + "NewContract".to_string(), + match &self.result.as_ref() { + Some(DecodedRes::Create(create_result)) => contracts + .get(create_result.address) + .unwrap_or(&create_result.address.to_lower_hex()) + .clone(), + _ => "".to_string(), + }, + create.init.len().try_into().unwrap_or(0), + ), + DecodedAction::Suicide(suicide) => trace.add_suicide( + parent_trace_index, + 0, + suicide.address.to_lower_hex(), + suicide.refund_address.to_lower_hex(), + wei_to_ether(suicide.balance), + ), + DecodedAction::Reward(reward) => trace.add_call_with_extra( + parent_trace_index, + 0, + Address::zero().to_lower_hex(), + "reward".to_string(), + vec![ + reward.author.to_lower_hex(), + format!("{:?}", reward.reward_type).to_lowercase(), + ], + "()".to_string(), + vec![format!("value: {} ether", wei_to_ether(reward.value))], + ), + }; + + // for each log, add to trace + for log in &self.logs { + if let Some(event) = &log.resolved_event { + // TODO: ResolveLog should decode raw data + trace.add_emission( + parent_trace_index, + log.log_index.unwrap_or(U256::zero()).as_u32(), + event.name.clone(), + event.inputs.clone(), + ); + trace.add_raw_emission( + parent_trace_index, + log.log_index.unwrap_or(U256::zero()).as_u32(), + log.topics.iter().map(|topic| topic.to_lower_hex()).collect(), + log.data.to_lower_hex(), + ); + } else { + trace.add_raw_emission( + parent_trace_index, + log.log_index.unwrap_or(U256::zero()).as_u32(), + log.topics.iter().map(|topic| topic.to_lower_hex()).collect(), + log.data.to_lower_hex(), + ); } + } - decoded_trace.add_to_trace(trace, parent_trace_index) + // iterate over traces + for decoded_trace in self.subtraces.iter() { + decoded_trace.add_to_trace(contracts, trace, parent_trace_index) } } } diff --git a/core/src/inspect/mod.rs b/core/src/inspect/mod.rs index 2050556a..5cb4b1c5 100644 --- a/core/src/inspect/mod.rs +++ b/core/src/inspect/mod.rs @@ -179,7 +179,7 @@ pub async fn inspect(args: InspectArgs) -> Result { "()".to_string(), ); - decoded_trace.add_to_trace(&mut trace, inspect_call); + decoded_trace.add_to_trace(&contracts, &mut trace, inspect_call); trace.display(); } else {