Skip to content

Commit

Permalink
feat(inspect): init transaction tracing (and decoding)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-Becker committed Dec 6, 2023
1 parent e38e6f6 commit 13b012d
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cache/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"]
clap = { version = "3.1.18", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
bincode = "1.3.3"
serde_json = "1.0.108"
20 changes: 12 additions & 8 deletions cache/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,9 @@ where
None => return None,
};

let binary_vec = decode_hex(&binary_string);
let binary_vec = decode_hex(&binary_string).ok()?;

if binary_vec.is_err() {
return None;
}

let cache: Cache<T> = match bincode::deserialize::<Cache<T>>(&binary_vec.unwrap()) {
let cache: Cache<T> = match bincode::deserialize::<Cache<T>>(&binary_vec) {
Ok(c) => {
// check if the cache has expired, if so, delete it and return None
if c.expiry <
Expand Down Expand Up @@ -233,7 +229,11 @@ where
/// store_cache("store_cache_key2", "value", Some(60 * 60 * 24));
/// ```
#[allow(deprecated)]
pub fn store_cache<T>(key: &str, value: T, expiry: Option<u64>)
pub fn store_cache<T>(
key: &str,
value: T,
expiry: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>>
where
T: Serialize, {
let home = home_dir().unwrap();
Expand All @@ -247,9 +247,12 @@ where
);

let cache = Cache { value, expiry };
let encoded: Vec<u8> = bincode::serialize(&cache).unwrap();
let encoded: Vec<u8> = bincode::serialize(&cache)
.map_err(|e| format!("Failed to serialize cache object: {:?}", e))?;
let binary_string = encode_hex(encoded);
write_file(cache_file.to_str().unwrap(), &binary_string);

Ok(())
}

/// Cache subcommand handler
Expand Down Expand Up @@ -289,6 +292,7 @@ pub fn cache(args: CacheArgs) -> Result<(), Box<dyn std::error::Error>> {
}

#[allow(deprecated)]
#[allow(unused_must_use)]
#[cfg(test)]
mod tests {
use crate::{delete_cache, exists, keys, read_cache, store_cache};
Expand Down
69 changes: 25 additions & 44 deletions common/src/ether/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ pub async fn chain_id(rpc_url: &str) -> Result<u64, Box<dyn std::error::Error>>
};

// cache the results
store_cache(&cache_key, chain_id.as_u64(), None);
let _ = store_cache(&cache_key, chain_id.as_u64(), None)
.map_err(|_| logger.error(&format!("failed to cache chain id for rpc url: {:?}", &rpc_url)));

debug_max!(&format!("chain_id is '{}'", &chain_id));

Expand Down Expand Up @@ -93,17 +94,16 @@ pub async fn get_code(
let logger = Logger::default();

// get chain_id
let _chain_id = chain_id(rpc_url).await.unwrap_or(1);

logger
.debug_max(&format!("fetching bytecode from node for contract: '{}' .", &contract_address));
let chain_id = chain_id(rpc_url).await.unwrap_or(1);

// check the cache for a matching address
if let Some(bytecode) = read_cache(&format!("contract.{}.{}", &_chain_id, &contract_address)) {
if let Some(bytecode) = read_cache(&format!("contract.{}.{}", &chain_id, &contract_address)) {
logger.debug(&format!("found cached bytecode for '{}' .", &contract_address));
return Ok(bytecode)
}

debug_max!("fetching bytecode from node for contract: '{}' .", &contract_address);

// make sure the RPC provider isn't empty
if rpc_url.is_empty() {
logger.error("reading on-chain data requires an RPC provider. Use `heimdall --help` for more information.");
Expand Down Expand Up @@ -138,11 +138,12 @@ pub async fn get_code(
};

// cache the results
store_cache(
&format!("contract.{}.{}", &_chain_id, &contract_address),
let _ = store_cache(
&format!("contract.{}.{}", &chain_id, &contract_address),
bytecode_as_bytes.to_string().replacen("0x", "", 1),
None,
);
)
.map_err(|_| logger.error(&format!("failed to cache bytecode for contract: {:?}", &contract_address)));

Ok(bytecode_as_bytes.to_string())
})
Expand Down Expand Up @@ -193,16 +194,16 @@ pub async fn get_transaction(
};

// safely unwrap the transaction hash
let transaction_hash = match H256::from_str(transaction_hash) {
let transaction_hash_hex = match H256::from_str(transaction_hash) {
Ok(transaction_hash) => transaction_hash,
Err(_) => {
logger.error(&format!("failed to parse transaction hash '{}' .", &transaction_hash));
std::process::exit(1)
}
};

// fetch the transaction from the node
Ok(match provider.get_transaction(transaction_hash).await {
// get the transaction
let tx = match provider.get_transaction(transaction_hash_hex).await {
Ok(tx) => match tx {
Some(tx) => tx,
None => {
Expand All @@ -214,7 +215,9 @@ pub async fn get_transaction(
logger.error(&format!("failed to fetch calldata from '{}' .", &transaction_hash));
return Err(backoff::Error::Transient { err: (), retry_after: Some(Duration::from_secs(1)) })
}
})
};

Ok(tx)
})
.await
.map_err(|_| Box::from("failed to get transaction"))
Expand Down Expand Up @@ -295,16 +298,17 @@ pub async fn get_storage_diff(
};

// write the state diff to the cache
let expiry = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() +
60 * 60 * 24 * 7;
store_cache(
let _ = store_cache(
&format!("diff.{}.{}", &chain_id, &transaction_hash),
&state_diff,
Some(expiry),
);
None,
)
.map_err(|_| {
logger.error(&format!(
"failed to cache state diff for transaction: {:?}",
&transaction_hash
))
});

debug_max!("fetched state diff for transaction '{}' .", &transaction_hash);

Expand Down Expand Up @@ -336,17 +340,6 @@ pub async fn get_trace(
// create new logger
let logger = Logger::default();

// get chain_id
let chain_id = chain_id(rpc_url).await.unwrap();

// check the cache for a matching address
if let Some(block_trace) =
read_cache(&format!("trace.{}.{}", &chain_id, &transaction_hash))
{
debug_max!("found cached trace for transaction '{}' .", &transaction_hash);
return Ok(block_trace);
}

debug_max!(&format!(
"fetching trace from node for transaction: '{}' .",
&transaction_hash
Expand Down Expand Up @@ -392,18 +385,6 @@ pub async fn get_trace(
}
};

// write the trace to the cache
let expiry = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60 * 60 * 24 * 7;
store_cache(
&format!("trace.{}.{}", &chain_id, &transaction_hash),
&block_trace,
Some(expiry),
);

debug_max!("fetched trace for transaction '{}' .", &transaction_hash);

Ok(block_trace)
Expand Down
9 changes: 6 additions & 3 deletions common/src/ether/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ impl ResolveSelector for ResolvedError {
}

// cache the results
store_cache(&format!("selector.{selector}"), signature_list.clone(), None);
let _ = store_cache(&format!("selector.{selector}"), signature_list.clone(), None)
.map_err(|e| debug_max!("error storing signatures in cache: {}", e));

match signature_list.len() {
0 => None,
Expand Down Expand Up @@ -184,7 +185,8 @@ impl ResolveSelector for ResolvedLog {
}

// cache the results
store_cache(&format!("selector.{selector}"), signature_list.clone(), None);
let _ = store_cache(&format!("selector.{selector}"), signature_list.clone(), None)
.map_err(|e| debug_max!("error storing signatures in cache: {}", e));

match signature_list.len() {
0 => None,
Expand Down Expand Up @@ -263,7 +265,8 @@ impl ResolveSelector for ResolvedFunction {
}

// cache the results
store_cache(&format!("selector.{selector}"), signature_list.clone(), None);
let _ = store_cache(&format!("selector.{selector}"), signature_list.clone(), None)
.map_err(|e| debug_max!("error storing signatures in cache: {}", e));

match signature_list.len() {
0 => None,
Expand Down
2 changes: 1 addition & 1 deletion common/src/utils/io/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ macro_rules! debug_max {
$crate::utils::io::logging::Logger::default().debug_max($message);
};
($message:expr, $($arg:tt)*) => {
$crate::utils::io::logging::Logger::default().debug_max(&format!($message, $($arg)*));
$crate::utils::io::logging::Logger::default().debug_max(&format!($message, $($arg)*))
};
}
29 changes: 29 additions & 0 deletions common/src/utils/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{fmt::Write, num::ParseIntError};
use ethers::{
abi::AbiEncode,
prelude::{I256, U256},
types::{Bloom, H160, H256, H64},
};
use fancy_regex::Regex;

Expand Down Expand Up @@ -309,6 +310,34 @@ pub fn tokenize(s: &str) -> Vec<String> {
tokens
}

pub trait ToLowerHex {
fn to_lower_hex(&self) -> String;
}

impl ToLowerHex for H256 {
fn to_lower_hex(&self) -> String {
format!("{:#032x}", self)
}
}

impl ToLowerHex for H160 {
fn to_lower_hex(&self) -> String {
format!("{:#020x}", self)
}
}

impl ToLowerHex for H64 {
fn to_lower_hex(&self) -> String {
format!("{:#016x}", self)
}
}

impl ToLowerHex for Bloom {
fn to_lower_hex(&self) -> String {
format!("{:#064x}", self)
}
}

#[derive(Debug, PartialEq)]
pub enum TokenType {
Control,
Expand Down
Loading

0 comments on commit 13b012d

Please sign in to comment.