From a7fa324f5809d7e0c79634c667e75de4297b5c28 Mon Sep 17 00:00:00 2001 From: Jon-Becker Date: Thu, 28 Dec 2023 11:48:27 -0600 Subject: [PATCH] feat(decompile): add support for `LOG0` anonymous events --- cache/src/lib.rs | 2 +- cache/src/util.rs | 6 +-- cli/src/output.rs | 4 +- common/src/ether/evm/core/types.rs | 22 ++++---- common/src/ether/evm/ext/exec/mod.rs | 16 +++--- common/src/ether/evm/ext/exec/util.rs | 20 +++---- common/src/ether/rpc.rs | 2 +- common/src/ether/selectors.rs | 8 +-- common/src/ether/signatures.rs | 6 +-- common/src/resources/transpose.rs | 10 ++-- common/src/utils/http.rs | 4 +- common/src/utils/io/logging.rs | 10 ++-- common/src/utils/strings.rs | 14 ++--- core/src/decode/core/abi.rs | 20 +++---- core/src/decode/mod.rs | 18 +++---- core/src/decompile/analyzers/solidity.rs | 18 +++---- core/src/decompile/analyzers/yul.rs | 8 +-- core/src/decompile/out/abi.rs | 13 +++-- .../decompile/out/postprocessers/solidity.rs | 54 +++++++++---------- core/src/decompile/out/postprocessers/yul.rs | 20 +++---- core/src/decompile/out/solidity.rs | 5 +- core/src/decompile/resolve.rs | 6 +-- core/src/dump/util/threads/indexer.rs | 2 +- core/src/dump/util/threads/tui.rs | 10 ++-- core/src/inspect/core/contracts.rs | 6 +-- core/src/inspect/core/tracing.rs | 2 +- core/src/snapshot/analyze.rs | 6 +-- core/src/snapshot/resolve.rs | 6 +-- core/src/snapshot/util/tui.rs | 4 +- core/tests/test_decompile.rs | 1 + core/tests/test_inspect.rs | 2 +- 31 files changed, 164 insertions(+), 161 deletions(-) diff --git a/cache/src/lib.rs b/cache/src/lib.rs index 3a821270..da7fc3bd 100644 --- a/cache/src/lib.rs +++ b/cache/src/lib.rs @@ -206,7 +206,7 @@ where .as_secs() { delete_cache(key); - return None; + return None } c diff --git a/cache/src/util.rs b/cache/src/util.rs index 7bbf7cbc..138a8dda 100644 --- a/cache/src/util.rs +++ b/cache/src/util.rs @@ -54,13 +54,13 @@ pub fn prettify_bytes(bytes: u64) -> String { format!("{bytes} B") } else if bytes < 1024 * 1024 { let kb = bytes / 1024; - return format!("{kb} KB"); + return format!("{kb} KB") } else if bytes < 1024 * 1024 * 1024 { let mb = bytes / (1024 * 1024); - return format!("{mb} MB"); + return format!("{mb} MB") } else { let gb = bytes / (1024 * 1024 * 1024); - return format!("{gb} GB"); + return format!("{gb} GB") } } diff --git a/cli/src/output.rs b/cli/src/output.rs index d4d12657..947c8ec9 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -24,9 +24,9 @@ pub async fn build_output_path( if ADDRESS_REGEX.is_match(target)? || TRANSACTION_HASH_REGEX.is_match(target)? { let chain_id = rpc::chain_id(rpc_url).await?; - return Ok(format!("{}/output/{}/{}/{}", cwd, chain_id, target, filename)); + return Ok(format!("{}/output/{}/{}/{}", cwd, chain_id, target, filename)) } else { - return Ok(format!("{}/output/local/{}", cwd, filename)); + return Ok(format!("{}/output/local/{}", cwd, filename)) } } diff --git a/common/src/ether/evm/core/types.rs b/common/src/ether/evm/core/types.rs index 752e1537..589058c2 100644 --- a/common/src/ether/evm/core/types.rs +++ b/common/src/ether/evm/core/types.rs @@ -31,7 +31,7 @@ pub fn parse_function_parameters(function_signature: &str) -> Option Option> { // if string is empty, return None if string.is_empty() { - return None; + return None } // if the string contains a tuple we cant simply split on commas @@ -57,7 +57,7 @@ fn extract_types_from_string(string: &str) -> Option> { // get balanced encapsulator let (tuple_start, tuple_end, valid) = find_balanced_encapsulator(string, ('(', ')')); if !valid { - return None; + return None } // extract the tuple @@ -78,7 +78,7 @@ fn extract_types_from_string(string: &str) -> Option> { if is_array { let (start, end, valid) = find_balanced_encapsulator(split, ('[', ']')); if !valid { - return None; + return None } let size = split[start + 1..end - 1].to_string(); @@ -156,7 +156,7 @@ fn extract_types_from_string(string: &str) -> Option> { // iterate over the split string and convert each type to a ParamType for string_type in split { if string_type.is_empty() { - continue; + continue } let param_type = to_type(string_type); @@ -191,7 +191,7 @@ pub fn to_type(string: &str) -> ParamType { while string.ends_with(']') { let (start, end, valid) = find_balanced_encapsulator(&string, ('[', ']')); if !valid { - return ParamType::Bytes; // default to bytes if invalid + return ParamType::Bytes // default to bytes if invalid } let size = string[start + 1..end - 1].to_string(); @@ -239,7 +239,7 @@ pub fn to_type(string: &str) -> ParamType { } } - return arg_type; + return arg_type } arg_type @@ -343,17 +343,17 @@ pub fn get_padding(bytes: &str) -> Padding { if null_byte_indices.is_empty() || null_byte_indices[0] != 0 && null_byte_indices[null_byte_indices.len() - 1] != size - 1 { - return Padding::None; + return Padding::None } // the first byte is a null byte AND the last byte is not a null byte, it is left padded if null_byte_indices[0] == 0 && null_byte_indices[null_byte_indices.len() - 1] != size - 1 { - return Padding::Left; + return Padding::Left } // the first byte is not a null byte AND the last byte is a null byte, it is right padded if null_byte_indices[0] != 0 && null_byte_indices[null_byte_indices.len() - 1] == size - 1 { - return Padding::Right; + return Padding::Right } // get non-null byte indices @@ -365,7 +365,7 @@ pub fn get_padding(bytes: &str) -> Padding { .collect::>(); if non_null_byte_indices.is_empty() { - return Padding::None; + return Padding::None } // check if the there are more null-bytes before the first non-null byte than after the last diff --git a/common/src/ether/evm/ext/exec/mod.rs b/common/src/ether/evm/ext/exec/mod.rs index 4ff6387e..514ac1db 100644 --- a/common/src/ether/evm/ext/exec/mod.rs +++ b/common/src/ether/evm/ext/exec/mod.rs @@ -40,7 +40,7 @@ impl VM { // this shouldn't be necessary, but it's safer to have it if self.exitcode != 255 || !self.returndata.is_empty() { - break; + break } } @@ -104,13 +104,13 @@ impl VM { // if the stack has over 16 items of the same source, it's probably a loop if stack_contains_too_many_of_the_same_item(&vm.stack) { - return vm_trace; + return vm_trace } // if any item on the stack has a depth > 16, it's probably a loop (because of stack // too deep) if stack_item_source_depth_too_deep(&vm.stack) { - return vm_trace; + return vm_trace } // break out of loops @@ -211,7 +211,7 @@ impl VM { // this key exists, but the stack is different, so the jump is new historical_stacks.push(vm.stack.clone()); - return vm_trace; + return vm_trace } else { debug_max!( "adding historical stack {} to jump frame {:?}", @@ -253,7 +253,7 @@ impl VM { // push the current path onto the stack vm_trace.children.push(vm.recursive_map(branch_count, handled_jumps)); - break; + break } else { // push a new vm trace to the children let mut trace_vm = vm.clone(); @@ -262,7 +262,7 @@ impl VM { // push the current path onto the stack vm_trace.children.push(vm.recursive_map(branch_count, handled_jumps)); - break; + break } } @@ -330,7 +330,7 @@ impl VM { // this key exists, but the stack is different, so the jump is new historical_stacks.push(vm.stack.clone()); - return vm_trace; + return vm_trace } else { debug_max!( "adding historical stack {} to jump frame {:?}", @@ -351,7 +351,7 @@ impl VM { // when the vm exits, this path is complete if vm.exitcode != 255 || !vm.returndata.is_empty() { - break; + break } } diff --git a/common/src/ether/evm/ext/exec/util.rs b/common/src/ether/evm/ext/exec/util.rs index 2e5c9047..6b795316 100644 --- a/common/src/ether/evm/ext/exec/util.rs +++ b/common/src/ether/evm/ext/exec/util.rs @@ -34,7 +34,7 @@ pub fn stack_contains_too_many_of_the_same_item(stack: &Stack) -> bool { debug_max!( "jump matches loop-detection heuristic: 'stack_contains_too_many_of_the_same_item'", ); - return true; + return true } false @@ -50,7 +50,7 @@ pub fn stack_item_source_depth_too_deep(stack: &Stack) -> bool { logger .debug_max("jump matches loop-detection heuristic: 'stack_item_source_depth_too_deep'"); - return true; + return true } false @@ -70,7 +70,7 @@ pub fn jump_condition_appears_recursive(stack_diff: &[StackFrame], jump_conditio logger .debug_max("jump matches loop-detection heuristic: 'jump_condition_appears_recursive'"); - return true; + return true } false @@ -85,7 +85,7 @@ pub fn jump_condition_contains_mutated_memory_access( if stack_diff.iter().any(|frame| { memory_accesses.any(|_match| { if _match.is_err() { - return false; + return false } let memory_access = _match.unwrap(); let slice = &jump_condition[memory_access.start()..memory_access.end()]; @@ -93,7 +93,7 @@ pub fn jump_condition_contains_mutated_memory_access( }) }) { debug_max!("jump matches loop-detection heuristic: 'jump_condition_contains_mutated_memory_access'"); - return true; + return true } false @@ -108,7 +108,7 @@ pub fn jump_condition_contains_mutated_storage_access( if stack_diff.iter().any(|frame| { storage_accesses.any(|_match| { if _match.is_err() { - return false; + return false } let storage_access = _match.unwrap(); let slice = &jump_condition[storage_access.start()..storage_access.end()]; @@ -116,7 +116,7 @@ pub fn jump_condition_contains_mutated_storage_access( }) }) { debug_max!("jump matches loop-detection heuristic: 'jump_condition_contains_mutated_storage_access'"); - return true; + return true } false @@ -130,7 +130,7 @@ pub fn jump_condition_historical_diffs_approximately_equal( // break if historical_stacks.len() < 4 // this is an arbitrary number, i picked it randomly :D if historical_stacks.len() < 4 { - return false; + return false } // get the stack diffs for all historical stacks @@ -149,12 +149,12 @@ pub fn jump_condition_historical_diffs_approximately_equal( // check if all stack diffs are similar if !stack_diffs.iter().all(|diff| diff.len() <= threshold) { - return false; + return false } // check if all stack diffs are the same if !stack_diffs.iter().all(|diff| diff[0] == stack_diffs[0][0]) { - return false; + return false } debug_max!("jump matches loop-detection heuristic: 'jump_condition_historical_diffs_approximately_equal'"); diff --git a/common/src/ether/rpc.rs b/common/src/ether/rpc.rs index a81f795c..b6e40a7f 100644 --- a/common/src/ether/rpc.rs +++ b/common/src/ether/rpc.rs @@ -254,7 +254,7 @@ pub async fn get_storage_diff( read_cache(&format!("diff.{}.{}", &chain_id, &transaction_hash)) { debug_max!("found cached state diff for transaction '{}' .", &transaction_hash); - return Ok(state_diff); + return Ok(state_diff) } debug_max!(&format!( diff --git a/common/src/ether/selectors.rs b/common/src/ether/selectors.rs index d171a724..e4b67631 100644 --- a/common/src/ether/selectors.rs +++ b/common/src/ether/selectors.rs @@ -63,7 +63,7 @@ pub fn find_function_selectors(evm: &VM, assembly: &str) -> HashMap u128 { jump_condition.contains(" == ") && jump_taken == 1 { - return call.last_instruction.inputs[0].try_into().unwrap_or(0); + return call.last_instruction.inputs[0].try_into().unwrap_or(0) } else if jump_taken == 1 { // if handled_jumps contains the jumpi, we have already handled this jump. // loops aren't supported in the dispatcher, so we can just return 0 if handled_jumps.contains(&call.last_instruction.inputs[0].try_into().unwrap_or(0)) { - return 0; + return 0 } else { handled_jumps.insert(call.last_instruction.inputs[0].try_into().unwrap_or(0)); } @@ -130,7 +130,7 @@ pub fn resolve_entry_point(evm: &VM, selector: &str) -> u128 { } if vm.exitcode != 255 || !vm.returndata.is_empty() { - break; + break } } diff --git a/common/src/ether/signatures.rs b/common/src/ether/signatures.rs index 82e1c5f5..2e86374f 100644 --- a/common/src/ether/signatures.rs +++ b/common/src/ether/signatures.rs @@ -56,7 +56,7 @@ impl ResolveSelector for ResolvedError { 0 => return None, _ => { debug_max!("found cached results for selector: {}", &selector); - return Some(cached_results); + return Some(cached_results) } } } @@ -141,7 +141,7 @@ impl ResolveSelector for ResolvedLog { 0 => return None, _ => { debug_max!("found cached results for selector: {}", &selector); - return Some(cached_results); + return Some(cached_results) } } } @@ -226,7 +226,7 @@ impl ResolveSelector for ResolvedFunction { 0 => return None, _ => { debug_max!("found cached results for selector: {}", &selector); - return Some(cached_results); + return Some(cached_results) } } } diff --git a/common/src/resources/transpose.rs b/common/src/resources/transpose.rs index 91192131..bc02d429 100644 --- a/common/src/resources/transpose.rs +++ b/common/src/resources/transpose.rs @@ -247,7 +247,7 @@ pub async fn get_contract_creation( } }; - return Some((block_number, transaction_hash)); + return Some((block_number, transaction_hash)) }; None @@ -273,7 +273,7 @@ pub async fn get_label(address: &str, api_key: &str) -> Option { Some(response) => response, None => { debug_max!(&format!("failed to get label from Transpose for address: {}", address)); - return None; + return None } }; @@ -287,7 +287,7 @@ pub async fn get_label(address: &str, api_key: &str) -> Option { "failed to parse label from Transpose for address: {}", address )); - return None; + return None } }, None => { @@ -295,10 +295,10 @@ pub async fn get_label(address: &str, api_key: &str) -> Option { "failed to fetch label from Transpose response for address: {}", address )); - return None; + return None } }; - return Some(label); + return Some(label) }; None diff --git a/common/src/utils/http.rs b/common/src/utils/http.rs index 7b822d80..db7101cc 100644 --- a/common/src/utils/http.rs +++ b/common/src/utils/http.rs @@ -45,7 +45,7 @@ async fn _get_json_from_url( Err(e) => { debug_max!("GET {}: {:?}", &url, &e); if retries_remaining == 0 { - return Ok(None); + return Ok(None) } // exponential backoff @@ -53,7 +53,7 @@ async fn _get_json_from_url( let retries_remaining = retries_remaining - 1; let sleep_time = 2u64.pow(retry_count as u32) * 250; async_sleep(Duration::from_millis(sleep_time)).await; - return _get_json_from_url(url, retry_count, retries_remaining, timeout).await; + return _get_json_from_url(url, retry_count, retries_remaining, timeout).await } }; let body = res.text().await?; diff --git a/common/src/utils/io/logging.rs b/common/src/utils/io/logging.rs index 70d12d6d..3653da45 100644 --- a/common/src/utils/io/logging.rs +++ b/common/src/utils/io/logging.rs @@ -589,7 +589,7 @@ impl Logger { ) -> u8 { // if silent, return the default if self.level == -1 { - return default.expect("Failed to get default option."); + return default.expect("Failed to get default option.") } // log the message with the given class @@ -633,7 +633,7 @@ impl Logger { } else { println!(); } - return default.expect("Failed to get default option."); + return default.expect("Failed to get default option.") } // get input @@ -642,10 +642,10 @@ impl Logger { // check if default was selected if selection.trim() == "" { if let Some(default) = default { - return default; + return default } else { self.error("invalid selection."); - return self.option(function, message, options, default, skip); + return self.option(function, message, options, default, skip) } } @@ -654,7 +654,7 @@ impl Logger { Ok(i) => i, Err(_) => { self.error("invalid selection."); - return self.option(function, message, options, default, skip); + return self.option(function, message, options, default, skip) } }; diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index 16423e83..615486d0 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -124,7 +124,7 @@ pub fn find_balanced_encapsulator(s: &str, encap: (char, char)) -> (usize, usize } if open == close && open > 0 { end = i; - break; + break } } (start, end + 1, (open == close && end > start && open > 0)) @@ -155,7 +155,7 @@ pub fn find_balanced_encapsulator_backwards(s: &str, encap: (char, char)) -> (us } if open == close && open > 0 { end = i; - break; + break } } (s.len() - end - 1, s.len() - start, (open == close && end > start && open > 0)) @@ -233,7 +233,7 @@ pub fn extract_condition(s: &str, keyword: &str) -> Option { condition = condition.split(", ").collect::>()[0]; } - return Some(condition.trim().to_string()); + return Some(condition.trim().to_string()) } } @@ -323,18 +323,18 @@ pub enum TokenType { pub fn classify_token(token: &str) -> TokenType { // return if the token is a parenthesis if token == "(" || token == ")" { - return TokenType::Control; + return TokenType::Control } // check if the token is an operator let operators = ['+', '-', '*', '/', '=', '>', '<', '!', '&', '|', '%', '^']; if token.chars().all(|c| operators.contains(&c)) { - return TokenType::Operator; + return TokenType::Operator } // check if the token is a constant if token.starts_with("0x") || token.parse::().is_ok() { - return TokenType::Constant; + return TokenType::Constant } // check if the token is a variable @@ -345,7 +345,7 @@ pub fn classify_token(token: &str) -> TokenType { .iter() .any(|keyword| token.contains(keyword)) { - return TokenType::Variable; + return TokenType::Variable } // this token must be a function call diff --git a/core/src/decode/core/abi.rs b/core/src/decode/core/abi.rs index 6d95e60d..c5e1bd26 100644 --- a/core/src/decode/core/abi.rs +++ b/core/src/decode/core/abi.rs @@ -89,14 +89,14 @@ fn process_and_validate_word( // ABI-encoded item if word % 32 != U256::zero() || word == U256::zero() { debug_max!("parameter {}: '{}' doesnt appear to be an offset ptr", parameter_index, word); - return Err(Error::BoundsError); + return Err(Error::BoundsError) } // check if the pointer is pointing to a valid location in the calldata let word_offset = word / 32; if word_offset >= U256::from(calldata_words.len()) { debug_max!("parameter {}: '{}' is out of bounds (offset check)", parameter_index, word); - return Err(Error::BoundsError); + return Err(Error::BoundsError) } Ok((word, word_offset)) @@ -123,7 +123,7 @@ fn try_decode_dynamic_parameter_bytes( // to contain the ABI-encoded item. If there aren't, return an [`Error::BoundsError`]. if data_words.join("").len() / 2 < size.as_usize() { debug_max!("parameter {}: '{}' is out of bounds (bytes check)", parameter_index, word); - return Ok(None); + return Ok(None) } // (3) calculate how many words are needed to store the encoded data with size `size`. @@ -142,7 +142,7 @@ fn try_decode_dynamic_parameter_bytes( let padding_size = get_padding_size(last_word); if padding_size > 32 - last_word_size { debug_max!("parameter {}: '{}' with size {} cannot fit into last word with padding of {} bytes (bytes)", parameter_index, word, size, padding_size); - return Ok(None); + return Ok(None) } // (5) we've covered all words from `data_start_word_offset` to `data_end_word_offset`, @@ -186,7 +186,7 @@ fn try_decode_dynamic_parameter_array( size, coverages.clone(), ) { - return Ok(Some(abi_encoded)); + return Ok(Some(abi_encoded)) } // (3) this is not a `string` type, so we can assume that it is an array. we can extend @@ -240,7 +240,7 @@ fn try_decode_dynamic_parameter_string( .all(|padding| padding == get_padding(data_words[0])) { debug_max!("parameter {}: '{}' is not string (conforming padding)", parameter_index, word); - return Ok(None); + return Ok(None) } debug_max!("parameter {}: '{}' may be string", parameter_index, word); @@ -263,7 +263,7 @@ fn try_decode_dynamic_parameter_string( let padding_size = get_padding_size(last_word); if padding_size > 32 - last_word_size { debug_max!("parameter {}: '{}' with size {} cannot fit into last word with padding of {} bytes (string)", parameter_index, word, size, padding_size); - return Err(Error::BoundsError); + return Err(Error::BoundsError) } // (5) we've covered all words from `data_start_word_offset` to `data_end_word_offset`, @@ -311,7 +311,7 @@ fn get_potential_type( // merge coverages and nested_coverages coverages.extend(nested_coverages); - return (32, vec![nested_abi_encoded_param.ty]); + return (32, vec![nested_abi_encoded_param.ty]) } let (padding_size, mut potential_types) = get_potential_types_for_word(w); @@ -332,9 +332,9 @@ fn get_potential_type( .fold((0, String::from("")), |(max_size, mut potential_type), (size, types)| { // "address" and "string" are priority types if types.contains(&String::from("string")) { - return (32, String::from("string")); + return (32, String::from("string")) } else if types.contains(&String::from("address")) { - return (32, String::from("address")); + return (32, String::from("address")) } if size > max_size { diff --git a/core/src/decode/mod.rs b/core/src/decode/mod.rs index dfe8d0c9..96dbca74 100644 --- a/core/src/decode/mod.rs +++ b/core/src/decode/mod.rs @@ -110,7 +110,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { logger.error("OpenAI API key is required for explaining calldata. Use `heimdall decode --help` for more information."); return Err(Error::GenericError( "OpenAI API key is required for explaining calldata.".to_string(), - )); + )) } // init variables @@ -133,13 +133,13 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { logger.error("invalid target. must be a transaction hash or calldata (bytes)."); return Err(Error::GenericError( "invalid target. must be a transaction hash or calldata (bytes).".to_string(), - )); + )) } // check if the calldata length is a standard length if calldata.len() % 2 != 0 || calldata.len() < 8 { logger.error("calldata is not a valid hex string."); - return Err(Error::GenericError("calldata is not a valid hex string.".to_string())); + return Err(Error::GenericError("calldata is not a valid hex string.".to_string())) } // if calldata isn't a multiple of 64, it may be harder to decode. @@ -161,7 +161,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { Ok(byte_args) => byte_args, Err(_) => { logger.error("failed to parse bytearray from calldata."); - return Err(Error::DecodeError); + return Err(Error::DecodeError) } }; @@ -219,7 +219,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { Some(decoded_function_call) => decoded_function_call.1, None => { logger.debug(&format!("potential match '{}' ignored. decoded inputs differed from provided calldata.", &potential_match.signature).to_string()); - continue; + continue } }; @@ -337,7 +337,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { matches.push(resolved_function); } else { logger.error("failed to dynamically decode calldata."); - return Err(Error::DecodeError); + return Err(Error::DecodeError) } } @@ -364,7 +364,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { Some(selected_match) => selected_match, None => { logger.error("invalid selection."); - return Err(Error::GenericError("invalid selection.".to_string())); + return Err(Error::GenericError("invalid selection.".to_string())) } }; @@ -404,7 +404,7 @@ pub async fn decode(args: DecodeArgs) -> Result, Error> { for (i, input) in selected_match.decoded_inputs.as_ref().unwrap().iter().enumerate() { let mut decoded_inputs_as_message = display(vec![input.to_owned()], " "); if decoded_inputs_as_message.is_empty() { - break; + break } if i == 0 { @@ -468,7 +468,7 @@ fn try_decode(inputs: &[ParamType], byte_args: &[u8]) -> Result<(Vec, Vec }); } - return Ok((result, params)); + return Ok((result, params)) } Err(Error::DecodeError) diff --git a/core/src/decompile/analyzers/solidity.rs b/core/src/decompile/analyzers/solidity.rs index 63968f8f..9fa65db3 100644 --- a/core/src/decompile/analyzers/solidity.rs +++ b/core/src/decompile/analyzers/solidity.rs @@ -137,16 +137,13 @@ pub fn analyze_sol( } }; + let event_selector = logged_event.topics.first().unwrap_or(&U256::zero()).to_owned(); + let anonymous = event_selector == U256::zero(); + // check to see if the event is a duplicate - if !function - .events - .iter() - .any(|(selector, _)| selector == logged_event.topics.first().unwrap()) - { + if !function.events.iter().any(|(selector, _)| selector == &event_selector) { // add the event to the function - function - .events - .insert(*logged_event.topics.first().unwrap(), (None, logged_event.clone())); + function.events.insert(event_selector, (None, logged_event.clone())); // decode the data field let data_mem_ops = @@ -160,7 +157,7 @@ pub fn analyze_sol( // add the event emission to the function's logic // will be decoded during post-processing function.logic.push(format!( - "emit Event_{}({}{});", + "emit Event_{}({}{});{}", &logged_event .topics .first() @@ -188,7 +185,8 @@ pub fn analyze_sol( }, None => "".to_string(), }, - data_mem_ops_solidified + data_mem_ops_solidified, + if anonymous { " // anonymous event" } else { "" } )); } } else if opcode_name == "JUMPI" { diff --git a/core/src/decompile/analyzers/yul.rs b/core/src/decompile/analyzers/yul.rs index 97433873..b56efb57 100644 --- a/core/src/decompile/analyzers/yul.rs +++ b/core/src/decompile/analyzers/yul.rs @@ -115,7 +115,7 @@ pub fn analyze_yul( "unable to decode event emission at instruction {}", instruction.instruction )); - continue; + continue } }; @@ -167,7 +167,7 @@ pub fn analyze_yul( // handle case with error string abiencoded if revert_data.starts_with(&decode_hex("4e487b71").unwrap()) { - continue; + continue } // handle case with custom error OR empty revert else { @@ -189,7 +189,7 @@ pub fn analyze_yul( instruction.input_operations[1].yulify() ); - break; + break } } } @@ -429,7 +429,7 @@ pub fn analyze_yul( for j in (0..conditional_map.len()).rev() { if conditional_map[j] == jumped_conditional.clone().unwrap() { conditional_map.remove(j); - break; + break } } diff --git a/core/src/decompile/out/abi.rs b/core/src/decompile/out/abi.rs index cc191f6a..6ebbb2c9 100644 --- a/core/src/decompile/out/abi.rs +++ b/core/src/decompile/out/abi.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use ethers::abi::AbiEncode; +use ethers::{abi::AbiEncode, types::U256}; use heimdall_common::utils::io::{ file::short_path, logging::{Logger, TraceFactory}, @@ -49,6 +49,7 @@ pub struct EventABI { pub type_: String, pub name: String, pub inputs: Vec, + pub anonymous: bool, } /// An [`ABIStructure`] may be a function, error, or event @@ -213,7 +214,7 @@ pub fn build_abi( ABIStructure::Error(x) => x.name == resolved_error.name, _ => false, }) { - continue; + continue } abi.push(ABIStructure::Error(ErrorABI { @@ -234,7 +235,7 @@ pub fn build_abi( } _ => false, }) { - continue; + continue } abi.push(ABIStructure::Error(ErrorABI { @@ -272,13 +273,14 @@ pub fn build_abi( ABIStructure::Event(x) => x.name == resolved_event.name, _ => false, }) { - continue; + continue } abi.push(ABIStructure::Event(EventABI { type_: "event".to_string(), name: resolved_event.name.clone(), inputs, + anonymous: false, })); } None => { @@ -293,7 +295,7 @@ pub fn build_abi( } _ => false, }) { - continue; + continue } abi.push(ABIStructure::Event(EventABI { @@ -303,6 +305,7 @@ pub fn build_abi( &event_selector.encode_hex().replacen("0x", "", 1)[0..8] ), inputs: Vec::new(), + anonymous: event_selector == &U256::zero(), })); } } diff --git a/core/src/decompile/out/postprocessers/solidity.rs b/core/src/decompile/out/postprocessers/solidity.rs index 36faed2c..6c1c51f2 100644 --- a/core/src/decompile/out/postprocessers/solidity.rs +++ b/core/src/decompile/out/postprocessers/solidity.rs @@ -177,12 +177,12 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { // if there is a negation of an expression, remove the parentheses // helps with double negation if first_char == "!" && last_char == ")" { - return true; + return true } // remove the parentheses if the expression is within brackets if first_char == "[" && last_char == "]" { - return true; + return true } // parens required if: @@ -190,14 +190,14 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { // - expression is a function call // - expression is the surrounding parens of a conditional if first_char != "(" { - return false; + return false } else if last_char == ")" { - return true; + return true } // don't include instantiations if expression.contains("memory ret") { - return false; + return false } // handle the inside of the expression @@ -207,14 +207,14 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { }; let inner_tokens = tokenize(&inside); - return !inner_tokens.iter().any(|tk| classify_token(tk) == TokenType::Operator); + return !inner_tokens.iter().any(|tk| classify_token(tk) == TokenType::Operator) } let mut cleaned: String = line.to_owned(); // skip lines that are defining a function if cleaned.contains("function") { - return cleaned; + return cleaned } // get the nth index of the first open paren @@ -414,7 +414,7 @@ fn convert_access_to_variable(line: &str) -> String { fn contains_unnecessary_assignment(line: &str, lines: &Vec<&str>) -> bool { // skip lines that don't contain an assignment, or contain a return or external calls if !line.contains(" = ") || line.contains("bool success") || line.contains("return") { - return false; + return false } // get var name @@ -423,25 +423,25 @@ fn contains_unnecessary_assignment(line: &str, lines: &Vec<&str>) -> bool { // skip lines that contain assignments to storage if var_name.contains("stor_") { - return false; + return false } //remove unused vars for x in lines { // break if the line contains a function definition if x.contains("function") { - break; + break } if x.contains(" = ") { let assignment = x.split(" = ").map(|x| x.trim()).collect::>(); if assignment[1].contains(var_name) { - return false; + return false } else if assignment[0].contains(var_name) { - return true; + return true } } else if x.contains(var_name) { - return false; + return false } } @@ -456,12 +456,12 @@ fn move_casts_to_declaration(line: &str) -> String { // if line contains "function" wipe the set if cleaned.contains("function") { type_declaration_set.clear(); - return cleaned.to_owned(); + return cleaned.to_owned() } // if the line doesn't contain an instantiation, return if !cleaned.contains(" = ") { - return cleaned.to_owned(); + return cleaned.to_owned() } let instantiation = cleaned.split(" = ").collect::>(); @@ -471,7 +471,7 @@ fn move_casts_to_declaration(line: &str) -> String { Some(x) => { // the match must occur at index 0 if x.start() != 0 { - return cleaned.to_owned(); + return cleaned.to_owned() } // find the matching close paren @@ -480,7 +480,7 @@ fn move_casts_to_declaration(line: &str) -> String { // the close paren must be at the end of the expression if paren_end != instantiation[1].len() - 1 { - return cleaned.to_owned(); + return cleaned.to_owned() } // get the inside of the parens @@ -515,19 +515,19 @@ fn replace_expression_with_var(line: &str) -> String { // skip function definitions if cleaned.contains("function") { - return cleaned; + return cleaned } // iterate over variable map for (var, expression) in var_map.iter() { // skip numeric expressions if expression.parse::().is_ok() { - continue; + continue } // skip expressions that are already variables. i.e, check if they contain a space if !expression.contains(' ') { - continue; + continue } // replace the expression with the variable @@ -576,7 +576,7 @@ fn inherit_infer_mem_type(line: &str) -> String { // if the line does not contains an instantiation, return if !line.contains(" = ") || line.trim().starts_with("stor") { - return cleaned; + return cleaned } let instantiation = line.split(" = ").collect::>(); @@ -597,7 +597,7 @@ fn inherit_infer_mem_type(line: &str) -> String { if cleaned.contains(var) && !type_map.contains_key(var_name) && !var_type.is_empty() { cleaned = format!("{var_type} {cleaned}"); type_map.insert(var_name.to_string(), var_type.to_string()); - break; + break } } } @@ -636,7 +636,7 @@ fn inherit_infer_storage_type(line: &str) { // since the regex is greedy, match the memory brackets let matched_loc = find_balanced_encapsulator(storage_access, ('[', ']')); if !matched_loc.2 { - return; + return } let mut storage_slot = format!("storage{}", storage_access.get(matched_loc.0..matched_loc.1).unwrap()); @@ -680,7 +680,7 @@ fn inherit_infer_storage_type(line: &str) { lhs_type = var_type.to_string(); // continue, so we cannot use this var in rhs - continue; + continue } // check for vars in rhs @@ -765,7 +765,7 @@ fn replace_resolved( // line must contain CustomError_ or Event_ if !cleaned.contains("CustomError_") && !cleaned.contains("Event_") { - return cleaned; + return cleaned } // not the best way to do it, can perf later @@ -805,7 +805,7 @@ fn cleanup( // skip comments if cleaned.starts_with('/') { - return cleaned; + return cleaned } // Find and convert all castings @@ -880,7 +880,7 @@ fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { ) { cleaned_lines.push(line.to_string()); } else { - continue; + continue } } diff --git a/core/src/decompile/out/postprocessers/yul.rs b/core/src/decompile/out/postprocessers/yul.rs index e2d0525a..9134766c 100644 --- a/core/src/decompile/out/postprocessers/yul.rs +++ b/core/src/decompile/out/postprocessers/yul.rs @@ -67,7 +67,7 @@ fn convert_bitmask_to_casting(line: &str) -> String { let is_rhs_all_ones = arg2.replacen("0x", "", 1).chars().all(|c| c == 'f' || c == 'F'); if !is_lhs_all_ones && !is_rhs_all_ones { index += end_index + 1; - continue; // skip if LHS and RHS are not bitwise masks + continue // skip if LHS and RHS are not bitwise masks } // determine size of bytes based on argument 1 @@ -139,7 +139,7 @@ fn remove_replace_casts(line: &str) -> String { cleaned.replace_range(cast_start - cast_type.len()..=cast_end - 1, &yul_cast); } else { - break; + break } } @@ -157,7 +157,7 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { // if there is a negation of an expression, remove the parentheses // helps with double negation if first_char == "iszero" && last_char == ")" { - return true; + return true } // parens required if: @@ -165,14 +165,14 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { // - expression is a function call // - expression is the surrounding parens of a conditional if first_char != "(" { - return false; + return false } else if last_char == ")" { - return true; + return true } // don't include instantiations if expression.contains(":=") { - return false; + return false } // handle the inside of the expression @@ -197,7 +197,7 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { // skip lines that are defining a function if cleaned.contains("case") { - return cleaned; + return cleaned } // get the nth index of the first open paren @@ -261,7 +261,7 @@ fn add_resolved_events(line: &str, all_resolved_events: HashMap) -> Str // skip comments if cleaned.starts_with('/') { - return cleaned; + return cleaned } // remove double negations diff --git a/core/src/decompile/out/solidity.rs b/core/src/decompile/out/solidity.rs index 6e2c13a1..008dc487 100644 --- a/core/src/decompile/out/solidity.rs +++ b/core/src/decompile/out/solidity.rs @@ -78,14 +78,15 @@ pub fn build_solidity_output( for event in abi.iter().filter(|x| matches!(x, ABIStructure::Event(_))) { if let ABIStructure::Event(event) = event { decompiled_output.push(format!( - "event {}({});", + "event {}({}){};", event.name, event .inputs .iter() .map(|x| format!("{} {}", x.type_, x.name)) .collect::>() - .join(", ") + .join(", "), + if event.anonymous { " anonymous" } else { "" } )); } } diff --git a/core/src/decompile/resolve.rs b/core/src/decompile/resolve.rs index f2c1a73b..93b357fe 100644 --- a/core/src/decompile/resolve.rs +++ b/core/src/decompile/resolve.rs @@ -42,7 +42,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - continue; + continue } } else if !potential_types.contains(input) { matched = false; @@ -53,7 +53,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - break; + break } } None => { @@ -65,7 +65,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - break; + break } } } diff --git a/core/src/dump/util/threads/indexer.rs b/core/src/dump/util/threads/indexer.rs index a7c32d9c..3ca09085 100644 --- a/core/src/dump/util/threads/indexer.rs +++ b/core/src/dump/util/threads/indexer.rs @@ -80,7 +80,7 @@ pub async fn handle(addr_hash: H160) { Diff::Changed(changed) => &changed.to, Diff::Died(_) => { state.storage.remove(slot); - continue; + continue } _ => continue, }; diff --git a/core/src/dump/util/threads/tui.rs b/core/src/dump/util/threads/tui.rs index f97edf54..47cb86d9 100644 --- a/core/src/dump/util/threads/tui.rs +++ b/core/src/dump/util/threads/tui.rs @@ -18,7 +18,7 @@ use crate::dump::{ pub fn handle(args: &DumpArgs, output_dir: &str) { // if no TUI is requested, just run the dump if args.no_tui { - return; + return } // create new TUI terminal @@ -67,7 +67,7 @@ pub fn handle(args: &DumpArgs, output_dir: &str) { match command { ":q" | ":quit" => { state.view = TUIView::Killed; - break; + break } ":h" | ":help" => { state.view = TUIView::Help; @@ -127,7 +127,7 @@ pub fn handle(args: &DumpArgs, output_dir: &str) { } drop(state); - continue; + continue } match key.code { @@ -154,7 +154,7 @@ pub fn handle(args: &DumpArgs, output_dir: &str) { value.decode_as_type_index += 1; } } else if i >= scroll_index + selection_size { - break; + break } } } @@ -176,7 +176,7 @@ pub fn handle(args: &DumpArgs, output_dir: &str) { value.decode_as_type_index -= 1; } } else if i >= scroll_index + selection_size { - break; + break } } } diff --git a/core/src/inspect/core/contracts.rs b/core/src/inspect/core/contracts.rs index bf251b2a..c99fbc14 100644 --- a/core/src/inspect/core/contracts.rs +++ b/core/src/inspect/core/contracts.rs @@ -27,12 +27,12 @@ impl Contracts { // if skip resolving, just add the address if self.skip_resolving { self.contracts.insert(address, address.to_lower_hex()); - return Ok(()); + return Ok(()) } // if alias already exists, don't overwrite if self.contracts.contains_key(&address) { - return Ok(()); + return Ok(()) } if !self.transpose_api_key.is_empty() { @@ -54,7 +54,7 @@ impl Contracts { if self.skip_resolving { self.contracts .extend(addresses.into_iter().map(|address| (address, address.to_lower_hex()))); - return Ok(()); + return Ok(()) } // for each address, get the label diff --git a/core/src/inspect/core/tracing.rs b/core/src/inspect/core/tracing.rs index a61c3da4..a164ec6d 100644 --- a/core/src/inspect/core/tracing.rs +++ b/core/src/inspect/core/tracing.rs @@ -138,7 +138,7 @@ impl TryFrom> for DecodedTransactionTrace { if let Some(last_index) = trace_address.last() { current_trace.subtraces.insert(*last_index, decoded_trace); } else { - return Err(Error::DecodeError); // Trace address cannot be empty here + return Err(Error::DecodeError) // Trace address cannot be empty here } } diff --git a/core/src/snapshot/analyze.rs b/core/src/snapshot/analyze.rs index ab18cd53..2ece3594 100644 --- a/core/src/snapshot/analyze.rs +++ b/core/src/snapshot/analyze.rs @@ -159,7 +159,7 @@ pub fn snapshot_trace( ), ); snapshot.payable = false; - continue; + continue } // perform a series of checks to determine if the condition @@ -171,7 +171,7 @@ pub fn snapshot_trace( !conditional.contains("arg") && !conditional.contains("storage")) { - continue; + continue } snapshot.control_statements.insert(format!("if ({}) {{ .. }}", conditional)); @@ -318,7 +318,7 @@ pub fn snapshot_trace( if address.replacen("0x", "", 1).chars().all(|c| c == 'f' || c == '0') || (address.len() > 42 || address.len() < 32) { - continue; + continue } snapshot.addresses.insert(address); diff --git a/core/src/snapshot/resolve.rs b/core/src/snapshot/resolve.rs index 67a71a7d..3b248a02 100644 --- a/core/src/snapshot/resolve.rs +++ b/core/src/snapshot/resolve.rs @@ -55,7 +55,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - continue; + continue } } else if !potential_types.contains(input) { matched = false; @@ -66,7 +66,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - break; + break } } None => { @@ -78,7 +78,7 @@ pub fn match_parameters( &resolved_function.name, &resolved_function.inputs.join(",") ); - break; + break } } } diff --git a/core/src/snapshot/util/tui.rs b/core/src/snapshot/util/tui.rs index 5eea9516..06b31e69 100644 --- a/core/src/snapshot/util/tui.rs +++ b/core/src/snapshot/util/tui.rs @@ -86,7 +86,7 @@ pub fn handle( match command { ":q" | ":quit" => { state.view = TUIView::Killed; - break; + break } ":h" | ":help" => { state.view = TUIView::Help; @@ -106,7 +106,7 @@ pub fn handle( } drop(state); - continue; + continue } match key.code { diff --git a/core/tests/test_decompile.rs b/core/tests/test_decompile.rs index 7a9a680b..3418f6fe 100644 --- a/core/tests/test_decompile.rs +++ b/core/tests/test_decompile.rs @@ -300,6 +300,7 @@ mod integration_tests { "0x940259178FbF021e625510919BC2FF0B944E5613", "0xff612db0583be8d5498731e4e32bc12e08fa6292", "0xd5FEa30Ed719693Ec8848Dc7501b582F5de6a5BB", + "0x4C727a07246A70862e45B2E58fcd82c0eD5Eda85", ]; // define flag checks diff --git a/core/tests/test_inspect.rs b/core/tests/test_inspect.rs index 44bb8279..3969dc92 100644 --- a/core/tests/test_inspect.rs +++ b/core/tests/test_inspect.rs @@ -91,7 +91,7 @@ mod integration_tests { let start_time = Instant::now(); loop { if *finished.lock().unwrap() { - break; // Exit loop if processing is finished + break // Exit loop if processing is finished } if start_time.elapsed() > Duration::from_secs(60) {