-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #86 from Jon-Becker/perf/decompile
✨ feat: `PUSH0` support, events & errors in .sol header
- Loading branch information
Showing
28 changed files
with
438 additions
and
553 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// returns the compiler version used to compile the contract. | ||
// for example: (solc, 0.8.10) or (vyper, 0.2.16) | ||
pub fn detect_compiler(bytecode: String) -> (String, String) { | ||
|
||
let mut compiler = "unknown".to_string(); | ||
let mut version = "unknown".to_string(); | ||
|
||
// perfom prefix check for rough version matching | ||
if bytecode.starts_with("363d3d373d3d3d363d73") { | ||
compiler = "proxy".to_string(); | ||
version = "minimal".to_string(); | ||
} | ||
else if bytecode.starts_with("366000600037611000600036600073") { | ||
compiler = "proxy".to_string(); | ||
version = "vyper".to_string(); | ||
} | ||
else if bytecode.starts_with("6004361015") { | ||
compiler = "vyper".to_string(); | ||
version = "0.2.0-0.2.4,0.2.11-0.3.3".to_string(); | ||
} | ||
else if bytecode.starts_with("341561000a") { | ||
compiler = "vyper".to_string(); | ||
version = "0.2.5-0.2.8".to_string(); | ||
} | ||
else if bytecode.starts_with("731bf797") { | ||
compiler = "solc".to_string(); | ||
version = "0.4.10-0.4.24".to_string(); | ||
} | ||
else if bytecode.starts_with("6080604052") { | ||
compiler = "solc".to_string(); | ||
version = "0.4.22+".to_string(); | ||
} | ||
else if bytecode.starts_with("6060604052") { | ||
compiler = "solc".to_string(); | ||
version = "0.4.11-0.4.21".to_string(); | ||
} | ||
else if bytecode.contains("7679706572") { | ||
compiler = "vyper".to_string(); | ||
} | ||
else if bytecode.contains("736f6c63") { | ||
compiler = "solc".to_string(); | ||
} | ||
|
||
// perform metadata check | ||
if compiler == "solc" { | ||
let compiler_version = bytecode.split("736f6c6343").collect::<Vec<&str>>(); | ||
|
||
if compiler_version.len() > 1 { | ||
if let Some(encoded_version) = compiler_version[1].get(0..6) { | ||
let version_array = encoded_version.chars() | ||
.collect::<Vec<char>>() | ||
.chunks(2) | ||
.map(|c| c.iter().collect::<String>()) | ||
.collect::<Vec<String>>(); | ||
|
||
version = String::new(); | ||
for version_part in version_array { | ||
version.push_str(&format!("{}.", u8::from_str_radix(&version_part, 16).unwrap())); | ||
} | ||
} | ||
} | ||
} | ||
else if compiler == "vyper" { | ||
let compiler_version = bytecode.split("767970657283").collect::<Vec<&str>>(); | ||
|
||
if compiler_version.len() > 1 { | ||
if let Some(encoded_version) = compiler_version[1].get(0..6) { | ||
let version_array = encoded_version.chars() | ||
.collect::<Vec<char>>() | ||
.chunks(2) | ||
.map(|c| c.iter().collect::<String>()) | ||
.collect::<Vec<String>>(); | ||
|
||
version = String::new(); | ||
for version_part in version_array { | ||
version.push_str(&format!("{}.", u8::from_str_radix(&version_part, 16).unwrap())); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
(compiler, version.trim_end_matches('.').to_string()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
pub mod signatures; | ||
pub mod selectors; | ||
pub mod solidity; | ||
pub mod compiler; | ||
pub mod evm; | ||
pub mod yul; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use std::{collections::HashMap, sync::{Arc, Mutex}, time::Duration, thread}; | ||
|
||
use indicatif::ProgressBar; | ||
|
||
use crate::io::logging::Logger; | ||
|
||
use super::{evm::vm::VM, signatures::{resolve_function_signature, ResolvedFunction}}; | ||
|
||
// find all function selectors in the given EVM assembly. | ||
pub fn find_function_selectors(assembly: String) -> Vec<String> { | ||
let mut function_selectors = Vec::new(); | ||
|
||
// search through assembly for PUSH4 instructions, optimistically assuming that they are function selectors | ||
let assembly: Vec<String> = assembly | ||
.split('\n') | ||
.map(|line| line.trim().to_string()) | ||
.collect(); | ||
for line in assembly.iter() { | ||
let instruction_args: Vec<String> = line.split(' ').map(|arg| arg.to_string()).collect(); | ||
|
||
if instruction_args.len() >= 2 { | ||
let instruction = instruction_args[1].clone(); | ||
|
||
if instruction == "PUSH4" { | ||
let function_selector = instruction_args[2].clone(); | ||
function_selectors.push(function_selector); | ||
} | ||
} | ||
} | ||
function_selectors.sort(); | ||
function_selectors.dedup(); | ||
function_selectors | ||
} | ||
|
||
// resolve a selector's function entry point from the EVM bytecode | ||
pub fn resolve_entry_point(evm: &VM, selector: String) -> u128 { | ||
let mut vm = evm.clone(); | ||
|
||
// execute the EVM call to find the entry point for the given selector | ||
vm.calldata = selector.clone(); | ||
while vm.bytecode.len() >= (vm.instruction * 2 + 2) as usize { | ||
let call = vm.step(); | ||
|
||
// if the opcode is an JUMPI and it matched the selector, the next jumpi is the entry point | ||
if call.last_instruction.opcode == "57" { | ||
let jump_condition = call.last_instruction.input_operations[1].solidify(); | ||
let jump_taken = call.last_instruction.inputs[1].try_into().unwrap_or(1); | ||
|
||
if jump_condition.contains(&selector) && | ||
jump_condition.contains("msg.data[0]") && | ||
jump_condition.contains(" == ") && | ||
jump_taken == 1 | ||
{ | ||
return call.last_instruction.inputs[0].try_into().unwrap_or(0) | ||
} | ||
} | ||
|
||
if vm.exitcode != 255 || !vm.returndata.is_empty() { | ||
break; | ||
} | ||
} | ||
|
||
0 | ||
} | ||
|
||
// resolve a function signature from the given selectors | ||
pub fn resolve_function_selectors( | ||
selectors: Vec<String>, | ||
logger: &Logger, | ||
) -> HashMap<String, Vec<ResolvedFunction>> { | ||
let resolved_functions: Arc<Mutex<HashMap<String, Vec<ResolvedFunction>>>> = Arc::new(Mutex::new(HashMap::new())); | ||
let resolve_progress: Arc<Mutex<ProgressBar>> = Arc::new(Mutex::new(ProgressBar::new_spinner())); | ||
|
||
let mut threads = Vec::new(); | ||
|
||
resolve_progress.lock().unwrap().enable_steady_tick(Duration::from_millis(100)); | ||
resolve_progress.lock().unwrap().set_style(logger.info_spinner()); | ||
|
||
for selector in selectors { | ||
let function_clone = resolved_functions.clone(); | ||
let resolve_progress = resolve_progress.clone(); | ||
|
||
// create a new thread for each selector | ||
threads.push(thread::spawn(move || { | ||
if let Some(function) = resolve_function_signature(&selector) { | ||
let mut _resolved_functions = function_clone.lock().unwrap(); | ||
let mut _resolve_progress = resolve_progress.lock().unwrap(); | ||
_resolve_progress.set_message(format!("resolved {} selectors...", _resolved_functions.len())); | ||
_resolved_functions.insert(selector, function); | ||
} | ||
})); | ||
|
||
} | ||
|
||
// wait for all threads to finish | ||
for thread in threads { | ||
thread.join().unwrap(); | ||
} | ||
|
||
resolve_progress.lock().unwrap().finish_and_clear(); | ||
|
||
let x = resolved_functions.lock().unwrap().clone(); | ||
x | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.