Skip to content

Commit

Permalink
feat(resources): add trace display
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-Becker committed Dec 11, 2023
1 parent 7abefaa commit c69346a
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 25 deletions.
5 changes: 4 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
cmd.openai_api_key = configuration.openai_api_key;
}

// set cmd.verbose to 6
// set cmd.verbose to 5
cmd.verbose = clap_verbosity_flag::Verbosity::new(5, 0);

let _ = decode(cmd).await;
Expand Down Expand Up @@ -357,6 +357,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
filename = format!("{}-{}", given_name, filename);
}

// set cmd.verbose to 5
cmd.verbose = clap_verbosity_flag::Verbosity::new(5, 0);

let inspect_result = inspect(cmd.clone()).await?;

if cmd.output == "print" {
Expand Down
20 changes: 10 additions & 10 deletions common/src/resources/transpose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use reqwest::header::HeaderMap;
use serde_json::Value;
use std::time::{Duration, Instant};

use crate::utils::io::logging::Logger;
use crate::{debug_max, utils::io::logging::Logger};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -255,10 +255,6 @@ pub async fn get_contract_creation(
/// // let label = get_label(address, api_key).await;
/// ```
pub async fn get_label(address: &str, api_key: &str) -> Option<String> {
// get a new logger
let logger = Logger::default();
let start_time = Instant::now();

// build the SQL query
let query = format!(
"{{\"sql\":\"SELECT COALESCE( (SELECT name FROM ethereum.contract_labels WHERE contract_address = '{address}' ), (SELECT ens_name FROM ethereum.ens_names WHERE primary_address = '{address}' LIMIT 1), (SELECT protocol_name FROM ethereum.protocols WHERE contract_address = '{address}' ), (SELECT symbol FROM ethereum.tokens WHERE contract_address = '{address}' ), (SELECT symbol FROM ethereum.collections WHERE contract_address = '{address}' ) ) as label\",\"parameters\":{{}},\"options\":{{\"timeout\": 999999999}}}}",
Expand All @@ -267,25 +263,29 @@ pub async fn get_label(address: &str, api_key: &str) -> Option<String> {
let response = match call_transpose(&query, api_key).await {
Some(response) => response,
None => {
logger.error("failed to get contract label from Transpose");
debug_max!(&format!("failed to get label from Transpose for address: {}", address));
return None;
}
};

logger.debug(&format!("fetching contract creation took {:?}", start_time.elapsed()));

// parse the results
if let Some(result) = response.results.into_iter().next() {
let label: String = match result.get("label") {
Some(label) => match label.as_str() {
Some(label) => label.to_string(),
None => {
logger.error("failed to parse label from Transpose");
debug_max!(&format!(
"failed to parse label from Transpose for address: {}",
address
));
return None;
}
},
None => {
logger.error("failed to fetch label from Transpose response");
debug_max!(&format!(
"failed to fetch label from Transpose response for address: {}",
address
));
return None;
}
};
Expand Down
14 changes: 5 additions & 9 deletions common/src/utils/io/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ impl TraceFactory {
}
TraceCategory::Log => {
println!(
"{} emit {}",
"{} emit {} {}",
replace_last(prefix, "│ ", " ├─").bold().bright_white(),
trace.message.first().expect("Failed to build trace.")
trace.message.first().expect("Failed to build trace."),
format!("[log index: {}]", trace.instruction).dimmed(),
);
}
TraceCategory::LogUnknown => {
Expand All @@ -146,13 +147,8 @@ impl TraceFactory {
let message =
trace.message.get(message_index).expect("Failed to build trace.");
println!(
"{} {} {}: {}",
if message_index == 0 {
replace_last(prefix, "│ ", " ├─").bold().bright_white()
} else {
replace_last(prefix, "│ ", " │ ").bold().bright_white()
},
if message_index == 0 { "emit" } else { " " },
"{} {}: {}",
replace_last(prefix, "│ ", " │ ").bold().bright_white(),
format!("topic {message_index}").purple(),
message
);
Expand Down
92 changes: 91 additions & 1 deletion core/src/inspect/core/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ use ethers::{
ExecutedInstruction, Reward, Suicide, TransactionTrace, VMTrace, U256,
},
};
use heimdall_common::ether::signatures::ResolvedFunction;
use heimdall_common::{
ether::signatures::ResolvedFunction,
utils::{
hex::ToLowerHex,
io::{logging::TraceFactory, types::Parameterize},
},
};
use serde::{Deserialize, Serialize};

use crate::{decode::DecodeArgsBuilder, error::Error};
Expand Down Expand Up @@ -345,4 +351,88 @@ 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()
.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::<Vec<String>>();

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(),
);
}
}

decoded_trace.add_to_trace(trace, parent_trace_index)
}
}
}

fn wei_to_ether(wei: U256) -> f64 {
// convert U256 to u64
let wei = wei.as_u64() as f64;
wei / 10f64.powf(18.0)
}
26 changes: 22 additions & 4 deletions core/src/inspect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ use derive_builder::Builder;
use ethers::types::{Log, TransactionTrace, U256, U64};
use futures::future::try_join_all;
use heimdall_common::{
debug_max,
ether::rpc::{get_block_logs, get_trace, get_transaction},
utils::io::logging::Logger,
utils::{
hex::ToLowerHex,
io::logging::{Logger, TraceFactory},
},
};

use crate::error::Error;
Expand Down Expand Up @@ -117,7 +121,7 @@ pub async fn inspect(args: InspectArgs) -> Result<InspectResult, Error> {
let handles =
transaction_logs.into_iter().map(<DecodedLog as async_convert::TryFrom<Log>>::try_from);

logger.debug(&format!("decoding {} logs", handles.len()));
debug_max!(&format!("resolving event signatures for {} logs", handles.len()));

// sort logs by log index
let mut decoded_logs = try_join_all(handles).await?;
Expand All @@ -136,7 +140,7 @@ pub async fn inspect(args: InspectArgs) -> Result<InspectResult, Error> {
None => None,
};
if let Some(decoded_trace) = decoded_trace.as_mut() {
logger.debug("resolving address contract labels");
debug_max!("resolving address contract labels");

// get contracts client
let mut contracts = Contracts::new(&args);
Expand All @@ -156,14 +160,28 @@ pub async fn inspect(args: InspectArgs) -> Result<InspectResult, Error> {
.warn("no state diff found for transaction. skipping state diff label resolution");
}

logger.debug(&format!("joining {} decoded logs to trace", decoded_logs.len()));
debug_max!(&format!("joining {} decoded logs to trace", decoded_logs.len()));

if let Some(vm_trace) = block_trace.vm_trace {
// join logs to trace
let _ = decoded_trace.join_logs(&mut decoded_logs, vm_trace, Vec::new()).await;
} else {
logger.warn("no vm trace found for transaction. skipping joining logs");
}

let mut trace = TraceFactory::default();
let inspect_call = trace.add_call(
0,
transaction.gas.as_u32(),
"heimdall".to_string(),
"inspect".to_string(),
vec![transaction.hash.to_lower_hex()],
"()".to_string(),
);

decoded_trace.add_to_trace(&mut trace, inspect_call);

trace.display();
} else {
logger.warn("no trace found for transaction");
}
Expand Down

0 comments on commit c69346a

Please sign in to comment.