diff --git a/.github/workflows/todo.yml b/.github/workflows/todo.yml new file mode 100644 index 00000000..7dfcdbc8 --- /dev/null +++ b/.github/workflows/todo.yml @@ -0,0 +1,12 @@ +name: todo +on: ["push"] +jobs: + build: + runs-on: "ubuntu-latest" + steps: + - uses: "actions/checkout@v3" + - name: "TODO to Issue" + uses: "alstr/todo-to-issue-action@v4" + with: + AUTO_ASSIGN: true + IDENTIFIERS: '[{"name": "TODO", "labels": ["T-todo"]}, {"name": "FIX", "labels": ["T-bug"]}]' diff --git a/bifrost/README.md b/bifrost/README.md index d167aac9..62fad13c 100644 --- a/bifrost/README.md +++ b/bifrost/README.md @@ -1,3 +1,47 @@ -# Bifrost +# `bifrost` -The version manager for Heimdall +![image](https://github.com/Jon-Becker/heimdall-rs/assets/64037729/4f236ff0-7417-4e8d-8a09-6cb6da9325da) + +Bifrost is heimdall's installer and version manager. Named after the rainbow bridge in Norse mythology, `bifrost` is the bridge between heimdall and your system. + +## Installation +```bash +curl -L http://get.heimdall.rs | bash +``` + +## Usage + +To install the latest stable release: +```bash +bifrost +``` + +To install the lastest stable release (pre-compiled): +```bash +bifrost --binary +``` + +To install a specific branch: +```bash +bifrost --version +``` + +To install a specific tag: +```bash +bifrost --version +``` + +To install a specific tag (pre-compiled): +```bash +bifrost --version --binary +``` + +To list all available versions: +```bash +bifrost --list +``` + +To update bifrost to the latest version: +```bash +bifrost --update +``` diff --git a/cache/README.md b/cache/README.md new file mode 100644 index 00000000..acc27c65 --- /dev/null +++ b/cache/README.md @@ -0,0 +1,3 @@ +# heimdall-cache + +This crate is a simple on-disc caching system utilized by the Heimdall library. Heimdall modules may use this library to save immutable on-chain data to disc, such as bytecode, calldata, as well as the results of expensive computations. diff --git a/cache/src/lib.rs b/cache/src/lib.rs index 0e741c44..226e392f 100644 --- a/cache/src/lib.rs +++ b/cache/src/lib.rs @@ -7,6 +7,7 @@ use util::*; pub mod util; +/// Clap argument parser for the cache subcommand #[derive(Debug, Clone, Parser)] #[clap( about = "Manage heimdall-rs' cached objects", @@ -23,7 +24,7 @@ pub struct CacheArgs { #[derive(Debug, Clone, Parser)] pub struct NoArguments {} -/// Clap subcommand parser for the cache subcommand +/// Clap subcommand parser for cache subcommands #[derive(Debug, Clone, Parser)] #[clap( about = "Manage heimdall-rs' cached objects", @@ -41,12 +42,31 @@ pub enum Subcommands { Size(NoArguments), } +/// A simple cache object that stores a value and an expiry time \ +/// The expiry time is a unix timestamp #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Cache { pub value: T, pub expiry: u64, } +/// Clear the cache, removing all objects +/// +/// ``` +/// use heimdall_cache::{clear_cache, store_cache, keys}; +/// +/// /// add a value to the cache +/// store_cache("clear_cache_key", "value", None); +/// +/// /// assert that the cache contains the key +/// assert!(keys("*").contains(&"clear_cache_key".to_string())); +/// +/// /// clear the cache +/// clear_cache(); +/// +/// /// assert that the cache no longer contains the key +/// assert!(!keys("*").contains(&"clear_cache_key".to_string())); +/// ``` #[allow(deprecated)] pub fn clear_cache() { let home = home_dir().unwrap(); @@ -59,6 +79,20 @@ pub fn clear_cache() { } } +/// Check if a cached object exists +/// +/// ``` +/// use heimdall_cache::{store_cache, exists}; +/// +/// /// add a value to the cache +/// store_cache("exists_key", "value", None); +/// +/// /// assert that the cache contains the key +/// assert!(exists("exists_key")); +/// +/// /// assert that the cache does not contain a non-existent key +/// assert!(!exists("non_existent_key")); +/// ``` #[allow(deprecated)] pub fn exists(key: &str) -> bool { let home = home_dir().unwrap(); @@ -68,6 +102,23 @@ pub fn exists(key: &str) -> bool { cache_file.exists() } +/// List all cached objects +/// +/// ``` +/// use heimdall_cache::{store_cache, keys}; +/// +/// /// add a value to the cache +/// store_cache("keys_key", "value", None); +/// +/// /// assert that the cache contains the key +/// assert!(keys("*").contains(&"keys_key".to_string())); +/// +/// /// assert that the cache does not contain a non-existent key +/// assert!(!keys("*").contains(&"non_existent_key".to_string())); +/// +/// /// assert that the cache contains the key +/// assert!(keys("keys_*").contains(&"keys_key".to_string())); +/// ``` #[allow(deprecated)] pub fn keys(pattern: &str) -> Vec { let home = home_dir().unwrap(); @@ -92,6 +143,22 @@ pub fn keys(pattern: &str) -> Vec { keys } +/// Delete a cached object +/// ``` +/// use heimdall_cache::{store_cache, delete_cache, keys}; +/// +/// /// add a value to the cache +/// store_cache("delete_cache_key", "value", None); +/// +/// /// assert that the cache contains the key +/// assert!(keys("*").contains(&"delete_cache_key".to_string())); +/// +/// /// delete the cached object +/// delete_cache("delete_cache_key"); +/// +/// /// assert that the cache does not contain the key +/// assert!(!keys("*").contains(&"delete_cache_key".to_string())); +/// ``` #[allow(deprecated)] pub fn delete_cache(key: &str) { let home = home_dir().unwrap(); @@ -103,42 +170,17 @@ pub fn delete_cache(key: &str) { } } -#[allow(deprecated)] -pub fn check_expiry() -> bool -where - T: DeserializeOwned, { - let home = home_dir().unwrap(); - let cache_dir = home.join(".bifrost").join("cache"); - - for entry in cache_dir.read_dir().unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - let binary_string = match read_file(path.to_str().unwrap()) { - Some(s) => s, - None => return false, - }; - - let binary_vec = decode_hex(&binary_string); - if binary_vec.is_err() { - return false - } - - let cache: Result, _> = bincode::deserialize(&binary_vec.unwrap()); - if cache.is_err() { - delete_path(path.to_str().unwrap()); - }; - - let cache = cache.unwrap(); - if cache.expiry < - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() - { - // delete file - delete_path(path.to_str().unwrap()); - } - } - true -} - +/// Read a cached object +/// +/// ``` +/// use heimdall_cache::{store_cache, read_cache}; +/// +/// /// add a value to the cache +/// store_cache("read_cache_key", "value", None); +/// +/// /// read the cached object +/// assert_eq!(read_cache::("read_cache_key").unwrap(), "value"); +/// ``` #[allow(deprecated)] pub fn read_cache(key: &str) -> Option where @@ -158,13 +200,38 @@ where return None } - let cache: Cache = match bincode::deserialize(&binary_vec.unwrap()) { - Ok(c) => c, + let cache: Cache = match bincode::deserialize::>(&binary_vec.unwrap()) { + Ok(c) => { + // check if the cache has expired, if so, delete it and return None + if c.expiry < + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() + { + delete_cache(key); + return None + } + + c + } Err(_) => return None, }; Some(*Box::new(cache.value)) } +/// Store a value in the cache, with an optional expiry time \ +/// If no expiry time is specified, the object will expire in 90 days +/// +/// ``` +/// use heimdall_cache::{store_cache, read_cache}; +/// +/// /// add a value to the cache with no expiry time (90 days) +/// store_cache("store_cache_key", "value", None); +/// +/// /// add a value to the cache with an expiry time of 1 day +/// store_cache("store_cache_key2", "value", Some(60 * 60 * 24)); +/// ``` #[allow(deprecated)] pub fn store_cache(key: &str, value: T, expiry: Option) where @@ -179,12 +246,13 @@ where 60 * 60 * 24 * 90, ); - let cache = Cache { value: value, expiry: expiry }; + let cache = Cache { value, expiry }; let encoded: Vec = bincode::serialize(&cache).unwrap(); let binary_string = encode_hex(encoded); write_file(cache_file.to_str().unwrap(), &binary_string); } +/// Cache subcommand handler #[allow(deprecated)] pub fn cache(args: CacheArgs) -> Result<(), Box> { match args.sub { diff --git a/cache/src/util.rs b/cache/src/util.rs index c1130e9d..138a8dda 100644 --- a/cache/src/util.rs +++ b/cache/src/util.rs @@ -6,12 +6,28 @@ use std::{ process::Command, }; -// decode a hex into an array of integer values +/// Decode a hex string into a bytearray +/// +/// ``` +/// use heimdall_cache::util::decode_hex; +/// +/// let hex = "48656c6c6f20576f726c64"; // "Hello World" in hex +/// let result = decode_hex(hex); +/// assert_eq!(result, Ok(vec![72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])); +/// ``` pub fn decode_hex(s: &str) -> Result, ParseIntError> { (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16)).collect() } -// encode a hex into a string +/// Encode a bytearray into a hex string +/// +/// ``` +/// use heimdall_cache::util::encode_hex; +/// +/// let bytes = vec![72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]; +/// let result = encode_hex(bytes); +/// assert_eq!(result, "48656c6c6f20576f726c64"); +/// ``` pub fn encode_hex(s: Vec) -> String { s.iter().fold(String::new(), |mut acc, b| { write!(acc, "{b:02x}", b = b).unwrap(); @@ -19,6 +35,20 @@ pub fn encode_hex(s: Vec) -> String { }) } +/// Prettify bytes into a human-readable format \ +/// e.g. 1024 -> 1 KB +/// +/// ``` +/// use heimdall_cache::util::prettify_bytes; +/// +/// let bytes = 500; +/// let result = prettify_bytes(bytes); +/// assert_eq!(result, "500 B"); +/// +/// let bytes = 500_000; +/// let result = prettify_bytes(bytes); +/// assert_eq!(result, "488 KB"); +/// ``` pub fn prettify_bytes(bytes: u64) -> String { if bytes < 1024 { format!("{bytes} B") @@ -34,6 +64,15 @@ pub fn prettify_bytes(bytes: u64) -> String { } } +/// Write contents to a file on the disc +/// +/// ```no_run +/// use heimdall_cache::util::write_file; +/// +/// let path = "/tmp/test.txt"; +/// let contents = "Hello, World!"; +/// let result = write_file(path, contents); +/// ``` pub fn write_file(_path: &str, contents: &str) -> Option { let path = std::path::Path::new(_path); let prefix = path.parent().unwrap(); @@ -54,6 +93,15 @@ pub fn write_file(_path: &str, contents: &str) -> Option { Some(_path.to_string()) } +/// Read contents from a file on the disc +/// +/// ```no_run +/// use heimdall_cache::util::read_file; +/// +/// let path = "/tmp/test.txt"; +/// let contents = read_file(path); +/// assert!(contents.is_some()); +/// ``` pub fn read_file(_path: &str) -> Option { let path = std::path::Path::new(_path); let mut file = match File::open(path) { @@ -68,6 +116,14 @@ pub fn read_file(_path: &str) -> Option { Some(contents) } +/// Delete a file or directory on the disc +/// +/// ```no_run +/// use heimdall_cache::util::delete_path; +/// +/// let path = "/tmp/test.txt"; +/// let result = delete_path(path); +/// ``` pub fn delete_path(_path: &str) -> bool { let path = match std::path::Path::new(_path).to_str() { Some(path) => path, diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 00000000..f2f48580 --- /dev/null +++ b/cli/README.md @@ -0,0 +1,3 @@ +# heimdall-cli + +This crate is a very simple clap-based CLI that allows you to interact with the Heimdall library by wrapping [heimdall-core](../core/README.md) modules. diff --git a/cli/src/main.rs b/cli/src/main.rs index 9efa96e6..9baf3149 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -12,11 +12,13 @@ use crossterm::{ use heimdall_cache::{cache, CacheArgs}; use heimdall_common::{ constants::ADDRESS_REGEX, - io::{ - file::{write_file, write_lines_to_file}, - logging::Logger, + utils::{ + io::{ + file::{write_file, write_lines_to_file}, + logging::Logger, + }, + version::{current_version, remote_version}, }, - utils::version::{current_version, remote_version}, }; use heimdall_config::{config, get_config, ConfigArgs}; use heimdall_core::{ diff --git a/common/README.md b/common/README.md new file mode 100644 index 00000000..abaa851a --- /dev/null +++ b/common/README.md @@ -0,0 +1,19 @@ +# heimdall-common + +This crate is a collection of common utilities used by the Heimdall library. It is not intended to be used directly, but rather as a dependency of other Heimdall crates. + +## Crate Structure + +``` +src +├── ether +│ ├── evm # symbolic evm implementation +│ │ ├── core # core evm +│ │ └── ext # evm extensions +│ │ └── exec +│ └── lexers # lexers for parsing the evm +├── resources # resources used by the library +└── utils + ├── io # io utilities + └── testing # testing utilities +``` diff --git a/common/src/constants.rs b/common/src/constants.rs index cc34c863..3cc66cbb 100644 --- a/common/src/constants.rs +++ b/common/src/constants.rs @@ -3,32 +3,32 @@ use lazy_static::lazy_static; lazy_static! { - // The following regex is used to validate Ethereum addresses. + /// The following regex is used to validate Ethereum addresses. pub static ref ADDRESS_REGEX: Regex = Regex::new(r"^(0x)?[0-9a-fA-F]{40}$").unwrap(); - // The following regex is used to validate Ethereum transaction hashes. + /// The following regex is used to validate Ethereum transaction hashes. pub static ref TRANSACTION_HASH_REGEX: Regex = Regex::new(r"^(0x)?[0-9a-fA-F]{64}$").unwrap(); - // The following regex is used to validate raw bytecode files as targets. - // It also restricts the file to a maximum of ~24kb, the maximum size of a - // contract on Ethereum. + /// The following regex is used to validate raw bytecode files as targets. + /// It also restricts the file to a maximum of ~24kb, the maximum size of a + /// contract on Ethereum. pub static ref BYTECODE_REGEX: Regex = Regex::new(r"^(0x)?[0-9a-fA-F]{0,50000}$").unwrap(); - // The following regex is used to reduce null byte prefixes + /// The following regex is used to reduce null byte prefixes pub static ref REDUCE_HEX_REGEX: Regex = Regex::new(r"^0x(00)*").unwrap(); - // The following regex is used as a search pattern for words + /// The following regex is used as a search pattern for words pub static ref WORD_REGEX: Regex = Regex::new(r"0x[0-9a-fA-F]{0,64}").unwrap(); - // The following regex is used to find type castings + /// The following regex is used to find type castings pub static ref TYPE_CAST_REGEX: Regex = Regex::new(r"(address\(|string\(|bool\(|bytes(\d*)\(|uint(\d*)\(|int(\d*)\()(?!\))").unwrap(); - // The following regex is used to find memory length accesses + /// The following regex is used to find memory length accesses pub static ref MEMLEN_REGEX: Regex = Regex::new(r"memory\[memory\[[0-9x]*\]\]").unwrap(); - // The following regex is used to find memory accesses + /// The following regex is used to find memory accesses pub static ref MEMORY_REGEX: Regex = Regex::new(r"memory\[\(?[0-9x]*\]").unwrap(); - // The following regex is used to find storage accesses + /// The following regex is used to find storage accesses pub static ref STORAGE_REGEX: Regex = Regex::new(r"storage\[\(?[0-9x]*\]").unwrap(); } diff --git a/common/src/ether/compiler.rs b/common/src/ether/compiler.rs index 7e01b477..36aa91ae 100644 --- a/common/src/ether/compiler.rs +++ b/common/src/ether/compiler.rs @@ -1,4 +1,4 @@ -use crate::io::logging::Logger; +use crate::utils::io::logging::Logger; // returns the compiler version used to compile the contract. // for example: (solc, 0.8.10) or (vyper, 0.2.16) @@ -99,3 +99,85 @@ pub fn detect_compiler(bytecode: &str) -> (&'static str, String) { (compiler, version.trim_end_matches('.').to_string()) } + +#[cfg(test)] +mod test_compiler { + use crate::ether::compiler::detect_compiler; + + #[test] + fn test_detect_compiler_proxy_minimal() { + let bytecode = "363d3d373d3d3d363d73"; + let expected_result = ("proxy", "minimal".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_proxy_vyper() { + let bytecode = "366000600037611000600036600073"; + let expected_result = ("proxy", "vyper".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_vyper_range_1() { + let bytecode = "6004361015"; + let expected_result = ("vyper", "0.2.0-0.2.4,0.2.11-0.3.3".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_vyper_range_2() { + let bytecode = "341561000a"; + let expected_result = ("vyper", "0.2.5-0.2.8".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_solc_range_1() { + let bytecode = "731bf797"; + let expected_result = ("solc", "0.4.10-0.4.24".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_solc_range_2() { + let bytecode = "6080604052"; + let expected_result = ("solc", "0.4.22+".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_solc_range_3() { + let bytecode = "6060604052"; + let expected_result = ("solc", "0.4.11-0.4.21".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_vyper() { + let bytecode = r#"7679706572"#; + let expected_result = ("vyper", "unknown".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_solc() { + let bytecode = "736f6c63"; + let expected_result = ("solc", "unknown".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_solc_metadata() { + let bytecode = "736f6c63434d4e"; + let expected_result = ("solc", "unknown".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } + + #[test] + fn test_detect_compiler_vyper_metadata() { + let bytecode = "7679706572833135353030"; + let expected_result = ("vyper", "49.53.53".to_string()); + assert_eq!(detect_compiler(bytecode), expected_result); + } +} diff --git a/common/src/ether/evm/core/log.rs b/common/src/ether/evm/core/log.rs index 23c33248..7c0d2197 100644 --- a/common/src/ether/evm/core/log.rs +++ b/common/src/ether/evm/core/log.rs @@ -1,5 +1,6 @@ use ethers::prelude::U256; +/// The [`Log`] struct represents a log emitted by a `LOG0-LOG4` opcode. #[derive(Clone, Debug)] pub struct Log { pub index: u128, @@ -8,9 +9,8 @@ pub struct Log { } impl Log { - // Implements a new log with the given index and "emits" - // the log at the given index. + /// Creates a new [`Log`] with the given log index, topics, and hex data. pub fn new(index: u128, topics: Vec, data: &[u8]) -> Log { - Log { index: index, topics: topics, data: data.to_vec() } + Log { index, topics, data: data.to_vec() } } } diff --git a/common/src/ether/evm/core/memory.rs b/common/src/ether/evm/core/memory.rs index 556d6209..25718a0a 100644 --- a/common/src/ether/evm/core/memory.rs +++ b/common/src/ether/evm/core/memory.rs @@ -1,20 +1,40 @@ +/// The [`Memory`] struct represents the memory of an EVM. #[derive(Clone, Debug)] pub struct Memory { pub memory: Vec, + // TODO: add bit-tracking for memory } impl Memory { - // Repr as a [u8] + /// Creates a new [`Memory`] with an empty memory vector. pub fn new() -> Memory { Memory { memory: Vec::new() } } - // get the size of the memory in bytes + /// Gets the current size of the memory in bytes. + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let memory = Memory::new(); + /// assert_eq!(memory.size(), 0); + /// ``` pub fn size(&self) -> u128 { self.memory.len() as u128 } - // extend the memory to a given size + /// Extends the memory to the given size, if necessary. \ + /// This is called when a memory store is performed, and the memory must be extended to fit the + /// value. + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let mut memory = Memory::new(); + /// assert_eq!(memory.size(), 0); + /// memory.extend(0, 32); + /// assert_eq!(memory.size(), 32); + /// ``` pub fn extend(&mut self, offset: u128, size: u128) { // Calculate the new size of the memory let new_mem_size = (offset + size + 31) / 32 * 32; @@ -26,7 +46,16 @@ impl Memory { } } - // stores a bytearray in the memory at offset + /// Store the given bytes in the memory at the given offset, with a fixed size. + /// May extend the memory if necessary. + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let mut memory = Memory::new(); + /// memory.store(0, 32, &[0xff]); + /// assert_eq!(memory.read(0, 32), vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff]); + /// ``` pub fn store(&mut self, mut offset: usize, mut size: usize, value: &[u8]) { // Cap offset and size to 2**16 offset = offset.min(65536); @@ -54,7 +83,17 @@ impl Memory { self.memory.splice(offset..offset + size, value); } - // read a value from the memory at the given offset, with a fixed size + /// Read the given number of bytes from the memory at the given offset. + /// If the offset + size is greater than the current size of the memory, null bytes will be + /// appended to the value. + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let mut memory = Memory::new(); + /// memory.store(0, 32, &[0xff]); + /// assert_eq!(memory.read(0, 32), vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff]); + /// ``` pub fn read(&self, offset: usize, size: usize) -> Vec { // Cap size to 2**16 and offset to 2**16 for optimization let size = size.min(65536); @@ -75,14 +114,31 @@ impl Memory { } } - // calculate the current memory cost + /// Calculate the current memory cost + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let mut memory = Memory::new(); + /// memory.store(0, 32, &[0xff]); + /// assert_eq!(memory.memory_cost(), 3); + /// ``` pub fn memory_cost(&self) -> u128 { // Calculate the new size of the memory let memory_word_size = (self.size() + 31) / 32; (memory_word_size.pow(2)) / 512 + (3 * memory_word_size) } - // calculate the memory cost of extending the memory to a given size + /// calculate the memory cost of extending the memory to a given size + /// + /// ``` + /// use heimdall_common::ether::evm::core::memory::Memory; + /// + /// let mut memory = Memory::new(); + /// memory.store(0, 32, &[0xff]); + /// assert_eq!(memory.expansion_cost(0, 32), 0); + /// assert_eq!(memory.expansion_cost(0, 64), 3); + /// ``` pub fn expansion_cost(&self, offset: usize, size: usize) -> u128 { // Calculate the new size of the memory let new_memory_word_size = ((offset + size + 31) / 32) as u128; @@ -94,3 +150,154 @@ impl Memory { } } } + +#[cfg(test)] +mod tests { + use crate::{ether::evm::core::memory::Memory, utils::strings::decode_hex}; + + #[test] + fn test_mstore_simple() { + let mut memory = Memory::new(); + memory.store( + 0, + 32, + &decode_hex("00000000000000000000000000000000000000000000000000000000000000ff") + .unwrap(), + ); + assert_eq!( + memory.memory, + decode_hex("00000000000000000000000000000000000000000000000000000000000000ff").unwrap() + ); + } + + #[test] + fn test_mstore_extend() { + let mut memory = Memory::new(); + memory.store(0, 32, &[0xff]); + assert_eq!( + memory.memory, + decode_hex("00000000000000000000000000000000000000000000000000000000000000ff").unwrap() + ); + } + + #[test] + fn test_mstore_offset() { + let mut memory = Memory::new(); + memory.store(4, 32, &[0xff]); + assert_eq!( + memory.memory, + decode_hex("0000000000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000").unwrap() + ); + } + + #[test] + fn test_mstore_large_nonstandard_offset() { + let mut memory = Memory::new(); + memory.store(34, 32, &[0xff]); + assert_eq!( + memory.memory, + decode_hex("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff000000000000000000000000000000000000000000000000000000000000").unwrap() + ); + } + + #[test] + fn test_mstore8() { + let mut memory = Memory::new(); + memory.store(0, 1, &[0xff]); + assert_eq!( + memory.memory, + decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() + ); + } + + #[test] + fn test_mstore_large_offser() { + let mut memory = Memory::new(); + memory.store(255, 32, &[0xff]); + assert_eq!( + memory.memory, + decode_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff00").unwrap() + ); + } + + #[test] + fn test_mload_simple() { + let mut memory = Memory::new(); + memory.store( + 0, + 32, + &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") + .unwrap(), + ); + assert_eq!( + memory.read(0, 32), + decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff").unwrap() + ); + } + + #[test] + fn test_mload_pad_one() { + let mut memory = Memory::new(); + memory.store( + 0, + 32, + &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") + .unwrap(), + ); + assert_eq!( + memory.read(1, 32), + decode_hex("223344556677889900aabbccddeeff11223344556677889900aabbccddeeff00").unwrap() + ); + } + + #[test] + fn test_mload_pad_large() { + let mut memory = Memory::new(); + memory.store( + 0, + 32, + &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") + .unwrap(), + ); + assert_eq!( + memory.read(31, 32), + decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() + ); + } + + #[test] + fn test_memory_cost() { + let mut memory = Memory::new(); + memory.store( + 0, + 32, + &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") + .unwrap(), + ); + assert_eq!(memory.memory_cost(), 3); + } + + #[test] + fn test_memory_cost_2() { + let mut memory = Memory::new(); + memory.store( + 32 * 32, + 32, + &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") + .unwrap(), + ); + assert_eq!(memory.memory_cost(), 101); + } + + #[test] + fn test_expansion_cost() { + let memory = Memory::new(); + assert_eq!(memory.expansion_cost(0, 32), 3); + } + + #[test] + fn test_expansion_cost_2() { + let memory = Memory::new(); + assert_eq!(memory.expansion_cost(32 * 32, 32), 101); + } +} diff --git a/common/src/ether/evm/core/mod.rs b/common/src/ether/evm/core/mod.rs index 562f5915..b1718d95 100644 --- a/common/src/ether/evm/core/mod.rs +++ b/common/src/ether/evm/core/mod.rs @@ -3,6 +3,5 @@ pub mod memory; pub mod opcodes; pub mod stack; pub mod storage; -mod tests; pub mod types; pub mod vm; diff --git a/common/src/ether/evm/core/opcodes.rs b/common/src/ether/evm/core/opcodes.rs index a24580cc..f6351308 100644 --- a/common/src/ether/evm/core/opcodes.rs +++ b/common/src/ether/evm/core/opcodes.rs @@ -1,6 +1,7 @@ use ethers::types::U256; use std::fmt::{Display, Formatter, Result}; +/// An [`Opcode`] represents an Ethereum Virtual Machine (EVM) opcode. \ #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Opcode { pub code: u8, @@ -10,165 +11,175 @@ pub struct Opcode { pub outputs: u16, } -// Returns the opcode for the given hexcode, fetched from the hashmap. -pub fn opcode(code: u8) -> Opcode { - match code { - 0x00 => Opcode { code: code, name: "STOP", mingas: 0, inputs: 0, outputs: 0 }, - 0x01 => Opcode { code: code, name: "ADD", mingas: 3, inputs: 2, outputs: 1 }, - 0x02 => Opcode { code: code, name: "MUL", mingas: 5, inputs: 2, outputs: 1 }, - 0x03 => Opcode { code: code, name: "SUB", mingas: 3, inputs: 2, outputs: 1 }, - 0x04 => Opcode { code: code, name: "DIV", mingas: 5, inputs: 2, outputs: 1 }, - 0x05 => Opcode { code: code, name: "SDIV", mingas: 5, inputs: 2, outputs: 1 }, - 0x06 => Opcode { code: code, name: "MOD", mingas: 5, inputs: 2, outputs: 1 }, - 0x07 => Opcode { code: code, name: "SMOD", mingas: 5, inputs: 2, outputs: 1 }, - 0x08 => Opcode { code: code, name: "ADDMOD", mingas: 8, inputs: 3, outputs: 1 }, - 0x09 => Opcode { code: code, name: "MULMOD", mingas: 8, inputs: 3, outputs: 1 }, - 0x0a => Opcode { code: code, name: "EXP", mingas: 10, inputs: 2, outputs: 1 }, - 0x0b => Opcode { code: code, name: "SIGNEXTEND", mingas: 5, inputs: 2, outputs: 1 }, - 0x10 => Opcode { code: code, name: "LT", mingas: 3, inputs: 2, outputs: 1 }, - 0x11 => Opcode { code: code, name: "GT", mingas: 3, inputs: 2, outputs: 1 }, - 0x12 => Opcode { code: code, name: "SLT", mingas: 3, inputs: 2, outputs: 1 }, - 0x13 => Opcode { code: code, name: "SGT", mingas: 3, inputs: 2, outputs: 1 }, - 0x14 => Opcode { code: code, name: "EQ", mingas: 3, inputs: 2, outputs: 1 }, - 0x15 => Opcode { code: code, name: "ISZERO", mingas: 3, inputs: 1, outputs: 1 }, - 0x16 => Opcode { code: code, name: "AND", mingas: 3, inputs: 2, outputs: 1 }, - 0x17 => Opcode { code: code, name: "OR", mingas: 3, inputs: 2, outputs: 1 }, - 0x18 => Opcode { code: code, name: "XOR", mingas: 3, inputs: 2, outputs: 1 }, - 0x19 => Opcode { code: code, name: "NOT", mingas: 3, inputs: 1, outputs: 1 }, - 0x1a => Opcode { code: code, name: "BYTE", mingas: 3, inputs: 2, outputs: 1 }, - 0x1b => Opcode { code: code, name: "SHL", mingas: 3, inputs: 2, outputs: 1 }, - 0x1c => Opcode { code: code, name: "SHR", mingas: 3, inputs: 2, outputs: 1 }, - 0x1d => Opcode { code: code, name: "SAR", mingas: 3, inputs: 2, outputs: 1 }, - 0x20 => Opcode { code: code, name: "SHA3", mingas: 30, inputs: 2, outputs: 1 }, - 0x30 => Opcode { code: code, name: "ADDRESS", mingas: 2, inputs: 0, outputs: 1 }, - 0x31 => Opcode { code: code, name: "BALANCE", mingas: 100, inputs: 1, outputs: 1 }, - 0x32 => Opcode { code: code, name: "ORIGIN", mingas: 2, inputs: 0, outputs: 1 }, - 0x33 => Opcode { code: code, name: "CALLER", mingas: 2, inputs: 0, outputs: 1 }, - 0x34 => Opcode { code: code, name: "CALLVALUE", mingas: 2, inputs: 0, outputs: 1 }, - 0x35 => Opcode { code: code, name: "CALLDATALOAD", mingas: 3, inputs: 1, outputs: 1 }, - 0x36 => Opcode { code: code, name: "CALLDATASIZE", mingas: 2, inputs: 0, outputs: 1 }, - 0x37 => Opcode { code: code, name: "CALLDATACOPY", mingas: 3, inputs: 3, outputs: 0 }, - 0x38 => Opcode { code: code, name: "CODESIZE", mingas: 2, inputs: 0, outputs: 1 }, - 0x39 => Opcode { code: code, name: "CODECOPY", mingas: 3, inputs: 3, outputs: 0 }, - 0x3a => Opcode { code: code, name: "GASPRICE", mingas: 2, inputs: 0, outputs: 1 }, - 0x3b => Opcode { code: code, name: "EXTCODESIZE", mingas: 100, inputs: 1, outputs: 1 }, - 0x3c => Opcode { code: code, name: "EXTCODECOPY", mingas: 100, inputs: 4, outputs: 0 }, - 0x3d => Opcode { code: code, name: "RETURNDATASIZE", mingas: 2, inputs: 0, outputs: 1 }, - 0x3e => Opcode { code: code, name: "RETURNDATACOPY", mingas: 3, inputs: 3, outputs: 0 }, - 0x3f => Opcode { code: code, name: "EXTCODEHASH", mingas: 100, inputs: 1, outputs: 1 }, - 0x40 => Opcode { code: code, name: "BLOCKHASH", mingas: 20, inputs: 1, outputs: 1 }, - 0x41 => Opcode { code: code, name: "COINBASE", mingas: 2, inputs: 0, outputs: 1 }, - 0x42 => Opcode { code: code, name: "TIMESTAMP", mingas: 2, inputs: 0, outputs: 1 }, - 0x43 => Opcode { code: code, name: "NUMBER", mingas: 2, inputs: 0, outputs: 1 }, - 0x44 => Opcode { code: code, name: "DIFFICULTY", mingas: 2, inputs: 0, outputs: 1 }, - 0x45 => Opcode { code: code, name: "GASLIMIT", mingas: 2, inputs: 0, outputs: 1 }, - 0x46 => Opcode { code: code, name: "CHAINID", mingas: 2, inputs: 0, outputs: 1 }, - 0x47 => Opcode { code: code, name: "SELFBALANCE", mingas: 5, inputs: 0, outputs: 1 }, - 0x48 => Opcode { code: code, name: "BASEFEE", mingas: 2, inputs: 0, outputs: 1 }, - 0x50 => Opcode { code: code, name: "POP", mingas: 2, inputs: 1, outputs: 0 }, - 0x51 => Opcode { code: code, name: "MLOAD", mingas: 3, inputs: 1, outputs: 1 }, - 0x52 => Opcode { code: code, name: "MSTORE", mingas: 3, inputs: 2, outputs: 0 }, - 0x53 => Opcode { code: code, name: "MSTORE8", mingas: 3, inputs: 2, outputs: 0 }, - 0x54 => Opcode { code: code, name: "SLOAD", mingas: 0, inputs: 1, outputs: 1 }, - 0x55 => Opcode { code: code, name: "SSTORE", mingas: 0, inputs: 2, outputs: 0 }, - 0x56 => Opcode { code: code, name: "JUMP", mingas: 8, inputs: 1, outputs: 0 }, - 0x57 => Opcode { code: code, name: "JUMPI", mingas: 10, inputs: 2, outputs: 0 }, - 0x58 => Opcode { code: code, name: "PC", mingas: 2, inputs: 0, outputs: 1 }, - 0x59 => Opcode { code: code, name: "MSIZE", mingas: 2, inputs: 0, outputs: 1 }, - 0x5a => Opcode { code: code, name: "GAS", mingas: 2, inputs: 0, outputs: 1 }, - 0x5b => Opcode { code: code, name: "JUMPDEST", mingas: 1, inputs: 0, outputs: 0 }, - 0x5f => Opcode { code: code, name: "PUSH0", mingas: 3, inputs: 0, outputs: 1 }, - 0x60 => Opcode { code: code, name: "PUSH1", mingas: 3, inputs: 0, outputs: 1 }, - 0x61 => Opcode { code: code, name: "PUSH2", mingas: 3, inputs: 0, outputs: 1 }, - 0x62 => Opcode { code: code, name: "PUSH3", mingas: 3, inputs: 0, outputs: 1 }, - 0x63 => Opcode { code: code, name: "PUSH4", mingas: 3, inputs: 0, outputs: 1 }, - 0x64 => Opcode { code: code, name: "PUSH5", mingas: 3, inputs: 0, outputs: 1 }, - 0x65 => Opcode { code: code, name: "PUSH6", mingas: 3, inputs: 0, outputs: 1 }, - 0x66 => Opcode { code: code, name: "PUSH7", mingas: 3, inputs: 0, outputs: 1 }, - 0x67 => Opcode { code: code, name: "PUSH8", mingas: 3, inputs: 0, outputs: 1 }, - 0x68 => Opcode { code: code, name: "PUSH9", mingas: 3, inputs: 0, outputs: 1 }, - 0x69 => Opcode { code: code, name: "PUSH10", mingas: 3, inputs: 0, outputs: 1 }, - 0x6a => Opcode { code: code, name: "PUSH11", mingas: 3, inputs: 0, outputs: 1 }, - 0x6b => Opcode { code: code, name: "PUSH12", mingas: 3, inputs: 0, outputs: 1 }, - 0x6c => Opcode { code: code, name: "PUSH13", mingas: 3, inputs: 0, outputs: 1 }, - 0x6d => Opcode { code: code, name: "PUSH14", mingas: 3, inputs: 0, outputs: 1 }, - 0x6e => Opcode { code: code, name: "PUSH15", mingas: 3, inputs: 0, outputs: 1 }, - 0x6f => Opcode { code: code, name: "PUSH16", mingas: 3, inputs: 0, outputs: 1 }, - 0x70 => Opcode { code: code, name: "PUSH17", mingas: 3, inputs: 0, outputs: 1 }, - 0x71 => Opcode { code: code, name: "PUSH18", mingas: 3, inputs: 0, outputs: 1 }, - 0x72 => Opcode { code: code, name: "PUSH19", mingas: 3, inputs: 0, outputs: 1 }, - 0x73 => Opcode { code: code, name: "PUSH20", mingas: 3, inputs: 0, outputs: 1 }, - 0x74 => Opcode { code: code, name: "PUSH21", mingas: 3, inputs: 0, outputs: 1 }, - 0x75 => Opcode { code: code, name: "PUSH22", mingas: 3, inputs: 0, outputs: 1 }, - 0x76 => Opcode { code: code, name: "PUSH23", mingas: 3, inputs: 0, outputs: 1 }, - 0x77 => Opcode { code: code, name: "PUSH24", mingas: 3, inputs: 0, outputs: 1 }, - 0x78 => Opcode { code: code, name: "PUSH25", mingas: 3, inputs: 0, outputs: 1 }, - 0x79 => Opcode { code: code, name: "PUSH26", mingas: 3, inputs: 0, outputs: 1 }, - 0x7a => Opcode { code: code, name: "PUSH27", mingas: 3, inputs: 0, outputs: 1 }, - 0x7b => Opcode { code: code, name: "PUSH28", mingas: 3, inputs: 0, outputs: 1 }, - 0x7c => Opcode { code: code, name: "PUSH29", mingas: 3, inputs: 0, outputs: 1 }, - 0x7d => Opcode { code: code, name: "PUSH30", mingas: 3, inputs: 0, outputs: 1 }, - 0x7e => Opcode { code: code, name: "PUSH31", mingas: 3, inputs: 0, outputs: 1 }, - 0x7f => Opcode { code: code, name: "PUSH32", mingas: 3, inputs: 0, outputs: 1 }, - 0x80 => Opcode { code: code, name: "DUP1", mingas: 3, inputs: 1, outputs: 2 }, - 0x81 => Opcode { code: code, name: "DUP2", mingas: 3, inputs: 2, outputs: 3 }, - 0x82 => Opcode { code: code, name: "DUP3", mingas: 3, inputs: 3, outputs: 4 }, - 0x83 => Opcode { code: code, name: "DUP4", mingas: 3, inputs: 4, outputs: 5 }, - 0x84 => Opcode { code: code, name: "DUP5", mingas: 3, inputs: 5, outputs: 6 }, - 0x85 => Opcode { code: code, name: "DUP6", mingas: 3, inputs: 6, outputs: 7 }, - 0x86 => Opcode { code: code, name: "DUP7", mingas: 3, inputs: 7, outputs: 8 }, - 0x87 => Opcode { code: code, name: "DUP8", mingas: 3, inputs: 8, outputs: 9 }, - 0x88 => Opcode { code: code, name: "DUP9", mingas: 3, inputs: 9, outputs: 10 }, - 0x89 => Opcode { code: code, name: "DUP10", mingas: 3, inputs: 10, outputs: 11 }, - 0x8a => Opcode { code: code, name: "DUP11", mingas: 3, inputs: 11, outputs: 12 }, - 0x8b => Opcode { code: code, name: "DUP12", mingas: 3, inputs: 12, outputs: 13 }, - 0x8c => Opcode { code: code, name: "DUP13", mingas: 3, inputs: 13, outputs: 14 }, - 0x8d => Opcode { code: code, name: "DUP14", mingas: 3, inputs: 14, outputs: 15 }, - 0x8e => Opcode { code: code, name: "DUP15", mingas: 3, inputs: 15, outputs: 16 }, - 0x8f => Opcode { code: code, name: "DUP16", mingas: 3, inputs: 16, outputs: 17 }, - 0x90 => Opcode { code: code, name: "SWAP1", mingas: 3, inputs: 2, outputs: 2 }, - 0x91 => Opcode { code: code, name: "SWAP2", mingas: 3, inputs: 3, outputs: 3 }, - 0x92 => Opcode { code: code, name: "SWAP3", mingas: 3, inputs: 4, outputs: 4 }, - 0x93 => Opcode { code: code, name: "SWAP4", mingas: 3, inputs: 5, outputs: 5 }, - 0x94 => Opcode { code: code, name: "SWAP5", mingas: 3, inputs: 6, outputs: 6 }, - 0x95 => Opcode { code: code, name: "SWAP6", mingas: 3, inputs: 7, outputs: 7 }, - 0x96 => Opcode { code: code, name: "SWAP7", mingas: 3, inputs: 8, outputs: 8 }, - 0x97 => Opcode { code: code, name: "SWAP8", mingas: 3, inputs: 9, outputs: 9 }, - 0x98 => Opcode { code: code, name: "SWAP9", mingas: 3, inputs: 10, outputs: 10 }, - 0x99 => Opcode { code: code, name: "SWAP10", mingas: 3, inputs: 11, outputs: 11 }, - 0x9a => Opcode { code: code, name: "SWAP11", mingas: 3, inputs: 12, outputs: 12 }, - 0x9b => Opcode { code: code, name: "SWAP12", mingas: 3, inputs: 13, outputs: 13 }, - 0x9c => Opcode { code: code, name: "SWAP13", mingas: 3, inputs: 14, outputs: 14 }, - 0x9d => Opcode { code: code, name: "SWAP14", mingas: 3, inputs: 15, outputs: 15 }, - 0x9e => Opcode { code: code, name: "SWAP15", mingas: 3, inputs: 16, outputs: 16 }, - 0x9f => Opcode { code: code, name: "SWAP16", mingas: 3, inputs: 17, outputs: 17 }, - 0xa0 => Opcode { code: code, name: "LOG0", mingas: 375, inputs: 2, outputs: 0 }, - 0xa1 => Opcode { code: code, name: "LOG1", mingas: 375, inputs: 3, outputs: 0 }, - 0xa2 => Opcode { code: code, name: "LOG2", mingas: 375, inputs: 4, outputs: 0 }, - 0xa3 => Opcode { code: code, name: "LOG3", mingas: 375, inputs: 5, outputs: 0 }, - 0xa4 => Opcode { code: code, name: "LOG4", mingas: 375, inputs: 6, outputs: 0 }, - 0xf0 => Opcode { code: code, name: "CREATE", mingas: 32000, inputs: 3, outputs: 1 }, - 0xf1 => Opcode { code: code, name: "CALL", mingas: 100, inputs: 7, outputs: 1 }, - 0xf2 => Opcode { code: code, name: "CALLCODE", mingas: 100, inputs: 7, outputs: 1 }, - 0xf3 => Opcode { code: code, name: "RETURN", mingas: 0, inputs: 2, outputs: 0 }, - 0xf4 => Opcode { code: code, name: "DELEGATECALL", mingas: 100, inputs: 6, outputs: 1 }, - 0xf5 => Opcode { code: code, name: "CREATE2", mingas: 32000, inputs: 4, outputs: 1 }, - 0xfa => Opcode { code: code, name: "STATICCALL", mingas: 100, inputs: 6, outputs: 1 }, - 0xfd => Opcode { code: code, name: "REVERT", mingas: 0, inputs: 2, outputs: 0 }, - 0xfe => Opcode { code: code, name: "INVALID", mingas: 0, inputs: 0, outputs: 0 }, - 0xff => Opcode { code: code, name: "SELFDESTRUCT", mingas: 5000, inputs: 1, outputs: 0 }, - _ => Opcode { code: code, name: "unknown", mingas: 0, inputs: 0, outputs: 0 }, +impl Opcode { + /// Creates a new [`Opcode`] with the given code. + /// + /// ``` + /// use heimdall_common::ether::evm::core::opcodes::Opcode; + /// + /// let opcode = Opcode::new(0x01); + /// assert_eq!(opcode.code, 0x01); + /// assert_eq!(opcode.name, "ADD"); + /// ``` + pub fn new(code: u8) -> Opcode { + match code { + 0x00 => Opcode { code, name: "STOP", mingas: 0, inputs: 0, outputs: 0 }, + 0x01 => Opcode { code, name: "ADD", mingas: 3, inputs: 2, outputs: 1 }, + 0x02 => Opcode { code, name: "MUL", mingas: 5, inputs: 2, outputs: 1 }, + 0x03 => Opcode { code, name: "SUB", mingas: 3, inputs: 2, outputs: 1 }, + 0x04 => Opcode { code, name: "DIV", mingas: 5, inputs: 2, outputs: 1 }, + 0x05 => Opcode { code, name: "SDIV", mingas: 5, inputs: 2, outputs: 1 }, + 0x06 => Opcode { code, name: "MOD", mingas: 5, inputs: 2, outputs: 1 }, + 0x07 => Opcode { code, name: "SMOD", mingas: 5, inputs: 2, outputs: 1 }, + 0x08 => Opcode { code, name: "ADDMOD", mingas: 8, inputs: 3, outputs: 1 }, + 0x09 => Opcode { code, name: "MULMOD", mingas: 8, inputs: 3, outputs: 1 }, + 0x0a => Opcode { code, name: "EXP", mingas: 10, inputs: 2, outputs: 1 }, + 0x0b => Opcode { code, name: "SIGNEXTEND", mingas: 5, inputs: 2, outputs: 1 }, + 0x10 => Opcode { code, name: "LT", mingas: 3, inputs: 2, outputs: 1 }, + 0x11 => Opcode { code, name: "GT", mingas: 3, inputs: 2, outputs: 1 }, + 0x12 => Opcode { code, name: "SLT", mingas: 3, inputs: 2, outputs: 1 }, + 0x13 => Opcode { code, name: "SGT", mingas: 3, inputs: 2, outputs: 1 }, + 0x14 => Opcode { code, name: "EQ", mingas: 3, inputs: 2, outputs: 1 }, + 0x15 => Opcode { code, name: "ISZERO", mingas: 3, inputs: 1, outputs: 1 }, + 0x16 => Opcode { code, name: "AND", mingas: 3, inputs: 2, outputs: 1 }, + 0x17 => Opcode { code, name: "OR", mingas: 3, inputs: 2, outputs: 1 }, + 0x18 => Opcode { code, name: "XOR", mingas: 3, inputs: 2, outputs: 1 }, + 0x19 => Opcode { code, name: "NOT", mingas: 3, inputs: 1, outputs: 1 }, + 0x1a => Opcode { code, name: "BYTE", mingas: 3, inputs: 2, outputs: 1 }, + 0x1b => Opcode { code, name: "SHL", mingas: 3, inputs: 2, outputs: 1 }, + 0x1c => Opcode { code, name: "SHR", mingas: 3, inputs: 2, outputs: 1 }, + 0x1d => Opcode { code, name: "SAR", mingas: 3, inputs: 2, outputs: 1 }, + 0x20 => Opcode { code, name: "SHA3", mingas: 30, inputs: 2, outputs: 1 }, + 0x30 => Opcode { code, name: "ADDRESS", mingas: 2, inputs: 0, outputs: 1 }, + 0x31 => Opcode { code, name: "BALANCE", mingas: 100, inputs: 1, outputs: 1 }, + 0x32 => Opcode { code, name: "ORIGIN", mingas: 2, inputs: 0, outputs: 1 }, + 0x33 => Opcode { code, name: "CALLER", mingas: 2, inputs: 0, outputs: 1 }, + 0x34 => Opcode { code, name: "CALLVALUE", mingas: 2, inputs: 0, outputs: 1 }, + 0x35 => Opcode { code, name: "CALLDATALOAD", mingas: 3, inputs: 1, outputs: 1 }, + 0x36 => Opcode { code, name: "CALLDATASIZE", mingas: 2, inputs: 0, outputs: 1 }, + 0x37 => Opcode { code, name: "CALLDATACOPY", mingas: 3, inputs: 3, outputs: 0 }, + 0x38 => Opcode { code, name: "CODESIZE", mingas: 2, inputs: 0, outputs: 1 }, + 0x39 => Opcode { code, name: "CODECOPY", mingas: 3, inputs: 3, outputs: 0 }, + 0x3a => Opcode { code, name: "GASPRICE", mingas: 2, inputs: 0, outputs: 1 }, + 0x3b => Opcode { code, name: "EXTCODESIZE", mingas: 100, inputs: 1, outputs: 1 }, + 0x3c => Opcode { code, name: "EXTCODECOPY", mingas: 100, inputs: 4, outputs: 0 }, + 0x3d => Opcode { code, name: "RETURNDATASIZE", mingas: 2, inputs: 0, outputs: 1 }, + 0x3e => Opcode { code, name: "RETURNDATACOPY", mingas: 3, inputs: 3, outputs: 0 }, + 0x3f => Opcode { code, name: "EXTCODEHASH", mingas: 100, inputs: 1, outputs: 1 }, + 0x40 => Opcode { code, name: "BLOCKHASH", mingas: 20, inputs: 1, outputs: 1 }, + 0x41 => Opcode { code, name: "COINBASE", mingas: 2, inputs: 0, outputs: 1 }, + 0x42 => Opcode { code, name: "TIMESTAMP", mingas: 2, inputs: 0, outputs: 1 }, + 0x43 => Opcode { code, name: "NUMBER", mingas: 2, inputs: 0, outputs: 1 }, + 0x44 => Opcode { code, name: "DIFFICULTY", mingas: 2, inputs: 0, outputs: 1 }, + 0x45 => Opcode { code, name: "GASLIMIT", mingas: 2, inputs: 0, outputs: 1 }, + 0x46 => Opcode { code, name: "CHAINID", mingas: 2, inputs: 0, outputs: 1 }, + 0x47 => Opcode { code, name: "SELFBALANCE", mingas: 5, inputs: 0, outputs: 1 }, + 0x48 => Opcode { code, name: "BASEFEE", mingas: 2, inputs: 0, outputs: 1 }, + 0x50 => Opcode { code, name: "POP", mingas: 2, inputs: 1, outputs: 0 }, + 0x51 => Opcode { code, name: "MLOAD", mingas: 3, inputs: 1, outputs: 1 }, + 0x52 => Opcode { code, name: "MSTORE", mingas: 3, inputs: 2, outputs: 0 }, + 0x53 => Opcode { code, name: "MSTORE8", mingas: 3, inputs: 2, outputs: 0 }, + 0x54 => Opcode { code, name: "SLOAD", mingas: 0, inputs: 1, outputs: 1 }, + 0x55 => Opcode { code, name: "SSTORE", mingas: 0, inputs: 2, outputs: 0 }, + 0x56 => Opcode { code, name: "JUMP", mingas: 8, inputs: 1, outputs: 0 }, + 0x57 => Opcode { code, name: "JUMPI", mingas: 10, inputs: 2, outputs: 0 }, + 0x58 => Opcode { code, name: "PC", mingas: 2, inputs: 0, outputs: 1 }, + 0x59 => Opcode { code, name: "MSIZE", mingas: 2, inputs: 0, outputs: 1 }, + 0x5a => Opcode { code, name: "GAS", mingas: 2, inputs: 0, outputs: 1 }, + 0x5b => Opcode { code, name: "JUMPDEST", mingas: 1, inputs: 0, outputs: 0 }, + 0x5f => Opcode { code, name: "PUSH0", mingas: 3, inputs: 0, outputs: 1 }, + 0x60 => Opcode { code, name: "PUSH1", mingas: 3, inputs: 0, outputs: 1 }, + 0x61 => Opcode { code, name: "PUSH2", mingas: 3, inputs: 0, outputs: 1 }, + 0x62 => Opcode { code, name: "PUSH3", mingas: 3, inputs: 0, outputs: 1 }, + 0x63 => Opcode { code, name: "PUSH4", mingas: 3, inputs: 0, outputs: 1 }, + 0x64 => Opcode { code, name: "PUSH5", mingas: 3, inputs: 0, outputs: 1 }, + 0x65 => Opcode { code, name: "PUSH6", mingas: 3, inputs: 0, outputs: 1 }, + 0x66 => Opcode { code, name: "PUSH7", mingas: 3, inputs: 0, outputs: 1 }, + 0x67 => Opcode { code, name: "PUSH8", mingas: 3, inputs: 0, outputs: 1 }, + 0x68 => Opcode { code, name: "PUSH9", mingas: 3, inputs: 0, outputs: 1 }, + 0x69 => Opcode { code, name: "PUSH10", mingas: 3, inputs: 0, outputs: 1 }, + 0x6a => Opcode { code, name: "PUSH11", mingas: 3, inputs: 0, outputs: 1 }, + 0x6b => Opcode { code, name: "PUSH12", mingas: 3, inputs: 0, outputs: 1 }, + 0x6c => Opcode { code, name: "PUSH13", mingas: 3, inputs: 0, outputs: 1 }, + 0x6d => Opcode { code, name: "PUSH14", mingas: 3, inputs: 0, outputs: 1 }, + 0x6e => Opcode { code, name: "PUSH15", mingas: 3, inputs: 0, outputs: 1 }, + 0x6f => Opcode { code, name: "PUSH16", mingas: 3, inputs: 0, outputs: 1 }, + 0x70 => Opcode { code, name: "PUSH17", mingas: 3, inputs: 0, outputs: 1 }, + 0x71 => Opcode { code, name: "PUSH18", mingas: 3, inputs: 0, outputs: 1 }, + 0x72 => Opcode { code, name: "PUSH19", mingas: 3, inputs: 0, outputs: 1 }, + 0x73 => Opcode { code, name: "PUSH20", mingas: 3, inputs: 0, outputs: 1 }, + 0x74 => Opcode { code, name: "PUSH21", mingas: 3, inputs: 0, outputs: 1 }, + 0x75 => Opcode { code, name: "PUSH22", mingas: 3, inputs: 0, outputs: 1 }, + 0x76 => Opcode { code, name: "PUSH23", mingas: 3, inputs: 0, outputs: 1 }, + 0x77 => Opcode { code, name: "PUSH24", mingas: 3, inputs: 0, outputs: 1 }, + 0x78 => Opcode { code, name: "PUSH25", mingas: 3, inputs: 0, outputs: 1 }, + 0x79 => Opcode { code, name: "PUSH26", mingas: 3, inputs: 0, outputs: 1 }, + 0x7a => Opcode { code, name: "PUSH27", mingas: 3, inputs: 0, outputs: 1 }, + 0x7b => Opcode { code, name: "PUSH28", mingas: 3, inputs: 0, outputs: 1 }, + 0x7c => Opcode { code, name: "PUSH29", mingas: 3, inputs: 0, outputs: 1 }, + 0x7d => Opcode { code, name: "PUSH30", mingas: 3, inputs: 0, outputs: 1 }, + 0x7e => Opcode { code, name: "PUSH31", mingas: 3, inputs: 0, outputs: 1 }, + 0x7f => Opcode { code, name: "PUSH32", mingas: 3, inputs: 0, outputs: 1 }, + 0x80 => Opcode { code, name: "DUP1", mingas: 3, inputs: 1, outputs: 2 }, + 0x81 => Opcode { code, name: "DUP2", mingas: 3, inputs: 2, outputs: 3 }, + 0x82 => Opcode { code, name: "DUP3", mingas: 3, inputs: 3, outputs: 4 }, + 0x83 => Opcode { code, name: "DUP4", mingas: 3, inputs: 4, outputs: 5 }, + 0x84 => Opcode { code, name: "DUP5", mingas: 3, inputs: 5, outputs: 6 }, + 0x85 => Opcode { code, name: "DUP6", mingas: 3, inputs: 6, outputs: 7 }, + 0x86 => Opcode { code, name: "DUP7", mingas: 3, inputs: 7, outputs: 8 }, + 0x87 => Opcode { code, name: "DUP8", mingas: 3, inputs: 8, outputs: 9 }, + 0x88 => Opcode { code, name: "DUP9", mingas: 3, inputs: 9, outputs: 10 }, + 0x89 => Opcode { code, name: "DUP10", mingas: 3, inputs: 10, outputs: 11 }, + 0x8a => Opcode { code, name: "DUP11", mingas: 3, inputs: 11, outputs: 12 }, + 0x8b => Opcode { code, name: "DUP12", mingas: 3, inputs: 12, outputs: 13 }, + 0x8c => Opcode { code, name: "DUP13", mingas: 3, inputs: 13, outputs: 14 }, + 0x8d => Opcode { code, name: "DUP14", mingas: 3, inputs: 14, outputs: 15 }, + 0x8e => Opcode { code, name: "DUP15", mingas: 3, inputs: 15, outputs: 16 }, + 0x8f => Opcode { code, name: "DUP16", mingas: 3, inputs: 16, outputs: 17 }, + 0x90 => Opcode { code, name: "SWAP1", mingas: 3, inputs: 2, outputs: 2 }, + 0x91 => Opcode { code, name: "SWAP2", mingas: 3, inputs: 3, outputs: 3 }, + 0x92 => Opcode { code, name: "SWAP3", mingas: 3, inputs: 4, outputs: 4 }, + 0x93 => Opcode { code, name: "SWAP4", mingas: 3, inputs: 5, outputs: 5 }, + 0x94 => Opcode { code, name: "SWAP5", mingas: 3, inputs: 6, outputs: 6 }, + 0x95 => Opcode { code, name: "SWAP6", mingas: 3, inputs: 7, outputs: 7 }, + 0x96 => Opcode { code, name: "SWAP7", mingas: 3, inputs: 8, outputs: 8 }, + 0x97 => Opcode { code, name: "SWAP8", mingas: 3, inputs: 9, outputs: 9 }, + 0x98 => Opcode { code, name: "SWAP9", mingas: 3, inputs: 10, outputs: 10 }, + 0x99 => Opcode { code, name: "SWAP10", mingas: 3, inputs: 11, outputs: 11 }, + 0x9a => Opcode { code, name: "SWAP11", mingas: 3, inputs: 12, outputs: 12 }, + 0x9b => Opcode { code, name: "SWAP12", mingas: 3, inputs: 13, outputs: 13 }, + 0x9c => Opcode { code, name: "SWAP13", mingas: 3, inputs: 14, outputs: 14 }, + 0x9d => Opcode { code, name: "SWAP14", mingas: 3, inputs: 15, outputs: 15 }, + 0x9e => Opcode { code, name: "SWAP15", mingas: 3, inputs: 16, outputs: 16 }, + 0x9f => Opcode { code, name: "SWAP16", mingas: 3, inputs: 17, outputs: 17 }, + 0xa0 => Opcode { code, name: "LOG0", mingas: 375, inputs: 2, outputs: 0 }, + 0xa1 => Opcode { code, name: "LOG1", mingas: 375, inputs: 3, outputs: 0 }, + 0xa2 => Opcode { code, name: "LOG2", mingas: 375, inputs: 4, outputs: 0 }, + 0xa3 => Opcode { code, name: "LOG3", mingas: 375, inputs: 5, outputs: 0 }, + 0xa4 => Opcode { code, name: "LOG4", mingas: 375, inputs: 6, outputs: 0 }, + 0xf0 => Opcode { code, name: "CREATE", mingas: 32000, inputs: 3, outputs: 1 }, + 0xf1 => Opcode { code, name: "CALL", mingas: 100, inputs: 7, outputs: 1 }, + 0xf2 => Opcode { code, name: "CALLCODE", mingas: 100, inputs: 7, outputs: 1 }, + 0xf3 => Opcode { code, name: "RETURN", mingas: 0, inputs: 2, outputs: 0 }, + 0xf4 => Opcode { code, name: "DELEGATECALL", mingas: 100, inputs: 6, outputs: 1 }, + 0xf5 => Opcode { code, name: "CREATE2", mingas: 32000, inputs: 4, outputs: 1 }, + 0xfa => Opcode { code, name: "STATICCALL", mingas: 100, inputs: 6, outputs: 1 }, + 0xfd => Opcode { code, name: "REVERT", mingas: 0, inputs: 2, outputs: 0 }, + 0xfe => Opcode { code, name: "INVALID", mingas: 0, inputs: 0, outputs: 0 }, + 0xff => Opcode { code, name: "SELFDESTRUCT", mingas: 5000, inputs: 1, outputs: 0 }, + _ => Opcode { code, name: "unknown", mingas: 0, inputs: 0, outputs: 0 }, + } } } -// enum allows for Wrapped Opcodes to contain both raw U256 and Opcodes as inputs +/// A WrappedInput can contain either a raw U256 value or a WrappedOpcode #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum WrappedInput { Raw(U256), Opcode(WrappedOpcode), } -// represents an opcode with its direct inputs as WrappedInputs +/// A WrappedOpcode is an Opcode with its inputs wrapped in a WrappedInput #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct WrappedOpcode { pub opcode: Opcode, @@ -176,12 +187,32 @@ pub struct WrappedOpcode { } impl WrappedOpcode { + /// Returns the depth of the opcode, i.e. the maximum recursion depth of its inputs + /// + /// ``` + /// use heimdall_common::ether::evm::core::opcodes::*; + /// + /// let opcode = WrappedOpcode::new(0x01, vec![WrappedInput::Raw(1.into()), WrappedInput::Raw(2.into())]); + /// assert_eq!(opcode.depth(), 1); + /// ``` pub fn depth(&self) -> u32 { self.inputs.iter().map(|x| x.depth()).max().unwrap_or(0) + 1 } } impl WrappedInput { + /// Returns the depth of the input, i.e. 0 for a raw U256 and the depth of the opcode for a + /// WrappedOpcode + /// + /// ``` + /// use heimdall_common::ether::evm::core::opcodes::*; + /// + /// let opcode = WrappedOpcode::new(0x01, vec![WrappedInput::Raw(1.into()), WrappedInput::Raw(2.into())]); + /// assert_eq!(opcode.depth(), 1); + /// + /// let input = WrappedInput::Opcode(opcode); + /// assert_eq!(input.depth(), 1); + /// ``` pub fn depth(&self) -> u32 { match self { WrappedInput::Raw(_) => 0, @@ -190,7 +221,6 @@ impl WrappedInput { } } -// implements pretty printing for WrappedOpcodes impl Display for WrappedOpcode { fn fmt(&self, f: &mut Formatter) -> Result { write!( @@ -210,3 +240,38 @@ impl Display for WrappedInput { } } } + +#[cfg(test)] +mod tests { + use ethers::types::U256; + + use crate::ether::evm::core::opcodes::*; + + #[test] + fn test_opcode() { + let add_operation = Opcode::new(0x01); + assert_eq!(add_operation.code, 0x01); + assert_eq!(add_operation.name, "ADD"); + } + + #[test] + fn test_get_unknown_opcode() { + let unknown_opcode = Opcode::new(0xee); + assert_eq!(unknown_opcode.name, "unknown"); + } + + #[test] + fn test_wrapping_opcodes() { + // wraps an ADD operation with 2 raw inputs + let add_operation_wrapped = WrappedOpcode::new( + 0x01, + vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], + ); + println!("{}", add_operation_wrapped); + + // wraps a CALLDATALOAD operation + let calldataload_wrapped = + WrappedOpcode::new(0x35, vec![WrappedInput::Opcode(add_operation_wrapped)]); + println!("{}", calldataload_wrapped); + } +} diff --git a/common/src/ether/evm/core/stack.rs b/common/src/ether/evm/core/stack.rs index f8c9ea37..ea7bf32f 100644 --- a/common/src/ether/evm/core/stack.rs +++ b/common/src/ether/evm/core/stack.rs @@ -8,14 +8,17 @@ use ethers::prelude::U256; use super::opcodes::WrappedOpcode; -// This implemtation is a simple, (hopefully lightweight) LIFO stack. -// Supports simple push/pop operations, with further helper operations -// such as peek and is_empty. +/// The [`Stack`] struct represents the EVM stack. +/// It is a LIFO data structure that holds a VecDeque of [`StackFrame`]s. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct Stack { pub stack: VecDeque, } +/// The [`StackFrame`] struct represents a single frame on the stack. +/// It holds a [`U256`] value and the [`WrappedOpcode`] that pushed it onto the stack. \ +/// \ +/// By doing this, we can keep track of the source of each value on the stack in a recursive manner. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct StackFrame { pub value: U256, @@ -23,16 +26,46 @@ pub struct StackFrame { } impl Stack { + /// Creates a new [`Stack`]. + /// + /// ``` + /// use heimdall_common::ether::evm::core::stack::Stack; + /// + /// let stack = Stack::new(); + /// assert_eq!(stack.size(), 0); + /// ``` pub fn new() -> Stack { Stack { stack: VecDeque::new() } } - // Push a value onto the stack. + /// Push a value onto the stack. + /// Creates a new [`StackFrame`] with the given [`U256`] value and [`WrappedOpcode`]. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// assert_eq!(stack.size(), 1); + /// ``` pub fn push(&mut self, value: U256, operation: WrappedOpcode) { - self.stack.push_front(StackFrame { value: value, operation }); + self.stack.push_front(StackFrame { value, operation }); } - // Pop a value off the stack. + /// Pop a value off the stack. + /// Returns a [`StackFrame`] with the value and [`WrappedOpcode`] of the popped value. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + + /// let frame = stack.pop(); + /// assert_eq!(frame.value, U256::from(0x00)); + /// ``` pub fn pop(&mut self) -> StackFrame { match self.stack.pop_front() { Some(value) => value, @@ -40,7 +73,29 @@ impl Stack { } } - // Pop n values off the stack. + /// Pop n values off the stack. + /// Returns a Vec of [`StackFrame`]s with the values and [`WrappedOpcode`]s of the popped + /// values. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// stack.push(U256::from(0x01), WrappedOpcode::default()); + /// stack.push(U256::from(0x02), WrappedOpcode::default()); + /// + /// // stack is now [0x02, 0x01, 0x00] + /// let frames = stack.pop_n(2); + /// assert_eq!(frames[0].value, U256::from(0x02)); + /// assert_eq!(frames[1].value, U256::from(0x01)); + /// + /// // stack is now [0x00] + /// assert_eq!(stack.pop().value, U256::from(0x00)); + /// + /// // stack is now [] + /// ``` pub fn pop_n(&mut self, n: usize) -> Vec { let mut values = Vec::new(); for _ in 0..n { @@ -49,7 +104,23 @@ impl Stack { values } - // Swap the top value and the nth value on the stack. + /// Swap the top value and the nth value on the stack. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// stack.push(U256::from(0x01), WrappedOpcode::default()); + /// + /// // stack is now [0x01, 0x00] + /// stack.swap(1); + /// + /// // stack is now [0x00, 0x01] + /// assert_eq!(stack.pop().value, U256::from(0x00)); + /// assert_eq!(stack.pop().value, U256::from(0x01)); + /// ``` pub fn swap(&mut self, n: usize) -> bool { if self.stack.get_mut(n).is_some() { self.stack.swap(0, n); @@ -59,7 +130,24 @@ impl Stack { } } - // Duplicate the nth value on the stack. + /// Duplicate the nth value on the stack. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// + /// // stack is now [0x00] + /// stack.dup(1); + /// + /// // stack is now [0x00, 0x00] + /// assert_eq!(stack.pop().value, U256::from(0x00)); + /// assert_eq!(stack.pop().value, U256::from(0x00)); + /// + /// // stack is now [] + /// ``` pub fn dup(&mut self, n: usize) -> bool { match self.stack.get_mut(n - 1) { Some(_) => { @@ -70,7 +158,18 @@ impl Stack { } } - // Peek at the top value on the stack. + /// Peek at the top value on the stack. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// + /// // stack is now [0x00] + /// assert_eq!(stack.peek(0).value, U256::from(0x00)); + /// ``` pub fn peek(&self, index: usize) -> StackFrame { match self.stack.get(index) { Some(value) => value.to_owned(), @@ -78,7 +177,29 @@ impl Stack { } } - // gets the top n values of the stack + /// gets the top n values of the stack + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// stack.push(U256::from(0x01), WrappedOpcode::default()); + /// stack.push(U256::from(0x02), WrappedOpcode::default()); + /// + /// // stack is now [0x02, 0x01, 0x00] + /// let frames = stack.peek_n(2); + /// assert_eq!(frames[0].value, U256::from(0x02)); + /// assert_eq!(frames[1].value, U256::from(0x01)); + /// + /// // stack is still [0x02, 0x01, 0x00] + /// assert_eq!(stack.pop().value, U256::from(0x02)); + /// assert_eq!(stack.pop().value, U256::from(0x01)); + /// assert_eq!(stack.pop().value, U256::from(0x00)); + /// + /// // stack is now [] + /// ``` pub fn peek_n(&self, n: usize) -> Vec { let mut values = Vec::new(); for i in 0..n { @@ -87,16 +208,55 @@ impl Stack { values } - // Get the size of the stack + /// Get the size of the stack + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// + /// // stack is now [0x00] + /// assert_eq!(stack.size(), 1); + /// ``` pub fn size(&self) -> usize { self.stack.len() } - // Check if the stack is empty. + /// Check if the stack is empty. + /// + /// ``` + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// + /// // stack is now [0x00] + /// assert_eq!(stack.is_empty(), false); + /// + /// stack.pop(); + /// + /// // stack is now [] + /// assert_eq!(stack.is_empty(), true); + /// ``` pub fn is_empty(&self) -> bool { self.stack.is_empty() } + /// A simple hash of the stack. Used in various symbolic execution optimizations. + /// + /// ```no_run + /// use ethers::prelude::U256; + /// use heimdall_common::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + /// + /// let mut stack = Stack::new(); + /// stack.push(U256::from(0x00), WrappedOpcode::default()); + /// + /// // stack is now [0x00] + /// assert_eq!(stack.hash(), 0x00); + /// ``` pub fn hash(&self) -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); self.stack.hash(&mut hasher); @@ -113,3 +273,73 @@ impl Display for Stack { write!(f, "[{:#02x?}]", stack) } } + +#[cfg(test)] +mod tests { + use crate::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; + use ethers::types::U256; + + #[test] + fn test_push_pop() { + let mut stack = Stack::new(); + stack.push(U256::from(1), WrappedOpcode::default()); + stack.push(U256::from(2), WrappedOpcode::default()); + assert_eq!(stack.pop().value, U256::from(2)); + assert_eq!(stack.pop().value, U256::from(1)); + assert!(stack.is_empty()); + } + + #[test] + fn test_pop_n() { + let mut stack = Stack::new(); + stack.push(U256::from(1), WrappedOpcode::default()); + stack.push(U256::from(2), WrappedOpcode::default()); + stack.push(U256::from(3), WrappedOpcode::default()); + let values = stack.pop_n(2); + assert_eq!(values.len(), 2); + assert_eq!(values[0].value, U256::from(3)); + assert_eq!(values[1].value, U256::from(2)); + assert_eq!(stack.pop().value, U256::from(1)); + assert!(stack.is_empty()); + } + + #[test] + fn test_swap() { + let mut stack = Stack::new(); + stack.push(U256::from(1), WrappedOpcode::default()); + stack.push(U256::from(2), WrappedOpcode::default()); + stack.push(U256::from(3), WrappedOpcode::default()); + assert!(stack.swap(1)); + assert_eq!(stack.pop().value, U256::from(2)); + assert_eq!(stack.pop().value, U256::from(3)); + assert_eq!(stack.pop().value, U256::from(1)); + assert!(stack.is_empty()); + assert!(!stack.swap(1)); + } + + #[test] + fn test_dup() { + let mut stack = Stack::new(); + stack.push(U256::from(1), WrappedOpcode::default()); + stack.push(U256::from(2), WrappedOpcode::default()); + stack.push(U256::from(3), WrappedOpcode::default()); + assert!(stack.dup(1)); + assert_eq!(stack.pop().value, U256::from(3)); + assert_eq!(stack.pop().value, U256::from(3)); + assert_eq!(stack.pop().value, U256::from(2)); + assert_eq!(stack.pop().value, U256::from(1)); + assert!(stack.is_empty()); + } + + #[test] + fn test_peek() { + let mut stack = Stack::new(); + stack.push(U256::from(1), WrappedOpcode::default()); + stack.push(U256::from(2), WrappedOpcode::default()); + stack.push(U256::from(3), WrappedOpcode::default()); + assert_eq!(stack.peek(0).value, U256::from(3)); + assert_eq!(stack.peek(1).value, U256::from(2)); + assert_eq!(stack.peek(2).value, U256::from(1)); + assert_eq!(stack.peek(3).value, U256::from(0)); + } +} diff --git a/common/src/ether/evm/core/storage.rs b/common/src/ether/evm/core/storage.rs index bd3d3104..f9bba6d5 100644 --- a/common/src/ether/evm/core/storage.rs +++ b/common/src/ether/evm/core/storage.rs @@ -1,26 +1,53 @@ use std::collections::{HashMap, HashSet}; +/// The [`Storage`] struct represents the storage of a contract. \ +/// \ +/// We keep track of the storage as a HashMap, as well as a HashSet of keys that have been accessed +/// for gas calculation purposes. #[derive(Clone, Debug)] pub struct Storage { - // HashMap of [u8; 32] -> [u8; 32] pub storage: HashMap<[u8; 32], [u8; 32]>, access_set: HashSet<[u8; 32]>, } impl Storage { - // Use sized HashMap of u8 to repr bytes32 + /// Creates a new [`Storage`] struct. + /// + /// ``` + /// use heimdall_common::ether::evm::core::storage::Storage; + /// + /// let storage = Storage::new(); + /// ``` pub fn new() -> Storage { Storage { storage: HashMap::new(), access_set: HashSet::new() } } - // stores a key:value pair in the storage map + /// Store a key-value pair in the storage map. + /// + /// ``` + /// use heimdall_common::ether::evm::core::storage::Storage; + /// + /// let mut storage = Storage::new(); + /// storage.store([1u8; 32], [2u8; 32]); + /// + /// assert_eq!(storage.storage.get(&[1u8; 32]), Some(&[2u8; 32])); + /// ``` pub fn store(&mut self, key: [u8; 32], value: [u8; 32]) { self.access_set.insert(key); self.storage.insert(key, value); } - // loads a key from the storage map + /// Load a value from the storage map. + /// + /// ``` + /// use heimdall_common::ether::evm::core::storage::Storage; + /// + /// let mut storage = Storage::new(); + /// storage.store([1u8; 32], [2u8; 32]); + /// + /// assert_eq!(storage.load([1u8; 32]), [2u8; 32]); + /// ``` pub fn load(&mut self, key: [u8; 32]) -> [u8; 32] { self.access_set.insert(key); @@ -31,6 +58,20 @@ impl Storage { } } + /// calculate the cost of accessing a key in storage + /// + /// ``` + /// use heimdall_common::ether::evm::core::storage::Storage; + /// + /// let mut storage = Storage::new(); + /// + /// // key `[1u8; 32]` is not warm, so the cost should be 2100 + /// assert_eq!(storage.access_cost([1u8; 32]), 2100); + /// storage.store([1u8; 32], [2u8; 32]); + /// + /// // key `[1u8; 32]` is warm, so the cost should be 100 + /// assert_eq!(storage.access_cost([1u8; 32]), 100); + /// ``` pub fn access_cost(&mut self, key: [u8; 32]) -> u128 { if self.access_set.contains(&key) { 100 @@ -40,6 +81,20 @@ impl Storage { } } + /// calculate the cost of storing a key-value pair in storage + /// + /// ``` + /// use heimdall_common::ether::evm::core::storage::Storage; + /// + /// let mut storage = Storage::new(); + /// + /// // value `[0u8; 32]` is zero, i.e. clearing a key, so the cost should be 2900 + self.access_cost(key) + /// assert_eq!(storage.storage_cost([1u8; 32], [0u8; 32]), 5000); + /// storage.store([1u8; 32], [2u8; 32]); + /// + /// // value `[2u8; 32]` is not zero, so the cost should be 20000 + self.access_cost(key) + /// assert_eq!(storage.storage_cost([1u8; 32], [2u8; 32]), 20100); + /// ``` pub fn storage_cost(&mut self, key: [u8; 32], value: [u8; 32]) -> u128 { if value == [0u8; 32] { 2900 + self.access_cost(key) @@ -48,3 +103,186 @@ impl Storage { } } } + +#[cfg(test)] +mod tests { + use crate::ether::evm::core::storage::Storage; + + #[test] + fn test_sstore_sload() { + let mut storage = Storage::new(); + + storage.store( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + ); + assert_eq!( + storage.load([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1 + ]), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1 + ] + ); + + storage.store( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, + 7, 8, 2, 1, + ], + ); + assert_eq!( + storage.load([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255 + ]), + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, + 7, 8, 2, 1 + ], + ); + + assert_eq!( + storage.load([ + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255 + ]), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + } + + #[test] + fn test_storage_access_cost_cold() { + let mut storage = Storage::new(); + assert_eq!( + storage.access_cost([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1 + ]), + 2100 + ); + } + + #[test] + fn test_storage_access_cost_warm() { + let mut storage = Storage::new(); + storage.load([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, + ]); + assert_eq!( + storage.access_cost([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1 + ]), + 100 + ); + } + + #[test] + fn test_storage_storage_cost_cold() { + let mut storage = Storage::new(); + assert_eq!( + storage.storage_cost( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ] + ), + 22100 + ); + } + + #[test] + fn test_storage_storage_cost_cold_zero() { + let mut storage = Storage::new(); + assert_eq!( + storage.storage_cost( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 + ] + ), + 5000 + ); + } + + #[test] + fn test_storage_storage_cost_warm() { + let mut storage = Storage::new(); + storage.store( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + ); + assert_eq!( + storage.storage_cost( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ] + ), + 20100 + ); + } + + #[test] + fn test_storage_storage_cost_warm_zero() { + let mut storage = Storage::new(); + storage.store( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + ); + assert_eq!( + storage.storage_cost( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 + ] + ), + 3000 + ); + } +} diff --git a/common/src/ether/evm/core/tests.rs b/common/src/ether/evm/core/tests.rs deleted file mode 100644 index d786d92e..00000000 --- a/common/src/ether/evm/core/tests.rs +++ /dev/null @@ -1,1176 +0,0 @@ -#[cfg(test)] -mod test_vm { - - use std::str::FromStr; - - use ethers::prelude::U256; - - use crate::{ether::evm::core::vm::VM, utils::strings::decode_hex}; - - // creates a new test VM with calldata. - fn new_test_vm(bytecode: &str) -> VM { - VM::new( - String::from(bytecode), - String::from("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), - String::from("0x6865696d64616c6c000000000061646472657373"), - String::from("0x6865696d64616c6c0000000000006f726967696e"), - String::from("0x6865696d64616c6c00000000000063616c6c6572"), - 0, - 9999999999, - ) - } - - #[test] - fn test_stop_vm() { - let mut vm = new_test_vm("0x00"); - vm.execute(); - - assert!(vm.returndata.is_empty()); - assert_eq!(vm.exitcode, 10); - } - - #[test] - fn test_pc_out_of_range() { - let mut vm = new_test_vm("0x"); - vm.execute(); - - assert!(vm.returndata.is_empty()); - assert_eq!(vm.exitcode, 255); - } - - #[test] - fn test_add() { - let mut vm = new_test_vm( - "0x600a600a017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600101", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x14").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_mul() { - let mut vm = new_test_vm( - "0x600a600a027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600202", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").unwrap()); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") - .unwrap() - ); - } - - #[test] - fn test_sub() { - let mut vm = new_test_vm("0x600a600a036001600003"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - .unwrap() - ); - } - - #[test] - fn test_div() { - let mut vm = new_test_vm("0x600a600a046002600104"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_div_by_zero() { - let mut vm = new_test_vm("0x6002600004"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_sdiv() { - let mut vm = new_test_vm("0x600a600a057fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE05"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").unwrap()); - } - - #[test] - fn test_sdiv_by_zero() { - let mut vm = new_test_vm("0x6002600005"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_mod() { - let mut vm = new_test_vm("0x6003600a066005601106"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").unwrap()); - } - - #[test] - fn test_mod_by_zero() { - let mut vm = new_test_vm("0x6002600006"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_smod() { - let mut vm = new_test_vm("0x6003600a077ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff807"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") - .unwrap() - ); - } - - #[test] - fn test_smod_by_zero() { - let mut vm = new_test_vm("0x6002600007"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_addmod() { - let mut vm = new_test_vm("0x6008600a600a08600260027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); - } - - #[test] - fn test_addmod_by_zero() { - let mut vm = new_test_vm("0x60026000600008"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_mulmod() { - let mut vm = new_test_vm("0x6008600a600a09600c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); - } - - #[test] - fn test_mulmod_by_zero() { - let mut vm = new_test_vm("0x60026000600009"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_exp() { - let mut vm = new_test_vm("0x6002600a0a600260020a"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x04").unwrap()); - } - - #[test] - fn test_signextend() { - let mut vm = new_test_vm("0x60ff60000b607f60000b"); - vm.execute(); - - assert_eq!( - vm.stack.peek(1).value, - U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - .unwrap() - ); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x7f").unwrap()); - } - - #[test] - fn test_lt() { - let mut vm = new_test_vm("0x600a600910600a600a10"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_gt() { - let mut vm = new_test_vm("0x6009600a11600a600a10"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_slt() { - let mut vm = new_test_vm( - "0x60097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12600a600a12", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_sgt() { - let mut vm = new_test_vm( - "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600913600a600a13", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_eq() { - let mut vm = new_test_vm("0x600a600a14600a600514"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_iszero() { - let mut vm = new_test_vm("0x600015600a15"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_and() { - let mut vm = new_test_vm("0x600f600f16600060ff1600"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x0F").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_or() { - let mut vm = new_test_vm("0x600f60f01760ff60ff17"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").unwrap()); - } - - #[test] - fn test_xor() { - let mut vm = new_test_vm("0x600f60f01860ff60ff18"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_not() { - let mut vm = new_test_vm("0x600019"); - vm.execute(); - - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - .unwrap() - ); - } - - #[test] - fn test_byte() { - let mut vm = new_test_vm("0x60ff601f1a61ff00601e1a"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").unwrap()); - } - - #[test] - fn test_shl() { - let mut vm = new_test_vm( - "600160011b7fFF0000000000000000000000000000000000000000000000000000000000000060041b", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x02").unwrap()); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000") - .unwrap() - ); - } - - #[test] - fn test_shl_gt_255() { - let mut vm = new_test_vm( - "600161ffff1b7fFF0000000000000000000000000000000000000000000000000000000000000060041b", - ); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000") - .unwrap() - ); - } - - #[test] - fn test_shr() { - let mut vm = new_test_vm("600260011c60ff60041c"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0f").unwrap()); - } - - #[test] - fn test_shr_gt_256() { - let mut vm = new_test_vm("600261ffff1c61ffff60041c"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0fff").unwrap()); - } - - #[test] - fn test_shr_zero() { - let mut vm = new_test_vm("0x600060011c"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_sar() { - let mut vm = new_test_vm("600260011d"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); - } - - #[test] - fn test_sar_zero() { - let mut vm = new_test_vm("0x600060011d"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_sha3() { - let mut vm = new_test_vm( - "0x7fffffffff000000000000000000000000000000000000000000000000000000006000526004600020", - ); - vm.execute(); - - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0x29045A592007D0C246EF02C2223570DA9522D0CF0F73282C79A1BC8F0BB2C238") - .unwrap() - ); - } - - #[test] - fn test_address() { - let mut vm = new_test_vm("0x30"); - vm.execute(); - - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0x6865696d64616c6c000000000061646472657373").unwrap() - ); - } - - #[test] - fn test_calldataload() { - let mut vm = new_test_vm("600035601f35"); - vm.execute(); - - assert_eq!( - vm.stack.peek(1).value, - U256::from_str("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") - .unwrap() - ); - assert_eq!( - vm.stack.peek(0).value, - U256::from_str("0xFF00000000000000000000000000000000000000000000000000000000000000") - .unwrap() - ); - } - - #[test] - fn test_calldatasize() { - let mut vm = new_test_vm("0x36"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").unwrap()); - } - - #[test] - fn test_xdatacopy() { - // returndatacopy, calldatacopy, etc share same code. - let mut vm = new_test_vm("0x60ff6000600037"); - vm.execute(); - assert_eq!( - vm.memory.read(0, 32), - decode_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() - ); - } - - #[test] - fn test_codesize() { - let mut vm = new_test_vm("0x60ff60ff60ff60ff60ff38"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0B").unwrap()); - } - - #[test] - fn test_mload_mstore() { - let mut vm = new_test_vm("0x7f00000000000000000000000000000000000000000000000000000000000000FF600052600051600151"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff00").unwrap()); - } - - #[test] - fn test_mstore8() { - let mut vm = new_test_vm("0x60ff600053"); - vm.execute(); - - assert_eq!( - vm.memory.read(0, 32), - decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() - ) - } - - #[test] - fn test_msize() { - let mut vm = new_test_vm("0x60ff60005359"); - vm.execute(); - - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").unwrap()); - } - - #[test] - fn test_sload_sstore() { - let mut vm = new_test_vm("0x602e600055600054600154"); - vm.execute(); - - assert_eq!(vm.stack.peek(1).value, U256::from_str("0x2e").unwrap()); - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); - } - - #[test] - fn test_jump() { - let mut vm = new_test_vm("0x60fe56"); - vm.execute(); - - assert_eq!(U256::from(vm.instruction), U256::from_str("0xff").unwrap()); - } - - #[test] - fn test_jumpi() { - let mut vm = new_test_vm("0x600160fe57"); - vm.execute(); - - assert_eq!(U256::from(vm.instruction), U256::from_str("0xff").unwrap()); - - let mut vm = new_test_vm("0x600060fe5758"); - vm.execute(); - - assert_eq!(U256::from(vm.instruction), U256::from_str("0x07").unwrap()); - - // PC test - assert_eq!(vm.stack.peek(0).value, U256::from_str("0x07").unwrap()); - } - - #[test] - fn test_usdt_sim() { - // this execution should return the name of the USDT contract - let mut vm = VM::new( - String::from("608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032"), - String::from("06fdde03"), - String::from("0x6865696d64616c6c000000000061646472657373"), - String::from("0x6865696d64616c6c0000000000006f726967696e"), - String::from("0x6865696d64616c6c00000000000063616c6c6572"), - 0, - 999999999, - ); - - vm.execute(); - - assert_eq!( - vm.returndata, - vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 10, 85, 110, 105, 115, 119, 97, 112, 32, 86, 50, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - } -} - -#[cfg(test)] -mod test_opcode { - use ethers::types::U256; - - use crate::ether::evm::core::opcodes::*; - - #[test] - fn test_wrapping_opcodes() { - // wraps an ADD operation with 2 raw inputs - let add_operation_wrapped = WrappedOpcode::new( - 0x01, - vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], - ); - println!("{}", add_operation_wrapped); - - // wraps a CALLDATALOAD operation - let calldataload_wrapped = - WrappedOpcode::new(0x35, vec![WrappedInput::Opcode(add_operation_wrapped)]); - println!("{}", calldataload_wrapped); - } -} - -#[cfg(test)] -mod test_memory { - use crate::{ether::evm::core::memory::Memory, utils::strings::decode_hex}; - - #[test] - fn test_mstore_simple() { - let mut memory = Memory::new(); - memory.store( - 0, - 32, - &decode_hex("00000000000000000000000000000000000000000000000000000000000000ff") - .unwrap(), - ); - assert_eq!( - memory.memory, - decode_hex("00000000000000000000000000000000000000000000000000000000000000ff").unwrap() - ); - } - - #[test] - fn test_mstore_extend() { - let mut memory = Memory::new(); - memory.store(0, 32, &[0xff]); - assert_eq!( - memory.memory, - decode_hex("00000000000000000000000000000000000000000000000000000000000000ff").unwrap() - ); - } - - #[test] - fn test_mstore_offset() { - let mut memory = Memory::new(); - memory.store(4, 32, &[0xff]); - assert_eq!( - memory.memory, - decode_hex("0000000000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000").unwrap() - ); - } - - #[test] - fn test_mstore_large_nonstandard_offset() { - let mut memory = Memory::new(); - memory.store(34, 32, &[0xff]); - assert_eq!( - memory.memory, - decode_hex("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff000000000000000000000000000000000000000000000000000000000000").unwrap() - ); - } - - #[test] - fn test_mstore8() { - let mut memory = Memory::new(); - memory.store(0, 1, &[0xff]); - assert_eq!( - memory.memory, - decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() - ); - } - - #[test] - fn test_mstore_large_offser() { - let mut memory = Memory::new(); - memory.store(255, 32, &[0xff]); - assert_eq!( - memory.memory, - decode_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff00").unwrap() - ); - } - - #[test] - fn test_mload_simple() { - let mut memory = Memory::new(); - memory.store( - 0, - 32, - &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") - .unwrap(), - ); - assert_eq!( - memory.read(0, 32), - decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff").unwrap() - ); - } - - #[test] - fn test_mload_pad_one() { - let mut memory = Memory::new(); - memory.store( - 0, - 32, - &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") - .unwrap(), - ); - assert_eq!( - memory.read(1, 32), - decode_hex("223344556677889900aabbccddeeff11223344556677889900aabbccddeeff00").unwrap() - ); - } - - #[test] - fn test_mload_pad_large() { - let mut memory = Memory::new(); - memory.store( - 0, - 32, - &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") - .unwrap(), - ); - assert_eq!( - memory.read(31, 32), - decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() - ); - } - - #[test] - fn test_memory_cost() { - let mut memory = Memory::new(); - memory.store( - 0, - 32, - &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") - .unwrap(), - ); - assert_eq!(memory.memory_cost(), 3); - } - - #[test] - fn test_memory_cost_2() { - let mut memory = Memory::new(); - memory.store( - 32 * 32, - 32, - &decode_hex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff") - .unwrap(), - ); - assert_eq!(memory.memory_cost(), 101); - } - - #[test] - fn test_expansion_cost() { - let memory = Memory::new(); - assert_eq!(memory.expansion_cost(0, 32), 3); - } - - #[test] - fn test_expansion_cost_2() { - let memory = Memory::new(); - assert_eq!(memory.expansion_cost(32 * 32, 32), 101); - } -} - -#[cfg(test)] -mod test_storage { - use crate::ether::evm::core::storage::Storage; - - #[test] - fn test_sstore_sload() { - let mut storage = Storage::new(); - - storage.store( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - ); - assert_eq!( - storage.load([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1 - ]), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1 - ] - ); - - storage.store( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, - ], - [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, - 7, 8, 2, 1, - ], - ); - assert_eq!( - storage.load([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255 - ]), - [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, - 7, 8, 2, 1 - ], - ); - - assert_eq!( - storage.load([ - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255 - ]), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ] - ); - } - - #[test] - fn test_storage_access_cost_cold() { - let mut storage = Storage::new(); - assert_eq!( - storage.access_cost([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1 - ]), - 2100 - ); - } - - #[test] - fn test_storage_access_cost_warm() { - let mut storage = Storage::new(); - storage.load([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]); - assert_eq!( - storage.access_cost([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1 - ]), - 100 - ); - } - - #[test] - fn test_storage_storage_cost_cold() { - let mut storage = Storage::new(); - assert_eq!( - storage.storage_cost( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ] - ), - 22100 - ); - } - - #[test] - fn test_storage_storage_cost_cold_zero() { - let mut storage = Storage::new(); - assert_eq!( - storage.storage_cost( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 - ] - ), - 5000 - ); - } - - #[test] - fn test_storage_storage_cost_warm() { - let mut storage = Storage::new(); - storage.store( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - ); - assert_eq!( - storage.storage_cost( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ] - ), - 20100 - ); - } - - #[test] - fn test_storage_storage_cost_warm_zero() { - let mut storage = Storage::new(); - storage.store( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - ); - assert_eq!( - storage.storage_cost( - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1 - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 - ] - ), - 3000 - ); - } -} - -#[cfg(test)] -mod test_stack { - use crate::ether::evm::core::{opcodes::WrappedOpcode, stack::Stack}; - use ethers::types::U256; - - #[test] - fn test_push_pop() { - let mut stack = Stack::new(); - stack.push(U256::from(1), WrappedOpcode::default()); - stack.push(U256::from(2), WrappedOpcode::default()); - assert_eq!(stack.pop().value, U256::from(2)); - assert_eq!(stack.pop().value, U256::from(1)); - assert!(stack.is_empty()); - } - - #[test] - fn test_pop_n() { - let mut stack = Stack::new(); - stack.push(U256::from(1), WrappedOpcode::default()); - stack.push(U256::from(2), WrappedOpcode::default()); - stack.push(U256::from(3), WrappedOpcode::default()); - let values = stack.pop_n(2); - assert_eq!(values.len(), 2); - assert_eq!(values[0].value, U256::from(3)); - assert_eq!(values[1].value, U256::from(2)); - assert_eq!(stack.pop().value, U256::from(1)); - assert!(stack.is_empty()); - } - - #[test] - fn test_swap() { - let mut stack = Stack::new(); - stack.push(U256::from(1), WrappedOpcode::default()); - stack.push(U256::from(2), WrappedOpcode::default()); - stack.push(U256::from(3), WrappedOpcode::default()); - assert!(stack.swap(1)); - assert_eq!(stack.pop().value, U256::from(2)); - assert_eq!(stack.pop().value, U256::from(3)); - assert_eq!(stack.pop().value, U256::from(1)); - assert!(stack.is_empty()); - assert!(!stack.swap(1)); - } - - #[test] - fn test_dup() { - let mut stack = Stack::new(); - stack.push(U256::from(1), WrappedOpcode::default()); - stack.push(U256::from(2), WrappedOpcode::default()); - stack.push(U256::from(3), WrappedOpcode::default()); - assert!(stack.dup(1)); - assert_eq!(stack.pop().value, U256::from(3)); - assert_eq!(stack.pop().value, U256::from(3)); - assert_eq!(stack.pop().value, U256::from(2)); - assert_eq!(stack.pop().value, U256::from(1)); - assert!(stack.is_empty()); - } - - #[test] - fn test_peek() { - let mut stack = Stack::new(); - stack.push(U256::from(1), WrappedOpcode::default()); - stack.push(U256::from(2), WrappedOpcode::default()); - stack.push(U256::from(3), WrappedOpcode::default()); - assert_eq!(stack.peek(0).value, U256::from(3)); - assert_eq!(stack.peek(1).value, U256::from(2)); - assert_eq!(stack.peek(2).value, U256::from(1)); - assert_eq!(stack.peek(3).value, U256::from(0)); - } -} - -#[cfg(test)] -mod test_types { - use ethers::abi::ParamType; - - use crate::ether::evm::core::types::parse_function_parameters; - - #[test] - fn test_simple_signature() { - let solidity_type = "test(uint256)".to_string(); - let param_type = parse_function_parameters(&solidity_type); - assert_eq!(param_type, Some(vec![ParamType::Uint(256)])); - } - - #[test] - fn test_multiple_signature() { - let solidity_type = "test(uint256,string)".to_string(); - let param_type = parse_function_parameters(&solidity_type); - assert_eq!(param_type, Some(vec![ParamType::Uint(256), ParamType::String])); - } - - #[test] - fn test_array_signature() { - let solidity_type = "test(uint256,string[],uint256)"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ - ParamType::Uint(256), - ParamType::Array(Box::new(ParamType::String)), - ParamType::Uint(256) - ]) - ); - } - - #[test] - fn test_array_fixed_signature() { - let solidity_type = "test(uint256,string[2],uint256)"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ - ParamType::Uint(256), - ParamType::FixedArray(Box::new(ParamType::String), 2), - ParamType::Uint(256) - ]) - ); - } - - #[test] - fn test_complex_signature() { - let solidity_type = - "test(uint256,string,(address,address,uint24,address,uint256,uint256,uint256,uint160))"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ - ParamType::Uint(256), - ParamType::String, - ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Uint(24), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(160) - ]) - ]) - ); - } - - #[test] - fn test_tuple_signature() { - let solidity_type = - "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Uint(24), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(160) - ])]) - ); - } - - #[test] - fn test_tuple_array_signature() { - let solidity_type = - "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)[])"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ParamType::Array(Box::new(ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Uint(24), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(160) - ])))]) - ); - } - - #[test] - fn test_tuple_fixedarray_signature() { - let solidity_type = - "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)[2])"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ParamType::FixedArray( - Box::new(ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Uint(24), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(160) - ])), - 2 - )]) - ); - } - - #[test] - fn test_nested_tuple_signature() { - let solidity_type = "exactInputSingle((address,address,uint24,address,uint256,(uint256,uint256)[],uint160))"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Uint(24), - ParamType::Address, - ParamType::Uint(256), - ParamType::Array(Box::new(ParamType::Tuple(vec![ - ParamType::Uint(256), - ParamType::Uint(256) - ]))), - ParamType::Uint(160) - ])]) - ); - } - - #[test] - fn test_seaport_fulfill_advanced_order() { - let solidity_type = "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8,address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32,uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32,address)"; - let param_type = parse_function_parameters(solidity_type); - assert_eq!( - param_type, - Some(vec![ - ParamType::Tuple(vec![ - ParamType::Tuple(vec![ - ParamType::Address, - ParamType::Address, - ParamType::Array(Box::new(ParamType::Tuple(vec![ - ParamType::Uint(8), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256) - ]))), - ParamType::Array(Box::new(ParamType::Tuple(vec![ - ParamType::Uint(8), - ParamType::Address, - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Address - ]))), - ParamType::Uint(8), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::FixedBytes(32), - ParamType::Uint(256), - ParamType::FixedBytes(32), - ParamType::Uint(256) - ]), - ParamType::Uint(120), - ParamType::Uint(120), - ParamType::Bytes, - ParamType::Bytes - ]), - ParamType::Array(Box::new(ParamType::Tuple(vec![ - ParamType::Uint(256), - ParamType::Uint(8), - ParamType::Uint(256), - ParamType::Uint(256), - ParamType::Array(Box::new(ParamType::FixedBytes(32))) - ]))), - ParamType::FixedBytes(32), - ParamType::Address - ]) - ); - } -} diff --git a/common/src/ether/evm/core/types.rs b/common/src/ether/evm/core/types.rs index b3ef1524..317257c2 100644 --- a/common/src/ether/evm/core/types.rs +++ b/common/src/ether/evm/core/types.rs @@ -5,7 +5,17 @@ use crate::{constants::TYPE_CAST_REGEX, utils::strings::find_balanced_encapsulat use super::vm::Instruction; -// decode a string into an ethereum type +/// Parse function parameters [`ParamType`]s from a function signature. +/// +/// ``` +/// use heimdall_common::ether::evm::core::types::parse_function_parameters; +/// use ethers::abi::ParamType; +/// +/// let function_signature = "foo(uint256,uint256)"; +/// let function_parameters = parse_function_parameters(function_signature).unwrap(); +/// +/// assert_eq!(function_parameters, vec![ParamType::Uint(256), ParamType::Uint(256)]); +/// ``` pub fn parse_function_parameters(function_signature: &str) -> Option> { // remove the function name from the signature, only keep the parameters let (start, end, valid) = find_balanced_encapsulator(function_signature, ('(', ')')); @@ -19,7 +29,8 @@ pub fn parse_function_parameters(function_signature: &str) -> Option Option> { let mut types = Vec::new(); @@ -148,6 +159,8 @@ fn extract_types_from_string(string: &str) -> Option> { } } +/// A helper function used by [`extract_types_from_string`] to check if the first type in a string +/// is a tuple. fn is_first_type_tuple(string: &str) -> bool { // split by first comma let split = string.splitn(2, ',').collect::>(); @@ -156,6 +169,8 @@ fn is_first_type_tuple(string: &str) -> bool { split[0].starts_with('(') } +/// A helper function used by [`extract_types_from_string`] that converts a string type to a +/// ParamType. For example, "address" will be converted to [`ParamType::Address`]. fn to_type(string: &str) -> ParamType { let is_array = string.ends_with(']'); @@ -210,7 +225,7 @@ fn to_type(string: &str) -> ParamType { } } -// returns a vec of beautified types for a given vec of tokens +/// A helper function used by the decode module to pretty format decoded tokens. pub fn display(inputs: Vec, prefix: &str) -> Vec { let mut output = Vec::new(); let prefix = prefix.to_string(); @@ -269,7 +284,9 @@ pub fn display(inputs: Vec, prefix: &str) -> Vec { output } -// converts a bit mask into it's potential types +/// Convert a bitwise masking operation to a tuple containing: \ +/// 1. The size of the type being masked \ +/// 2. Potential types that the type being masked could be. pub fn convert_bitmask(instruction: Instruction) -> (usize, Vec) { let mask = instruction.output_operations[0].clone(); @@ -296,6 +313,17 @@ pub fn convert_bitmask(instruction: Instruction) -> (usize, Vec) { byte_size_to_type(type_byte_size) } +/// Given a byte size, return a tuple containing: \ +/// 1. The byte size \ +/// 2. Potential types that the byte size could be. +/// +/// ``` +/// use heimdall_common::ether::evm::core::types::byte_size_to_type; +/// +/// let (byte_size, potential_types) = byte_size_to_type(1); +/// assert_eq!(byte_size, 1); +/// assert_eq!(potential_types, vec!["bool".to_string(), "uint8".to_string(), "bytes1".to_string(), "int8".to_string()]); +/// ``` pub fn byte_size_to_type(byte_size: usize) -> (usize, Vec) { let mut potential_types = Vec::new(); @@ -314,6 +342,7 @@ pub fn byte_size_to_type(byte_size: usize) -> (usize, Vec) { (byte_size, potential_types) } +/// Given a string (typically a line of decompiled source code), extract a type cast if one exists. pub fn find_cast(line: &str) -> (usize, usize, Option) { // find the start of the cast match TYPE_CAST_REGEX.find(line).expect("Failed to find type cast.") { @@ -329,3 +358,212 @@ pub fn find_cast(line: &str) -> (usize, usize, Option) { None => (0, 0, None), } } + +#[cfg(test)] +mod tests { + use ethers::abi::ParamType; + + use crate::ether::evm::core::types::parse_function_parameters; + + #[test] + fn test_simple_signature() { + let solidity_type = "test(uint256)".to_string(); + let param_type = parse_function_parameters(&solidity_type); + assert_eq!(param_type, Some(vec![ParamType::Uint(256)])); + } + + #[test] + fn test_multiple_signature() { + let solidity_type = "test(uint256,string)".to_string(); + let param_type = parse_function_parameters(&solidity_type); + assert_eq!(param_type, Some(vec![ParamType::Uint(256), ParamType::String])); + } + + #[test] + fn test_array_signature() { + let solidity_type = "test(uint256,string[],uint256)"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ + ParamType::Uint(256), + ParamType::Array(Box::new(ParamType::String)), + ParamType::Uint(256) + ]) + ); + } + + #[test] + fn test_array_fixed_signature() { + let solidity_type = "test(uint256,string[2],uint256)"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ + ParamType::Uint(256), + ParamType::FixedArray(Box::new(ParamType::String), 2), + ParamType::Uint(256) + ]) + ); + } + + #[test] + fn test_complex_signature() { + let solidity_type = + "test(uint256,string,(address,address,uint24,address,uint256,uint256,uint256,uint160))"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ + ParamType::Uint(256), + ParamType::String, + ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Uint(24), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(160) + ]) + ]) + ); + } + + #[test] + fn test_tuple_signature() { + let solidity_type = + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Uint(24), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(160) + ])]) + ); + } + + #[test] + fn test_tuple_array_signature() { + let solidity_type = + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)[])"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Uint(24), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(160) + ])))]) + ); + } + + #[test] + fn test_tuple_fixedarray_signature() { + let solidity_type = + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)[2])"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ParamType::FixedArray( + Box::new(ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Uint(24), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(160) + ])), + 2 + )]) + ); + } + + #[test] + fn test_nested_tuple_signature() { + let solidity_type = "exactInputSingle((address,address,uint24,address,uint256,(uint256,uint256)[],uint160))"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Uint(24), + ParamType::Address, + ParamType::Uint(256), + ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Uint(256), + ParamType::Uint(256) + ]))), + ParamType::Uint(160) + ])]) + ); + } + + #[test] + fn test_seaport_fulfill_advanced_order() { + let solidity_type = "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8,address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32,uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32,address)"; + let param_type = parse_function_parameters(solidity_type); + assert_eq!( + param_type, + Some(vec![ + ParamType::Tuple(vec![ + ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Address, + ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Uint(8), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256) + ]))), + ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Uint(8), + ParamType::Address, + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Address + ]))), + ParamType::Uint(8), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::FixedBytes(32), + ParamType::Uint(256), + ParamType::FixedBytes(32), + ParamType::Uint(256) + ]), + ParamType::Uint(120), + ParamType::Uint(120), + ParamType::Bytes, + ParamType::Bytes + ]), + ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Uint(256), + ParamType::Uint(8), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Array(Box::new(ParamType::FixedBytes(32))) + ]))), + ParamType::FixedBytes(32), + ParamType::Address + ]) + ); + } +} diff --git a/common/src/ether/evm/core/vm.rs b/common/src/ether/evm/core/vm.rs index 13d63a2d..93f83d81 100644 --- a/common/src/ether/evm/core/vm.rs +++ b/common/src/ether/evm/core/vm.rs @@ -14,6 +14,9 @@ use crate::{ use super::{log::Log, memory::Memory, stack::Stack, storage::Storage}; +/// The [`VM`] struct represents an EVM instance. \ +/// It contains the EVM's [`Stack`], [`Memory`], [`Storage`], and other state variables needed to +/// emulate EVM execution. #[derive(Clone, Debug)] pub struct VM { pub stack: Stack, @@ -35,8 +38,9 @@ pub struct VM { pub address_access_set: HashSet, } +/// [`ExecutionResult`] is the result of a single contract execution. #[derive(Clone, Debug)] -pub struct Result { +pub struct ExecutionResult { pub gas_used: u128, pub gas_remaining: u128, pub returndata: Vec, @@ -46,6 +50,8 @@ pub struct Result { pub instruction: u128, } +/// [`State`] is the state of the EVM after executing a single instruction. It is returned by the +/// [`VM::step`] function, and is used by heimdall for tracing contract execution. #[derive(Clone, Debug)] pub struct State { pub last_instruction: Instruction, @@ -57,6 +63,9 @@ pub struct State { pub events: Vec, } +/// [`Instruction`] is a single EVM instruction. It is returned by the [`VM::step`] function, and +/// contains necessary tracing information, such as the opcode executed, it's inputs and outputs, as +/// well as their parent operations. #[derive(Clone, Debug)] pub struct Instruction { pub instruction: u128, @@ -69,7 +78,23 @@ pub struct Instruction { } impl VM { - // Creates a new VM instance + /// Creates a new [`VM`] instance with the given bytecode, calldata, address, origin, caller, + /// value, and gas limit. + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// ``` pub fn new( bytecode: String, calldata: String, @@ -89,7 +114,7 @@ impl VM { address: decode_hex(&address.replacen("0x", "", 1)).unwrap(), origin: decode_hex(&origin.replacen("0x", "", 1)).unwrap(), caller: decode_hex(&caller.replacen("0x", "", 1)).unwrap(), - value: value, + value, gas_remaining: gas_limit.max(21000) - 21000, gas_used: 21000, events: Vec::new(), @@ -100,16 +125,59 @@ impl VM { } } - // exits the current VM state with the given exit code and return data + /// Exits current execution with the given code and returndata. + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.exit(0xff, Vec::new()); + /// assert_eq!(vm.exitcode, 0xff); + /// ``` pub fn exit(&mut self, code: u128, returndata: Vec) { self.exitcode = code; self.returndata = returndata; } - // consumes the given amount of gas, exiting if there is not enough remaining + /// Consume gas units, halting execution if out of gas + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.consume_gas(100); + /// assert_eq!(vm.gas_remaining, 999999999999978900); + /// + /// vm.consume_gas(1000000000000000000); + /// assert_eq!(vm.gas_remaining, 0); + /// assert_eq!(vm.exitcode, 9); + /// ``` pub fn consume_gas(&mut self, amount: u128) -> bool { // REVERT if out of gas if amount > self.gas_remaining { + self.gas_used += self.gas_remaining; + self.gas_remaining = 0; + self.exit(9, Vec::new()); return false } @@ -118,7 +186,26 @@ impl VM { true } - // Steps to the next PC and executes the instruction + /// Executes the next instruction in the bytecode. Returns information about the instruction + /// executed. + /// + /// ```no_run + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// // vm._step(); // 0x00 EXIT + /// // assert_eq!(vm.exitcode, 10); + /// ``` fn _step(&mut self) -> Instruction { // sanity check if self.bytecode.len() < self.instruction as usize { @@ -140,7 +227,7 @@ impl VM { self.instruction += 1; // add the opcode to the trace - let opcode_details = crate::ether::evm::core::opcodes::opcode(opcode); + let opcode_details = Opcode::new(opcode); let input_frames = self.stack.peek_n(opcode_details.inputs as usize); let input_operations = input_frames.iter().map(|x| x.operation.clone()).collect::>(); @@ -148,21 +235,7 @@ impl VM { // Consume the minimum gas for the opcode let gas_cost = opcode_details.mingas; - match self.consume_gas(gas_cost.into()) { - true => {} - false => { - self.exit(9, Vec::new()); - return Instruction { - instruction: last_instruction, - opcode: opcode, - opcode_details: Some(opcode_details), - inputs: inputs, - outputs: Vec::new(), - input_operations: input_operations, - output_operations: Vec::new(), - } - } - } + self.consume_gas(gas_cost.into()); // convert inputs to WrappedInputs let wrapped_inputs = input_operations @@ -178,11 +251,11 @@ impl VM { self.exit(10, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -620,11 +693,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -659,11 +732,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -674,11 +747,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -757,11 +830,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -802,11 +875,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -817,11 +890,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -832,11 +905,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -879,11 +952,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -894,11 +967,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -909,11 +982,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -970,11 +1043,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -985,11 +1058,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1031,11 +1104,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1046,11 +1119,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1119,11 +1192,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1150,11 +1223,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1179,11 +1252,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1230,11 +1303,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1247,11 +1320,11 @@ impl VM { self.exit(790, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } else { @@ -1271,11 +1344,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1290,11 +1363,11 @@ impl VM { self.exit(790, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } else { @@ -1378,11 +1451,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1393,11 +1466,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1454,11 +1527,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1469,11 +1542,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1524,11 +1597,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1539,11 +1612,11 @@ impl VM { self.exit(2, Vec::new()); return Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, + inputs, outputs: Vec::new(), - input_operations: input_operations, + input_operations, output_operations: Vec::new(), } } @@ -1566,16 +1639,35 @@ impl VM { Instruction { instruction: last_instruction, - opcode: opcode, + opcode, opcode_details: Some(opcode_details), - inputs: inputs, - outputs: outputs, - input_operations: input_operations, - output_operations: output_operations, + inputs, + outputs, + input_operations, + output_operations, } } - // Executes the next instruction in the VM and returns a snapshot its the state + /// Executes the next instruction in the VM and returns a snapshot of the VM state after + /// executing the instruction + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.step(); // 0x00 EXIT + /// assert_eq!(vm.exitcode, 10); + /// ``` pub fn step(&mut self) -> State { let instruction = self._step(); @@ -1590,7 +1682,25 @@ impl VM { } } - // View the next n instructions without executing them + /// View the next n instructions without executing them + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.peek(1); // 0x00 EXIT (not executed) + /// assert_eq!(vm.exitcode, 255); + /// ``` pub fn peek(&mut self, n: usize) -> Vec { let mut states = Vec::new(); let mut vm_clone = self.clone(); @@ -1608,7 +1718,28 @@ impl VM { states } - // Resets the VM state for a new execution + /// Resets the VM state for a new execution + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.step(); // 0x00 EXIT (not executed) + /// assert_eq!(vm.exitcode, 10); + /// + /// vm.reset(); + /// assert_eq!(vm.exitcode, 255); + /// ``` pub fn reset(&mut self) { self.stack = Stack::new(); self.memory = Memory::new(); @@ -1621,8 +1752,26 @@ impl VM { self.timestamp = Instant::now(); } - // Executes the code until finished - pub fn execute(&mut self) -> Result { + /// Executes the code until finished + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.execute(); // 0x00 EXIT (not executed) + /// assert_eq!(vm.exitcode, 10); + /// ``` + pub fn execute(&mut self) -> ExecutionResult { while self.bytecode.len() >= self.instruction as usize { self.step(); @@ -1631,7 +1780,7 @@ impl VM { } } - Result { + ExecutionResult { gas_used: self.gas_used, gas_remaining: self.gas_remaining, returndata: self.returndata.to_owned(), @@ -1642,8 +1791,26 @@ impl VM { } } - // Executes provided calldata until finished - pub fn call(&mut self, calldata: &str, value: u128) -> Result { + /// Executes provided calldata until finished + /// + /// ``` + /// use heimdall_common::ether::evm::core::vm::VM; + /// + /// let bytecode = "0x00"; + /// let mut vm = VM::new( + /// bytecode.to_string(), + /// "0x".to_string(), + /// "0x0000000000000000000000000000000000000000".to_string(), + /// "0x0000000000000000000000000000000000000001".to_string(), + /// "0x0000000000000000000000000000000000000002".to_string(), + /// 0, + /// 1000000000000000000, + /// ); + /// + /// vm.call("0x", 0); + /// assert_eq!(vm.exitcode, 10); + /// ``` + pub fn call(&mut self, calldata: &str, value: u128) -> ExecutionResult { // reset the VM temp state self.reset(); self.calldata = decode_hex(&calldata.replacen("0x", "", 1)).unwrap(); @@ -1652,3 +1819,545 @@ impl VM { self.execute() } } + +#[cfg(test)] +mod tests { + + use std::str::FromStr; + + use ethers::prelude::U256; + + use crate::{ether::evm::core::vm::VM, utils::strings::decode_hex}; + + // creates a new test VM with calldata. + fn new_test_vm(bytecode: &str) -> VM { + VM::new( + String::from(bytecode), + String::from("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + String::from("0x6865696d64616c6c000000000061646472657373"), + String::from("0x6865696d64616c6c0000000000006f726967696e"), + String::from("0x6865696d64616c6c00000000000063616c6c6572"), + 0, + 9999999999, + ) + } + + #[test] + fn test_stop_vm() { + let mut vm = new_test_vm("0x00"); + vm.execute(); + + assert!(vm.returndata.is_empty()); + assert_eq!(vm.exitcode, 10); + } + + #[test] + fn test_pc_out_of_range() { + let mut vm = new_test_vm("0x"); + vm.execute(); + + assert!(vm.returndata.is_empty()); + assert_eq!(vm.exitcode, 255); + } + + #[test] + fn test_add() { + let mut vm = new_test_vm( + "0x600a600a017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600101", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x14").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_mul() { + let mut vm = new_test_vm( + "0x600a600a027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600202", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").unwrap()); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") + .unwrap() + ); + } + + #[test] + fn test_sub() { + let mut vm = new_test_vm("0x600a600a036001600003"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + ); + } + + #[test] + fn test_div() { + let mut vm = new_test_vm("0x600a600a046002600104"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_div_by_zero() { + let mut vm = new_test_vm("0x6002600004"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_sdiv() { + let mut vm = new_test_vm("0x600a600a057fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE05"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").unwrap()); + } + + #[test] + fn test_sdiv_by_zero() { + let mut vm = new_test_vm("0x6002600005"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_mod() { + let mut vm = new_test_vm("0x6003600a066005601106"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").unwrap()); + } + + #[test] + fn test_mod_by_zero() { + let mut vm = new_test_vm("0x6002600006"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_smod() { + let mut vm = new_test_vm("0x6003600a077ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff807"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") + .unwrap() + ); + } + + #[test] + fn test_smod_by_zero() { + let mut vm = new_test_vm("0x6002600007"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_addmod() { + let mut vm = new_test_vm("0x6008600a600a08600260027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); + } + + #[test] + fn test_addmod_by_zero() { + let mut vm = new_test_vm("0x60026000600008"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_mulmod() { + let mut vm = new_test_vm("0x6008600a600a09600c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); + } + + #[test] + fn test_mulmod_by_zero() { + let mut vm = new_test_vm("0x60026000600009"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_exp() { + let mut vm = new_test_vm("0x6002600a0a600260020a"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x04").unwrap()); + } + + #[test] + fn test_signextend() { + let mut vm = new_test_vm("0x60ff60000b607f60000b"); + vm.execute(); + + assert_eq!( + vm.stack.peek(1).value, + U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + ); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x7f").unwrap()); + } + + #[test] + fn test_lt() { + let mut vm = new_test_vm("0x600a600910600a600a10"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_gt() { + let mut vm = new_test_vm("0x6009600a11600a600a10"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_slt() { + let mut vm = new_test_vm( + "0x60097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12600a600a12", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_sgt() { + let mut vm = new_test_vm( + "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600913600a600a13", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_eq() { + let mut vm = new_test_vm("0x600a600a14600a600514"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_iszero() { + let mut vm = new_test_vm("0x600015600a15"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_and() { + let mut vm = new_test_vm("0x600f600f16600060ff1600"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x0F").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_or() { + let mut vm = new_test_vm("0x600f60f01760ff60ff17"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").unwrap()); + } + + #[test] + fn test_xor() { + let mut vm = new_test_vm("0x600f60f01860ff60ff18"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_not() { + let mut vm = new_test_vm("0x600019"); + vm.execute(); + + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + .unwrap() + ); + } + + #[test] + fn test_byte() { + let mut vm = new_test_vm("0x60ff601f1a61ff00601e1a"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").unwrap()); + } + + #[test] + fn test_shl() { + let mut vm = new_test_vm( + "600160011b7fFF0000000000000000000000000000000000000000000000000000000000000060041b", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x02").unwrap()); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000") + .unwrap() + ); + } + + #[test] + fn test_shl_gt_255() { + let mut vm = new_test_vm( + "600161ffff1b7fFF0000000000000000000000000000000000000000000000000000000000000060041b", + ); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000") + .unwrap() + ); + } + + #[test] + fn test_shr() { + let mut vm = new_test_vm("600260011c60ff60041c"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0f").unwrap()); + } + + #[test] + fn test_shr_gt_256() { + let mut vm = new_test_vm("600261ffff1c61ffff60041c"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0fff").unwrap()); + } + + #[test] + fn test_shr_zero() { + let mut vm = new_test_vm("0x600060011c"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_sar() { + let mut vm = new_test_vm("600260011d"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").unwrap()); + } + + #[test] + fn test_sar_zero() { + let mut vm = new_test_vm("0x600060011d"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_sha3() { + let mut vm = new_test_vm( + "0x7fffffffff000000000000000000000000000000000000000000000000000000006000526004600020", + ); + vm.execute(); + + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0x29045A592007D0C246EF02C2223570DA9522D0CF0F73282C79A1BC8F0BB2C238") + .unwrap() + ); + } + + #[test] + fn test_address() { + let mut vm = new_test_vm("0x30"); + vm.execute(); + + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0x6865696d64616c6c000000000061646472657373").unwrap() + ); + } + + #[test] + fn test_calldataload() { + let mut vm = new_test_vm("600035601f35"); + vm.execute(); + + assert_eq!( + vm.stack.peek(1).value, + U256::from_str("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + .unwrap() + ); + assert_eq!( + vm.stack.peek(0).value, + U256::from_str("0xFF00000000000000000000000000000000000000000000000000000000000000") + .unwrap() + ); + } + + #[test] + fn test_calldatasize() { + let mut vm = new_test_vm("0x36"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").unwrap()); + } + + #[test] + fn test_xdatacopy() { + // returndatacopy, calldatacopy, etc share same code. + let mut vm = new_test_vm("0x60ff6000600037"); + vm.execute(); + assert_eq!( + vm.memory.read(0, 32), + decode_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ); + } + + #[test] + fn test_codesize() { + let mut vm = new_test_vm("0x60ff60ff60ff60ff60ff38"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0B").unwrap()); + } + + #[test] + fn test_mload_mstore() { + let mut vm = new_test_vm("0x7f00000000000000000000000000000000000000000000000000000000000000FF600052600051600151"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff00").unwrap()); + } + + #[test] + fn test_mstore8() { + let mut vm = new_test_vm("0x60ff600053"); + vm.execute(); + + assert_eq!( + vm.memory.read(0, 32), + decode_hex("ff00000000000000000000000000000000000000000000000000000000000000").unwrap() + ) + } + + #[test] + fn test_msize() { + let mut vm = new_test_vm("0x60ff60005359"); + vm.execute(); + + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").unwrap()); + } + + #[test] + fn test_sload_sstore() { + let mut vm = new_test_vm("0x602e600055600054600154"); + vm.execute(); + + assert_eq!(vm.stack.peek(1).value, U256::from_str("0x2e").unwrap()); + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").unwrap()); + } + + #[test] + fn test_jump() { + let mut vm = new_test_vm("0x60fe56"); + vm.execute(); + + assert_eq!(U256::from(vm.instruction), U256::from_str("0xff").unwrap()); + } + + #[test] + fn test_jumpi() { + let mut vm = new_test_vm("0x600160fe57"); + vm.execute(); + + assert_eq!(U256::from(vm.instruction), U256::from_str("0xff").unwrap()); + + let mut vm = new_test_vm("0x600060fe5758"); + vm.execute(); + + assert_eq!(U256::from(vm.instruction), U256::from_str("0x07").unwrap()); + + // PC test + assert_eq!(vm.stack.peek(0).value, U256::from_str("0x07").unwrap()); + } + + #[test] + fn test_usdt_sim() { + // this execution should return the name of the USDT contract + let mut vm = VM::new( + String::from("608060405234801561001057600080fd5b50600436106101b95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146105da578063d505accf146105e2578063dd62ed3e14610640578063fff6cae91461067b576101b9565b8063ba9a7a5614610597578063bc25cf771461059f578063c45a0155146105d2576101b9565b80637ecebe00116100d35780637ecebe00146104d757806389afcb441461050a57806395d89b4114610556578063a9059cbb1461055e576101b9565b80636a6278421461046957806370a082311461049c5780637464fc3d146104cf576101b9565b806323b872dd116101665780633644e515116101405780633644e51514610416578063485cc9551461041e5780635909c0d5146104595780635a3d549314610461576101b9565b806323b872dd146103ad57806330adf81f146103f0578063313ce567146103f8576101b9565b8063095ea7b311610197578063095ea7b3146103155780630dfe16811461036257806318160ddd14610393576101b9565b8063022c0d9f146101be57806306fdde03146102595780630902f1ac146102d6575b600080fd5b610257600480360360808110156101d457600080fd5b81359160208101359173ffffffffffffffffffffffffffffffffffffffff604083013516919081019060808101606082013564010000000081111561021857600080fd5b82018360208201111561022a57600080fd5b8035906020019184600183028401116401000000008311171561024c57600080fd5b509092509050610683565b005b610261610d57565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561029b578181015183820152602001610283565b50505050905090810190601f1680156102c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102de610d90565b604080516dffffffffffffffffffffffffffff948516815292909316602083015263ffffffff168183015290519081900360600190f35b61034e6004803603604081101561032b57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610de5565b604080519115158252519081900360200190f35b61036a610dfc565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61039b610e18565b60408051918252519081900360200190f35b61034e600480360360608110156103c357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610e1e565b61039b610efd565b610400610f21565b6040805160ff9092168252519081900360200190f35b61039b610f26565b6102576004803603604081101561043457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610f2c565b61039b611005565b61039b61100b565b61039b6004803603602081101561047f57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611011565b61039b600480360360208110156104b257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113cb565b61039b6113dd565b61039b600480360360208110156104ed57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113e3565b61053d6004803603602081101561052057600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166113f5565b6040805192835260208301919091528051918290030190f35b610261611892565b61034e6004803603604081101561057457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356118cb565b61039b6118d8565b610257600480360360208110156105b557600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166118de565b61036a611ad4565b61036a611af0565b610257600480360360e08110156105f857600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c00135611b0c565b61039b6004803603604081101561065657600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516611dd8565b610257611df5565b600c546001146106f457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55841515806107075750600084115b61075c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180612b2f6025913960400191505060405180910390fd5b600080610767610d90565b5091509150816dffffffffffffffffffffffffffff168710801561079a5750806dffffffffffffffffffffffffffff1686105b6107ef576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b786021913960400191505060405180910390fd5b600654600754600091829173ffffffffffffffffffffffffffffffffffffffff91821691908116908916821480159061085457508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b6108bf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f556e697377617056323a20494e56414c49445f544f0000000000000000000000604482015290519081900360640190fd5b8a156108d0576108d0828a8d611fdb565b89156108e1576108e1818a8c611fdb565b86156109c3578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109aa57600080fd5b505af11580156109be573d6000803e3d6000fd5b505050505b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8416916370a08231916024808301926020929190829003018186803b158015610a2f57600080fd5b505afa158015610a43573d6000803e3d6000fd5b505050506040513d6020811015610a5957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191955073ffffffffffffffffffffffffffffffffffffffff8316916370a0823191602480820192602092909190829003018186803b158015610acb57600080fd5b505afa158015610adf573d6000803e3d6000fd5b505050506040513d6020811015610af557600080fd5b5051925060009150506dffffffffffffffffffffffffffff85168a90038311610b1f576000610b35565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610b59576000610b6f565b89856dffffffffffffffffffffffffffff160383035b90506000821180610b805750600081115b610bd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180612b546024913960400191505060405180910390fd5b6000610c09610beb84600363ffffffff6121e816565b610bfd876103e863ffffffff6121e816565b9063ffffffff61226e16565b90506000610c21610beb84600363ffffffff6121e816565b9050610c59620f4240610c4d6dffffffffffffffffffffffffffff8b8116908b1663ffffffff6121e816565b9063ffffffff6121e816565b610c69838363ffffffff6121e816565b1015610cd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f556e697377617056323a204b0000000000000000000000000000000000000000604482015290519081900360640190fd5b5050610ce4848488886122e0565b60408051838152602081018390528082018d9052606081018c9052905173ffffffffffffffffffffffffffffffffffffffff8b169133917fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229181900360800190a350506001600c55505050505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6008546dffffffffffffffffffffffffffff808216926e0100000000000000000000000000008304909116917c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6000610df233848461259c565b5060015b92915050565b60065473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b73ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14610ee85773ffffffffffffffffffffffffffffffffffffffff84166000908152600260209081526040808320338452909152902054610eb6908363ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602090815260408083203384529091529020555b610ef384848461260b565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b601281565b60035481565b60055473ffffffffffffffffffffffffffffffffffffffff163314610fb257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e697377617056323a20464f5242494444454e000000000000000000000000604482015290519081900360640190fd5b6006805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560078054929093169116179055565b60095481565b600a5481565b6000600c5460011461108457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611094610d90565b50600654604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905193955091935060009273ffffffffffffffffffffffffffffffffffffffff909116916370a08231916024808301926020929190829003018186803b15801561110e57600080fd5b505afa158015611122573d6000803e3d6000fd5b505050506040513d602081101561113857600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905192935060009273ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d60208110156111db57600080fd5b505190506000611201836dffffffffffffffffffffffffffff871663ffffffff61226e16565b90506000611225836dffffffffffffffffffffffffffff871663ffffffff61226e16565b9050600061123387876126ec565b600054909150806112705761125c6103e8610bfd611257878763ffffffff6121e816565b612878565b985061126b60006103e86128ca565b6112cd565b6112ca6dffffffffffffffffffffffffffff8916611294868463ffffffff6121e816565b8161129b57fe5b046dffffffffffffffffffffffffffff89166112bd868563ffffffff6121e816565b816112c457fe5b0461297a565b98505b60008911611326576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612bc16028913960400191505060405180910390fd5b6113308a8a6128ca565b61133c86868a8a6122e0565b811561137e5760085461137a906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b6040805185815260208101859052815133927f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f928290030190a250506001600c5550949695505050505050565b60016020526000908152604090205481565b600b5481565b60046020526000908152604090205481565b600080600c5460011461146957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c81905580611479610d90565b50600654600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905194965092945073ffffffffffffffffffffffffffffffffffffffff9182169391169160009184916370a08231916024808301926020929190829003018186803b1580156114fb57600080fd5b505afa15801561150f573d6000803e3d6000fd5b505050506040513d602081101561152557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191925060009173ffffffffffffffffffffffffffffffffffffffff8516916370a08231916024808301926020929190829003018186803b15801561159957600080fd5b505afa1580156115ad573d6000803e3d6000fd5b505050506040513d60208110156115c357600080fd5b5051306000908152600160205260408120549192506115e288886126ec565b600054909150806115f9848763ffffffff6121e816565b8161160057fe5b049a5080611614848663ffffffff6121e816565b8161161b57fe5b04995060008b11801561162e575060008a115b611683576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180612b996028913960400191505060405180910390fd5b61168d3084612992565b611698878d8d611fdb565b6116a3868d8c611fdb565b604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff8916916370a08231916024808301926020929190829003018186803b15801561170f57600080fd5b505afa158015611723573d6000803e3d6000fd5b505050506040513d602081101561173957600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905191965073ffffffffffffffffffffffffffffffffffffffff8816916370a0823191602480820192602092909190829003018186803b1580156117ab57600080fd5b505afa1580156117bf573d6000803e3d6000fd5b505050506040513d60208110156117d557600080fd5b505193506117e585858b8b6122e0565b811561182757600854611823906dffffffffffffffffffffffffffff808216916e01000000000000000000000000000090041663ffffffff6121e816565b600b555b604080518c8152602081018c9052815173ffffffffffffffffffffffffffffffffffffffff8f169233927fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496929081900390910190a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b6000610df233848461260b565b6103e881565b600c5460011461194f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654600754600854604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff9485169490931692611a2b9285928792611a26926dffffffffffffffffffffffffffff169185916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b505afa158015611a02573d6000803e3d6000fd5b505050506040513d6020811015611a1857600080fd5b50519063ffffffff61226e16565b611fdb565b600854604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611aca9284928792611a26926e01000000000000000000000000000090046dffffffffffffffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff8616916370a0823191602480820192602092909190829003018186803b1580156119ee57600080fd5b50506001600c5550565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60075473ffffffffffffffffffffffffffffffffffffffff1681565b42841015611b7b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f556e697377617056323a20455850495245440000000000000000000000000000604482015290519081900360640190fd5b60035473ffffffffffffffffffffffffffffffffffffffff80891660008181526004602090815260408083208054600180820190925582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98186015280840196909652958d166060860152608085018c905260a085019590955260c08085018b90528151808603909101815260e0850182528051908301207f19010000000000000000000000000000000000000000000000000000000000006101008601526101028501969096526101228085019690965280518085039096018652610142840180825286519683019690962095839052610162840180825286905260ff89166101828501526101a284018890526101c28401879052519193926101e2808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015611cdc573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611d5757508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611dc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f556e697377617056323a20494e56414c49445f5349474e415455524500000000604482015290519081900360640190fd5b611dcd89898961259c565b505050505050505050565b600260209081526000928352604080842090915290825290205481565b600c54600114611e6657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f556e697377617056323a204c4f434b4544000000000000000000000000000000604482015290519081900360640190fd5b6000600c55600654604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051611fd49273ffffffffffffffffffffffffffffffffffffffff16916370a08231916024808301926020929190829003018186803b158015611edd57600080fd5b505afa158015611ef1573d6000803e3d6000fd5b505050506040513d6020811015611f0757600080fd5b5051600754604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216916370a0823191602480820192602092909190829003018186803b158015611f7a57600080fd5b505afa158015611f8e573d6000803e3d6000fd5b505050506040513d6020811015611fa457600080fd5b50516008546dffffffffffffffffffffffffffff808216916e0100000000000000000000000000009004166122e0565b6001600c55565b604080518082018252601981527f7472616e7366657228616464726573732c75696e743235362900000000000000602091820152815173ffffffffffffffffffffffffffffffffffffffff85811660248301526044808301869052845180840390910181526064909201845291810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251815160009460609489169392918291908083835b602083106120e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016120a4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612143576040519150601f19603f3d011682016040523d82523d6000602084013e612148565b606091505b5091509150818015612176575080511580612176575080806020019051602081101561217357600080fd5b50515b6121e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e697377617056323a205452414e534645525f4641494c4544000000000000604482015290519081900360640190fd5b5050505050565b60008115806122035750508082028282828161220057fe5b04145b610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b80820382811115610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6dffffffffffffffffffffffffffff841180159061230c57506dffffffffffffffffffffffffffff8311155b61237757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e697377617056323a204f564552464c4f5700000000000000000000000000604482015290519081900360640190fd5b60085463ffffffff428116917c0100000000000000000000000000000000000000000000000000000000900481168203908116158015906123c757506dffffffffffffffffffffffffffff841615155b80156123e257506dffffffffffffffffffffffffffff831615155b15612492578063ffffffff16612425856123fb86612a57565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169063ffffffff612a7b16565b600980547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092169290920201905563ffffffff8116612465846123fb87612a57565b600a80547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216929092020190555b600880547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff888116919091177fffffffff0000000000000000000000000000ffffffffffffffffffffffffffff166e0100000000000000000000000000008883168102919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff871602179283905560408051848416815291909304909116602082015281517f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1929181900390910190a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260016020526040902054612641908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600160205260408082209390935590841681522054612683908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561275757600080fd5b505afa15801561276b573d6000803e3d6000fd5b505050506040513d602081101561278157600080fd5b5051600b5473ffffffffffffffffffffffffffffffffffffffff821615801594509192509061286457801561285f5760006127d86112576dffffffffffffffffffffffffffff88811690881663ffffffff6121e816565b905060006127e583612878565b90508082111561285c576000612813612804848463ffffffff61226e16565b6000549063ffffffff6121e816565b905060006128388361282c86600563ffffffff6121e816565b9063ffffffff612abc16565b9050600081838161284557fe5b04905080156128585761285887826128ca565b5050505b50505b612870565b8015612870576000600b555b505092915050565b600060038211156128bb575080600160028204015b818110156128b5578091506002818285816128a457fe5b0401816128ad57fe5b04905061288d565b506128c5565b81156128c5575060015b919050565b6000546128dd908263ffffffff612abc16565b600090815573ffffffffffffffffffffffffffffffffffffffff8316815260016020526040902054612915908263ffffffff612abc16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000818310612989578161298b565b825b9392505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546129c8908263ffffffff61226e16565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081209190915554612a02908263ffffffff61226e16565b600090815560408051838152905173ffffffffffffffffffffffffffffffffffffffff8516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef919081900360200190a35050565b6dffffffffffffffffffffffffffff166e0100000000000000000000000000000290565b60006dffffffffffffffffffffffffffff82167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff841681612ab457fe5b049392505050565b80820182811015610df657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fdfe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158207dca18479e58487606bf70c79e44d8dee62353c9ee6d01f9a9d70885b8765f2264736f6c63430005100032"), + String::from("06fdde03"), + String::from("0x6865696d64616c6c000000000061646472657373"), + String::from("0x6865696d64616c6c0000000000006f726967696e"), + String::from("0x6865696d64616c6c00000000000063616c6c6572"), + 0, + 999999999, + ); + + vm.execute(); + + assert_eq!( + vm.returndata, + vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 85, 110, 105, 115, 119, 97, 112, 32, 86, 50, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } +} diff --git a/common/src/ether/evm/ext/exec/mod.rs b/common/src/ether/evm/ext/exec/mod.rs index 880b1e7e..2667501c 100644 --- a/common/src/ether/evm/ext/exec/mod.rs +++ b/common/src/ether/evm/ext/exec/mod.rs @@ -11,8 +11,7 @@ use crate::{ stack::Stack, vm::{State, VM}, }, - io::logging::Logger, - utils::strings::decode_hex, + utils::{io::logging::Logger, strings::decode_hex}, }; use ethers::types::U256; use std::collections::HashMap; diff --git a/common/src/ether/evm/ext/exec/util.rs b/common/src/ether/evm/ext/exec/util.rs index cf8fae89..0e46d57f 100644 --- a/common/src/ether/evm/ext/exec/util.rs +++ b/common/src/ether/evm/ext/exec/util.rs @@ -3,7 +3,7 @@ use ethers::types::U256; use crate::{ constants::{MEMORY_REGEX, STORAGE_REGEX}, ether::evm::core::stack::{Stack, StackFrame}, - io::logging::Logger, + utils::io::logging::Logger, }; /// Given two stacks A and B, return A - B, i.e. the items in A that are not in B. diff --git a/common/src/ether/lexers/cleanup.rs b/common/src/ether/lexers/cleanup.rs index 61553536..29604e87 100644 --- a/common/src/ether/lexers/cleanup.rs +++ b/common/src/ether/lexers/cleanup.rs @@ -22,23 +22,6 @@ lazy_static! { } /// Convert bitwise operations to a variable type cast -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);".to_string(); -/// let converted = convert_bitmask_to_casting(line); -/// assert_eq!(converted, "uint256(arg0);"); -/// -/// let line = "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);".to_string(); -/// let converted = convert_bitmask_to_casting(line); -/// assert_eq!(converted, "uint256(arg0);"); -/// ``` fn convert_bitmask_to_casting(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -145,19 +128,6 @@ fn convert_bitmask_to_casting(line: &str) -> String { } /// Removes unnecessary casts -/// -/// # Arguments -/// line: String - the line to simplify -/// -/// # Returns -/// String - the simplified line -/// -/// # Example -/// ```no_run -/// let line = "uint256(uint256(arg0))".to_string(); -/// let simplified = simplify_casts(line); -/// assert_eq!(simplified, "uint256(arg0)"); -/// ``` fn simplify_casts(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -188,19 +158,6 @@ fn simplify_casts(line: &str) -> String { } /// Simplifies arithmatic by removing unnecessary operations -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "var_a = 1 * 2;".to_string(); -/// let converted = simplify_arithmatic(line); -/// assert_eq!(converted, "var_a = 2;"); -/// ``` fn simplify_arithmatic(line: &str) -> String { let cleaned = DIV_BY_ONE_REGEX.replace_all(line, ""); let cleaned = MUL_BY_ONE_REGEX.replace_all(&cleaned, ""); diff --git a/common/src/ether/lexers/solidity.rs b/common/src/ether/lexers/solidity.rs index 9f4711d5..19ff636c 100644 --- a/common/src/ether/lexers/solidity.rs +++ b/common/src/ether/lexers/solidity.rs @@ -18,7 +18,7 @@ pub fn is_ext_call_precompile(precompile_address: U256) -> bool { } impl WrappedOpcode { - // Returns a WrappedOpcode's solidity representation. + /// Returns a WrappedOpcode's solidity representation. pub fn solidify(&self) -> String { let mut solidified_wrapped_opcode = String::new(); @@ -382,9 +382,9 @@ impl WrappedOpcode { solidified_wrapped_opcode } - // creates a new WrappedOpcode from a set of raw inputs + /// creates a new WrappedOpcode from a set of raw inputs pub fn new(opcode_int: u8, inputs: Vec) -> WrappedOpcode { - WrappedOpcode { opcode: opcode(opcode_int), inputs: inputs } + WrappedOpcode { opcode: Opcode::new(opcode_int), inputs } } } @@ -398,7 +398,7 @@ impl Default for WrappedOpcode { } impl WrappedInput { - // Returns a WrappedInput's solidity representation. + /// Returns a WrappedInput's solidity representation. fn _solidify(&self) -> String { let mut solidified_wrapped_input = String::new(); @@ -420,3 +420,568 @@ impl WrappedInput { solidified_wrapped_input } } + +#[cfg(test)] +mod tests { + use crate::ether::{ + evm::core::opcodes::{Opcode, WrappedInput, WrappedOpcode}, + lexers::solidity::is_ext_call_precompile, + }; + use ethers::types::U256; + + #[test] + fn test_is_ext_call_precompile() { + assert!(is_ext_call_precompile(U256::from(1))); + assert!(is_ext_call_precompile(U256::from(2))); + assert!(is_ext_call_precompile(U256::from(3))); + assert!(!is_ext_call_precompile(U256::from(4))); + assert!(!is_ext_call_precompile(U256::MAX)); + } + + #[test] + fn test_wrapped_opcode_solidify_add() { + let opcode = Opcode { code: 0x01, name: "ADD", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x01 + 0x02"); + } + + #[test] + fn test_wrapped_opcode_solidify_mul() { + let opcode = Opcode { code: 0x02, name: "MUL", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x02 * 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_sub() { + let opcode = Opcode { code: 0x03, name: "SUB", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x05 - 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_div() { + let opcode = Opcode { code: 0x04, name: "DIV", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(2u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a / 0x02"); + } + + #[test] + fn test_wrapped_opcode_solidify_sdiv() { + let opcode = Opcode { code: 0x05, name: "SDIV", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(2u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a / 0x02"); + } + + #[test] + fn test_wrapped_opcode_solidify_mod() { + let opcode = Opcode { code: 0x06, name: "MOD", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a % 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_smod() { + let opcode = Opcode { code: 0x07, name: "SMOD", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a % 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_addmod() { + let opcode = Opcode { code: 0x08, name: "ADDMOD", mingas: 1, inputs: 3, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(3u8)), + WrappedInput::Raw(U256::from(4u8)), + WrappedInput::Raw(U256::from(5u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x03 + 0x04 % 0x05"); + } + + #[test] + fn test_wrapped_opcode_solidify_mulmod() { + let opcode = Opcode { code: 0x09, name: "MULMOD", mingas: 1, inputs: 3, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(3u8)), + WrappedInput::Raw(U256::from(4u8)), + WrappedInput::Raw(U256::from(5u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "(0x03 * 0x04) % 0x05"); + } + + #[test] + fn test_wrapped_opcode_solidify_exp() { + let opcode = Opcode { code: 0x0a, name: "EXP", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x02 ** 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_lt() { + let opcode = Opcode { code: 0x10, name: "LT", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x02 < 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_gt() { + let opcode = Opcode { code: 0x11, name: "GT", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x05 > 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_slt() { + let opcode = Opcode { code: 0x12, name: "SLT", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x02 < 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_sgt() { + let opcode = Opcode { code: 0x13, name: "SGT", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x05 > 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_eq() { + let opcode = Opcode { code: 0x14, name: "EQ", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(5u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x05 == 0x05"); + } + + #[test] + fn test_wrapped_opcode_solidify_iszero() { + let opcode = Opcode { code: 0x15, name: "ISZERO", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "!0"); + } + + #[test] + fn test_wrapped_opcode_solidify_and() { + let opcode = Opcode { code: 0x16, name: "AND", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = + vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "(0x0a) & (0x0c)"); + } + + #[test] + fn test_wrapped_opcode_solidify_or() { + let opcode = Opcode { code: 0x17, name: "OR", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = + vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a | 0x0c"); + } + + #[test] + fn test_wrapped_opcode_solidify_xor() { + let opcode = Opcode { code: 0x18, name: "XOR", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = + vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x0a ^ 0x0c"); + } + + #[test] + fn test_wrapped_opcode_solidify_not() { + let opcode = Opcode { code: 0x19, name: "NOT", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0b1010u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "~(0x0a)"); + } + + #[test] + fn test_wrapped_opcode_solidify_shl() { + let opcode = Opcode { code: 0x1a, name: "SHL", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(3u8)), WrappedInput::Raw(U256::from(1u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x01 << 0x03"); + } + + #[test] + fn test_wrapped_opcode_solidify_shr() { + let opcode = Opcode { code: 0x1b, name: "SHR", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(6u8)), WrappedInput::Raw(U256::from(1u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x01 >> 0x06"); + } + + #[test] + fn test_wrapped_opcode_solidify_sar() { + let opcode = Opcode { code: 0x1c, name: "SAR", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(6u8)), WrappedInput::Raw(U256::from(1u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x01 >> 0x06"); + } + + #[test] + fn test_wrapped_opcode_solidify_byte() { + let opcode = Opcode { code: 0x1d, name: "BYTE", mingas: 1, inputs: 2, outputs: 1 }; + let inputs = + vec![WrappedInput::Raw(U256::from(3u8)), WrappedInput::Raw(U256::from(0x12345678u32))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0x12345678"); + } + + #[test] + fn test_wrapped_opcode_solidify_sha3() { + let opcode = Opcode { code: 0x20, name: "SHA3", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0u8))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "keccak256(memory[0])"); + } + + #[test] + fn test_wrapped_opcode_solidify_address() { + let opcode = Opcode { code: 0x30, name: "ADDRESS", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "address(this)"); + } + + #[test] + fn test_wrapped_opcode_solidify_balance() { + let opcode = Opcode { code: 0x31, name: "BALANCE", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "address(0x1234).balance"); + } + + #[test] + fn test_wrapped_opcode_solidify_origin() { + let opcode = Opcode { code: 0x32, name: "ORIGIN", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "tx.origin"); + } + + #[test] + fn test_wrapped_opcode_solidify_caller() { + let opcode = Opcode { code: 0x33, name: "CALLER", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "msg.sender"); + } + + #[test] + fn test_wrapped_opcode_solidify_callvalue() { + let opcode = Opcode { code: 0x34, name: "CALLVALUE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "msg.value"); + } + + #[test] + fn test_wrapped_opcode_solidify_calldataload() { + let opcode = Opcode { code: 0x35, name: "CALLDATALOAD", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "arg145"); + } + + #[test] + fn test_wrapped_opcode_solidify_calldatasize() { + let opcode = Opcode { code: 0x36, name: "CALLDATASIZE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "msg.data.length"); + } + + #[test] + fn test_wrapped_opcode_solidify_codesize() { + let opcode = Opcode { code: 0x38, name: "CODESIZE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "this.code.length"); + } + + #[test] + fn test_wrapped_opcode_solidify_extcodesize() { + let opcode = Opcode { code: 0x3b, name: "EXTCODESIZE", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "address(0x1234).code.length"); + } + + #[test] + fn test_wrapped_opcode_solidify_extcodehash() { + let opcode = Opcode { code: 0x3f, name: "EXTCODEHASH", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "address(0x1234).codehash"); + } + + #[test] + fn test_wrapped_opcode_solidify_blockhash() { + let opcode = Opcode { code: 0x40, name: "BLOCKHASH", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "blockhash(0x1234)"); + } + + #[test] + fn test_wrapped_opcode_solidify_coinbase() { + let opcode = Opcode { code: 0x41, name: "COINBASE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.coinbase"); + } + + #[test] + fn test_wrapped_opcode_solidify_timestamp() { + let opcode = Opcode { code: 0x42, name: "TIMESTAMP", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.timestamp"); + } + + #[test] + fn test_wrapped_opcode_solidify_number() { + let opcode = Opcode { code: 0x43, name: "NUMBER", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.number"); + } + + #[test] + fn test_wrapped_opcode_solidify_difficulty() { + let opcode = Opcode { code: 0x44, name: "DIFFICULTY", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.difficulty"); + } + + #[test] + fn test_wrapped_opcode_solidify_gaslimit() { + let opcode = Opcode { code: 0x45, name: "GASLIMIT", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.gaslimit"); + } + + #[test] + fn test_wrapped_opcode_solidify_chainid() { + let opcode = Opcode { code: 0x46, name: "CHAINID", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.chainid"); + } + + #[test] + fn test_wrapped_opcode_solidify_selfbalance() { + let opcode = Opcode { code: 0x47, name: "SELFBALANCE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "address(this).balance"); + } + + #[test] + fn test_wrapped_opcode_solidify_basefee() { + let opcode = Opcode { code: 0x48, name: "BASEFEE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "block.basefee"); + } + + #[test] + fn test_wrapped_opcode_solidify_gas() { + let opcode = Opcode { code: 0x5a, name: "GAS", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "gasleft()"); + } + + #[test] + fn test_wrapped_opcode_solidify_gasprice() { + let opcode = Opcode { code: 0x3a, name: "GASPRICE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "tx.gasprice"); + } + + #[test] + fn test_wrapped_opcode_solidify_sload() { + let opcode = Opcode { code: 0x54, name: "SLOAD", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "storage[0x1234]"); + } + + #[test] + fn test_wrapped_opcode_solidify_mload() { + let opcode = Opcode { code: 0x51, name: "MLOAD", mingas: 1, inputs: 1, outputs: 1 }; + let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory[0x1234]"); + } + + #[test] + fn test_wrapped_opcode_solidify_msize() { + let opcode = Opcode { code: 0x59, name: "MSIZE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory.length"); + } + + #[test] + fn test_wrapped_opcode_solidify_call() { + let opcode = Opcode { code: 0xf1, name: "CALL", mingas: 1, inputs: 7, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(0x1234u16)), + WrappedInput::Raw(U256::from(0x01u8)), + WrappedInput::Raw(U256::from(0x02u8)), + WrappedInput::Raw(U256::from(0x03u8)), + WrappedInput::Raw(U256::from(0x04u8)), + WrappedInput::Raw(U256::from(0x05u8)), + WrappedInput::Raw(U256::from(0x06u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); + } + + #[test] + fn test_wrapped_opcode_solidify_callcode() { + let opcode = Opcode { code: 0xf2, name: "CALLCODE", mingas: 1, inputs: 7, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(0x1234u16)), + WrappedInput::Raw(U256::from(0x01u8)), + WrappedInput::Raw(U256::from(0x02u8)), + WrappedInput::Raw(U256::from(0x03u8)), + WrappedInput::Raw(U256::from(0x04u8)), + WrappedInput::Raw(U256::from(0x05u8)), + WrappedInput::Raw(U256::from(0x06u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); + } + + #[test] + fn test_wrapped_opcode_solidify_delegatecall() { + let opcode = Opcode { code: 0xf4, name: "DELEGATECALL", mingas: 1, inputs: 6, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(0x1234u16)), + WrappedInput::Raw(U256::from(0x01u8)), + WrappedInput::Raw(U256::from(0x02u8)), + WrappedInput::Raw(U256::from(0x03u8)), + WrappedInput::Raw(U256::from(0x04u8)), + WrappedInput::Raw(U256::from(0x05u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); + } + + #[test] + fn test_wrapped_opcode_solidify_staticcall() { + let opcode = Opcode { code: 0xfa, name: "STATICCALL", mingas: 1, inputs: 6, outputs: 1 }; + let inputs = vec![ + WrappedInput::Raw(U256::from(0x1234u16)), + WrappedInput::Raw(U256::from(0x01u8)), + WrappedInput::Raw(U256::from(0x02u8)), + WrappedInput::Raw(U256::from(0x03u8)), + WrappedInput::Raw(U256::from(0x04u8)), + WrappedInput::Raw(U256::from(0x05u8)), + ]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); + } + + #[test] + fn test_wrapped_opcode_solidify_returndatasize() { + let opcode = + Opcode { code: 0x3d, name: "RETURNDATASIZE", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "ret0.length"); + } + + #[test] + fn test_wrapped_opcode_solidify_push() { + let opcode = Opcode { code: 0x5f, name: "PUSH0", mingas: 1, inputs: 0, outputs: 1 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "0"); + } + + #[test] + fn test_wrapped_opcode_solidify_unknown() { + let opcode = Opcode { code: 0xff, name: "unknown", mingas: 1, inputs: 0, outputs: 0 }; + let inputs = vec![]; + let wrapped_opcode = WrappedOpcode { opcode, inputs }; + + assert_eq!(wrapped_opcode.solidify(), "unknown"); + } +} diff --git a/common/src/ether/lexers/yul.rs b/common/src/ether/lexers/yul.rs index 02389ac8..166a500d 100644 --- a/common/src/ether/lexers/yul.rs +++ b/common/src/ether/lexers/yul.rs @@ -1,7 +1,7 @@ use crate::{ether::evm::core::opcodes::*, utils::strings::encode_hex_reduced}; impl WrappedOpcode { - // Returns a WrappedOpcode's yul representation. + /// Returns a WrappedOpcode's yul representation. pub fn yulify(&self) -> String { if self.opcode.name == "PUSH0" { "0".to_string() @@ -18,7 +18,7 @@ impl WrappedOpcode { } impl WrappedInput { - // Returns a WrappedInput's solidity representation. + /// Returns a WrappedInput's solidity representation. fn _yulify(&self) -> String { let mut solidified_wrapped_input = String::new(); @@ -34,3 +34,42 @@ impl WrappedInput { solidified_wrapped_input } } + +#[cfg(test)] +mod tests { + + use ethers::types::U256; + + use crate::ether::evm::core::opcodes::{WrappedInput, WrappedOpcode}; + + #[test] + fn test_push0() { + // wraps an ADD operation with 2 raw inputs + let add_operation_wrapped = WrappedOpcode::new(0x5f, vec![]); + assert_eq!(add_operation_wrapped.yulify(), "0"); + } + + #[test] + fn test_yulify_add() { + // wraps an ADD operation with 2 raw inputs + let add_operation_wrapped = WrappedOpcode::new( + 0x01, + vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], + ); + assert_eq!(add_operation_wrapped.yulify(), "add(0x01, 0x02)"); + } + + #[test] + fn test_yulify_add_complex() { + // wraps an ADD operation with 2 raw inputs + let add_operation_wrapped = WrappedOpcode::new( + 0x01, + vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], + ); + let complex_add_operation = WrappedOpcode::new( + 0x01, + vec![WrappedInput::Opcode(add_operation_wrapped), WrappedInput::Raw(U256::from(3u8))], + ); + assert_eq!(complex_add_operation.yulify(), "add(add(0x01, 0x02), 0x03)"); + } +} diff --git a/common/src/ether/mod.rs b/common/src/ether/mod.rs index 4b4b32ac..dc484173 100644 --- a/common/src/ether/mod.rs +++ b/common/src/ether/mod.rs @@ -4,4 +4,3 @@ pub mod lexers; pub mod rpc; pub mod selectors; pub mod signatures; -pub mod tests; diff --git a/common/src/ether/rpc.rs b/common/src/ether/rpc.rs index 8e25495e..43b1c7ec 100644 --- a/common/src/ether/rpc.rs +++ b/common/src/ether/rpc.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use crate::io::logging::Logger; +use crate::utils::io::logging::Logger; use ethers::{ core::types::Address, providers::{Http, Middleware, Provider}, @@ -10,11 +10,12 @@ use heimdall_cache::{read_cache, store_cache}; /// Get the chainId of the provided RPC URL /// -/// # Arguments -/// - `rpc_url`: &str +/// ```no_run +/// use heimdall_common::ether::rpc::chain_id; /// -/// # Returns -/// `Result>` +/// // let chain_id = chain_id("https://eth.llamarpc.com").await.unwrap(); +/// //assert_eq!(chain_id, 1); +/// ``` pub async fn chain_id(rpc_url: &str) -> Result> { // get a new logger let logger = Logger::default(); @@ -62,12 +63,12 @@ pub async fn chain_id(rpc_url: &str) -> Result> /// Get the bytecode of the provided contract address /// -/// # Arguments -/// - `contract_address`: &str -/// - `rpc_url`: &str +/// ```no_run +/// use heimdall_common::ether::rpc::get_code; /// -/// # Returns -/// `Result>` +/// // let bytecode = get_code("0x0", "https://eth.llamarpc.com").await; +/// // assert!(bytecode.is_ok()); +/// ``` pub async fn get_code( contract_address: &str, rpc_url: &str, @@ -132,12 +133,12 @@ pub async fn get_code( /// Get the raw transaction data of the provided transaction hash /// -/// # Arguments -/// - `transaction_hash`: &str -/// - `rpc_url`: &str +/// ```no_run +/// use heimdall_common::ether::rpc::get_code; /// -/// # Returns -/// `Result>` +/// // let bytecode = get_code("0x0", "https://eth.llamarpc.com").await; +/// // assert!(bytecode.is_ok()); +/// ``` pub async fn get_transaction( transaction_hash: &str, rpc_url: &str, diff --git a/common/src/ether/selectors.rs b/common/src/ether/selectors.rs index 7a12a225..9bb537fc 100644 --- a/common/src/ether/selectors.rs +++ b/common/src/ether/selectors.rs @@ -7,11 +7,11 @@ use std::{ use indicatif::ProgressBar; use tokio::task; -use crate::{io::logging::Logger, utils::strings::decode_hex}; +use crate::utils::{io::logging::Logger, strings::decode_hex}; use super::{evm::core::vm::VM, signatures::ResolveSelector}; -// find all function selectors in the given EVM assembly. +/// find all function selectors in the given EVM assembly. pub fn find_function_selectors(evm: &VM, assembly: &str) -> HashMap { let mut function_selectors = HashMap::new(); let mut handled_selectors = HashSet::new(); @@ -63,7 +63,7 @@ pub fn find_function_selectors(evm: &VM, assembly: &str) -> HashMap u128 { let mut vm = evm.clone(); let mut handled_jumps = HashSet::new(); @@ -104,6 +104,7 @@ pub fn resolve_entry_point(evm: &VM, selector: &str) -> u128 { 0 } +/// Resolve a list of selectors to their function signatures. pub async fn resolve_selectors(selectors: Vec) -> HashMap> where T: ResolveSelector + Send + Clone + 'static, { diff --git a/common/src/ether/signatures.rs b/common/src/ether/signatures.rs index 44b7ccc6..b8afee19 100644 --- a/common/src/ether/signatures.rs +++ b/common/src/ether/signatures.rs @@ -2,10 +2,7 @@ use async_trait::async_trait; use ethers::abi::Token; use heimdall_cache::{read_cache, store_cache}; -use crate::{ - io::logging::Logger, - utils::{http::get_json_from_url, strings::replace_last}, -}; +use crate::utils::{http::get_json_from_url, io::logging::Logger, strings::replace_last}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -300,3 +297,81 @@ pub fn score_signature(signature: &str) -> u32 { score } + +#[cfg(test)] +mod tests { + use heimdall_cache::delete_cache; + + use crate::ether::signatures::{ + score_signature, ResolveSelector, ResolvedError, ResolvedFunction, ResolvedLog, + }; + + #[tokio::test] + async fn resolve_function_signature_should_return_none_when_cached_results_not_found() { + let signature = String::from("test_signature_nocache"); + let result = ResolvedFunction::resolve(&signature).await; + + assert_eq!(result, None,) + } + + #[tokio::test] + async fn resolve_function_signature_should_return_none_when_json_url_returns_empty_signatures() + { + delete_cache(&format!("selector.{}", "test_signature")); + let signature = String::from("test_signature"); + let result = ResolvedFunction::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_error_signature_should_return_none_when_cached_results_not_found() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedError::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_error_signature_should_return_none_when_json_url_returns_none() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedError::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_error_signature_should_return_none_when_json_url_returns_empty_signatures() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedError::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_event_signature_should_return_none_when_cached_results_not_found() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedLog::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_event_signature_should_return_none_when_json_url_returns_none() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedLog::resolve(&signature).await; + assert_eq!(result, None); + } + + #[tokio::test] + async fn resolve_event_signature_should_return_none_when_json_url_returns_empty_signatures() { + let signature = String::from("test_signature_notfound"); + let result = ResolvedLog::resolve(&signature).await; + assert_eq!(result, None); + } + + #[test] + fn score_signature_should_return_correct_score() { + let signature = String::from("test_signature"); + let score = score_signature(&signature); + let expected_score = 1000 - + (signature.len() as u32) - + (signature.matches(|c: char| c.is_numeric()).count() as u32) * 3; + assert_eq!(score, expected_score); + } +} diff --git a/common/src/ether/tests.rs b/common/src/ether/tests.rs deleted file mode 100644 index e53f5f09..00000000 --- a/common/src/ether/tests.rs +++ /dev/null @@ -1,766 +0,0 @@ -#[cfg(test)] -mod test_solidity { - use crate::ether::{ - evm::core::opcodes::{Opcode, WrappedInput, WrappedOpcode}, - lexers::solidity::is_ext_call_precompile, - }; - use ethers::types::U256; - - #[test] - fn test_is_ext_call_precompile() { - assert!(is_ext_call_precompile(U256::from(1))); - assert!(is_ext_call_precompile(U256::from(2))); - assert!(is_ext_call_precompile(U256::from(3))); - assert!(!is_ext_call_precompile(U256::from(4))); - assert!(!is_ext_call_precompile(U256::MAX)); - } - - #[test] - fn test_wrapped_opcode_solidify_add() { - let opcode = Opcode { code: 0x01, name: "ADD", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x01 + 0x02"); - } - - #[test] - fn test_wrapped_opcode_solidify_mul() { - let opcode = Opcode { code: 0x02, name: "MUL", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x02 * 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_sub() { - let opcode = Opcode { code: 0x03, name: "SUB", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x05 - 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_div() { - let opcode = Opcode { code: 0x04, name: "DIV", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(2u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a / 0x02"); - } - - #[test] - fn test_wrapped_opcode_solidify_sdiv() { - let opcode = Opcode { code: 0x05, name: "SDIV", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(2u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a / 0x02"); - } - - #[test] - fn test_wrapped_opcode_solidify_mod() { - let opcode = Opcode { code: 0x06, name: "MOD", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a % 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_smod() { - let opcode = Opcode { code: 0x07, name: "SMOD", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(10u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a % 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_addmod() { - let opcode = Opcode { code: 0x08, name: "ADDMOD", mingas: 1, inputs: 3, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(3u8)), - WrappedInput::Raw(U256::from(4u8)), - WrappedInput::Raw(U256::from(5u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x03 + 0x04 % 0x05"); - } - - #[test] - fn test_wrapped_opcode_solidify_mulmod() { - let opcode = Opcode { code: 0x09, name: "MULMOD", mingas: 1, inputs: 3, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(3u8)), - WrappedInput::Raw(U256::from(4u8)), - WrappedInput::Raw(U256::from(5u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "(0x03 * 0x04) % 0x05"); - } - - #[test] - fn test_wrapped_opcode_solidify_exp() { - let opcode = Opcode { code: 0x0a, name: "EXP", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x02 ** 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_lt() { - let opcode = Opcode { code: 0x10, name: "LT", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x02 < 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_gt() { - let opcode = Opcode { code: 0x11, name: "GT", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x05 > 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_slt() { - let opcode = Opcode { code: 0x12, name: "SLT", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(2u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x02 < 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_sgt() { - let opcode = Opcode { code: 0x13, name: "SGT", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(3u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x05 > 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_eq() { - let opcode = Opcode { code: 0x14, name: "EQ", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(5u8)), WrappedInput::Raw(U256::from(5u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x05 == 0x05"); - } - - #[test] - fn test_wrapped_opcode_solidify_iszero() { - let opcode = Opcode { code: 0x15, name: "ISZERO", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "!0"); - } - - #[test] - fn test_wrapped_opcode_solidify_and() { - let opcode = Opcode { code: 0x16, name: "AND", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = - vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "(0x0a) & (0x0c)"); - } - - #[test] - fn test_wrapped_opcode_solidify_or() { - let opcode = Opcode { code: 0x17, name: "OR", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = - vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a | 0x0c"); - } - - #[test] - fn test_wrapped_opcode_solidify_xor() { - let opcode = Opcode { code: 0x18, name: "XOR", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = - vec![WrappedInput::Raw(U256::from(0b1010u8)), WrappedInput::Raw(U256::from(0b1100u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x0a ^ 0x0c"); - } - - #[test] - fn test_wrapped_opcode_solidify_not() { - let opcode = Opcode { code: 0x19, name: "NOT", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0b1010u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "~(0x0a)"); - } - - #[test] - fn test_wrapped_opcode_solidify_shl() { - let opcode = Opcode { code: 0x1a, name: "SHL", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(3u8)), WrappedInput::Raw(U256::from(1u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x01 << 0x03"); - } - - #[test] - fn test_wrapped_opcode_solidify_shr() { - let opcode = Opcode { code: 0x1b, name: "SHR", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(6u8)), WrappedInput::Raw(U256::from(1u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x01 >> 0x06"); - } - - #[test] - fn test_wrapped_opcode_solidify_sar() { - let opcode = Opcode { code: 0x1c, name: "SAR", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(6u8)), WrappedInput::Raw(U256::from(1u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x01 >> 0x06"); - } - - #[test] - fn test_wrapped_opcode_solidify_byte() { - let opcode = Opcode { code: 0x1d, name: "BYTE", mingas: 1, inputs: 2, outputs: 1 }; - let inputs = - vec![WrappedInput::Raw(U256::from(3u8)), WrappedInput::Raw(U256::from(0x12345678u32))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0x12345678"); - } - - #[test] - fn test_wrapped_opcode_solidify_sha3() { - let opcode = Opcode { code: 0x20, name: "SHA3", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0u8))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "keccak256(memory[0])"); - } - - #[test] - fn test_wrapped_opcode_solidify_address() { - let opcode = Opcode { code: 0x30, name: "ADDRESS", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "address(this)"); - } - - #[test] - fn test_wrapped_opcode_solidify_balance() { - let opcode = Opcode { code: 0x31, name: "BALANCE", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "address(0x1234).balance"); - } - - #[test] - fn test_wrapped_opcode_solidify_origin() { - let opcode = Opcode { code: 0x32, name: "ORIGIN", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "tx.origin"); - } - - #[test] - fn test_wrapped_opcode_solidify_caller() { - let opcode = Opcode { code: 0x33, name: "CALLER", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "msg.sender"); - } - - #[test] - fn test_wrapped_opcode_solidify_callvalue() { - let opcode = Opcode { code: 0x34, name: "CALLVALUE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "msg.value"); - } - - #[test] - fn test_wrapped_opcode_solidify_calldataload() { - let opcode = Opcode { code: 0x35, name: "CALLDATALOAD", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "arg145"); - } - - #[test] - fn test_wrapped_opcode_solidify_calldatasize() { - let opcode = Opcode { code: 0x36, name: "CALLDATASIZE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "msg.data.length"); - } - - #[test] - fn test_wrapped_opcode_solidify_codesize() { - let opcode = Opcode { code: 0x38, name: "CODESIZE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "this.code.length"); - } - - #[test] - fn test_wrapped_opcode_solidify_extcodesize() { - let opcode = Opcode { code: 0x3b, name: "EXTCODESIZE", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "address(0x1234).code.length"); - } - - #[test] - fn test_wrapped_opcode_solidify_extcodehash() { - let opcode = Opcode { code: 0x3f, name: "EXTCODEHASH", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "address(0x1234).codehash"); - } - - #[test] - fn test_wrapped_opcode_solidify_blockhash() { - let opcode = Opcode { code: 0x40, name: "BLOCKHASH", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "blockhash(0x1234)"); - } - - #[test] - fn test_wrapped_opcode_solidify_coinbase() { - let opcode = Opcode { code: 0x41, name: "COINBASE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.coinbase"); - } - - #[test] - fn test_wrapped_opcode_solidify_timestamp() { - let opcode = Opcode { code: 0x42, name: "TIMESTAMP", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.timestamp"); - } - - #[test] - fn test_wrapped_opcode_solidify_number() { - let opcode = Opcode { code: 0x43, name: "NUMBER", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.number"); - } - - #[test] - fn test_wrapped_opcode_solidify_difficulty() { - let opcode = Opcode { code: 0x44, name: "DIFFICULTY", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.difficulty"); - } - - #[test] - fn test_wrapped_opcode_solidify_gaslimit() { - let opcode = Opcode { code: 0x45, name: "GASLIMIT", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.gaslimit"); - } - - #[test] - fn test_wrapped_opcode_solidify_chainid() { - let opcode = Opcode { code: 0x46, name: "CHAINID", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.chainid"); - } - - #[test] - fn test_wrapped_opcode_solidify_selfbalance() { - let opcode = Opcode { code: 0x47, name: "SELFBALANCE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "address(this).balance"); - } - - #[test] - fn test_wrapped_opcode_solidify_basefee() { - let opcode = Opcode { code: 0x48, name: "BASEFEE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "block.basefee"); - } - - #[test] - fn test_wrapped_opcode_solidify_gas() { - let opcode = Opcode { code: 0x5a, name: "GAS", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "gasleft()"); - } - - #[test] - fn test_wrapped_opcode_solidify_gasprice() { - let opcode = Opcode { code: 0x3a, name: "GASPRICE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "tx.gasprice"); - } - - #[test] - fn test_wrapped_opcode_solidify_sload() { - let opcode = Opcode { code: 0x54, name: "SLOAD", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "storage[0x1234]"); - } - - #[test] - fn test_wrapped_opcode_solidify_mload() { - let opcode = Opcode { code: 0x51, name: "MLOAD", mingas: 1, inputs: 1, outputs: 1 }; - let inputs = vec![WrappedInput::Raw(U256::from(0x1234u16))]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory[0x1234]"); - } - - #[test] - fn test_wrapped_opcode_solidify_msize() { - let opcode = Opcode { code: 0x59, name: "MSIZE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory.length"); - } - - #[test] - fn test_wrapped_opcode_solidify_call() { - let opcode = Opcode { code: 0xf1, name: "CALL", mingas: 1, inputs: 7, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(0x1234u16)), - WrappedInput::Raw(U256::from(0x01u8)), - WrappedInput::Raw(U256::from(0x02u8)), - WrappedInput::Raw(U256::from(0x03u8)), - WrappedInput::Raw(U256::from(0x04u8)), - WrappedInput::Raw(U256::from(0x05u8)), - WrappedInput::Raw(U256::from(0x06u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); - } - - #[test] - fn test_wrapped_opcode_solidify_callcode() { - let opcode = Opcode { code: 0xf2, name: "CALLCODE", mingas: 1, inputs: 7, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(0x1234u16)), - WrappedInput::Raw(U256::from(0x01u8)), - WrappedInput::Raw(U256::from(0x02u8)), - WrappedInput::Raw(U256::from(0x03u8)), - WrappedInput::Raw(U256::from(0x04u8)), - WrappedInput::Raw(U256::from(0x05u8)), - WrappedInput::Raw(U256::from(0x06u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); - } - - #[test] - fn test_wrapped_opcode_solidify_delegatecall() { - let opcode = Opcode { code: 0xf4, name: "DELEGATECALL", mingas: 1, inputs: 6, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(0x1234u16)), - WrappedInput::Raw(U256::from(0x01u8)), - WrappedInput::Raw(U256::from(0x02u8)), - WrappedInput::Raw(U256::from(0x03u8)), - WrappedInput::Raw(U256::from(0x04u8)), - WrappedInput::Raw(U256::from(0x05u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); - } - - #[test] - fn test_wrapped_opcode_solidify_staticcall() { - let opcode = Opcode { code: 0xfa, name: "STATICCALL", mingas: 1, inputs: 6, outputs: 1 }; - let inputs = vec![ - WrappedInput::Raw(U256::from(0x1234u16)), - WrappedInput::Raw(U256::from(0x01u8)), - WrappedInput::Raw(U256::from(0x02u8)), - WrappedInput::Raw(U256::from(0x03u8)), - WrappedInput::Raw(U256::from(0x04u8)), - WrappedInput::Raw(U256::from(0x05u8)), - ]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "memory[0x05]"); - } - - #[test] - fn test_wrapped_opcode_solidify_returndatasize() { - let opcode = - Opcode { code: 0x3d, name: "RETURNDATASIZE", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "ret0.length"); - } - - #[test] - fn test_wrapped_opcode_solidify_push() { - let opcode = Opcode { code: 0x5f, name: "PUSH0", mingas: 1, inputs: 0, outputs: 1 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "0"); - } - - #[test] - fn test_wrapped_opcode_solidify_unknown() { - let opcode = Opcode { code: 0xff, name: "unknown", mingas: 1, inputs: 0, outputs: 0 }; - let inputs = vec![]; - let wrapped_opcode = WrappedOpcode { opcode, inputs }; - - assert_eq!(wrapped_opcode.solidify(), "unknown"); - } -} - -#[cfg(test)] -mod test_yul { - - use ethers::types::U256; - - use crate::ether::evm::core::opcodes::{WrappedInput, WrappedOpcode}; - - #[test] - fn test_push0() { - // wraps an ADD operation with 2 raw inputs - let add_operation_wrapped = WrappedOpcode::new(0x5f, vec![]); - assert_eq!(add_operation_wrapped.yulify(), "0"); - } - - #[test] - fn test_yulify_add() { - // wraps an ADD operation with 2 raw inputs - let add_operation_wrapped = WrappedOpcode::new( - 0x01, - vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], - ); - assert_eq!(add_operation_wrapped.yulify(), "add(0x01, 0x02)"); - } - - #[test] - fn test_yulify_add_complex() { - // wraps an ADD operation with 2 raw inputs - let add_operation_wrapped = WrappedOpcode::new( - 0x01, - vec![WrappedInput::Raw(U256::from(1u8)), WrappedInput::Raw(U256::from(2u8))], - ); - let complex_add_operation = WrappedOpcode::new( - 0x01, - vec![WrappedInput::Opcode(add_operation_wrapped), WrappedInput::Raw(U256::from(3u8))], - ); - assert_eq!(complex_add_operation.yulify(), "add(add(0x01, 0x02), 0x03)"); - } -} - -#[cfg(test)] -mod test_signatures { - use heimdall_cache::delete_cache; - - use crate::ether::signatures::{ - score_signature, ResolveSelector, ResolvedError, ResolvedFunction, ResolvedLog, - }; - - #[tokio::test] - async fn resolve_function_signature_should_return_none_when_cached_results_not_found() { - let signature = String::from("test_signature_nocache"); - let result = ResolvedFunction::resolve(&signature).await; - - assert_eq!(result, None,) - } - - #[tokio::test] - async fn resolve_function_signature_should_return_none_when_json_url_returns_empty_signatures() - { - delete_cache(&format!("selector.{}", "test_signature")); - let signature = String::from("test_signature"); - let result = ResolvedFunction::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_error_signature_should_return_none_when_cached_results_not_found() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedError::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_error_signature_should_return_none_when_json_url_returns_none() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedError::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_error_signature_should_return_none_when_json_url_returns_empty_signatures() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedError::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_event_signature_should_return_none_when_cached_results_not_found() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedLog::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_event_signature_should_return_none_when_json_url_returns_none() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedLog::resolve(&signature).await; - assert_eq!(result, None); - } - - #[tokio::test] - async fn resolve_event_signature_should_return_none_when_json_url_returns_empty_signatures() { - let signature = String::from("test_signature_notfound"); - let result = ResolvedLog::resolve(&signature).await; - assert_eq!(result, None); - } - - #[test] - fn score_signature_should_return_correct_score() { - let signature = String::from("test_signature"); - let score = score_signature(&signature); - let expected_score = 1000 - - (signature.len() as u32) - - (signature.matches(|c: char| c.is_numeric()).count() as u32) * 3; - assert_eq!(score, expected_score); - } -} - -#[cfg(test)] -mod test_selector {} - -#[cfg(test)] -mod test_compiler { - use crate::ether::compiler::detect_compiler; - - #[test] - fn test_detect_compiler_proxy_minimal() { - let bytecode = "363d3d373d3d3d363d73"; - let expected_result = ("proxy", "minimal".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_proxy_vyper() { - let bytecode = "366000600037611000600036600073"; - let expected_result = ("proxy", "vyper".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_vyper_range_1() { - let bytecode = "6004361015"; - let expected_result = ("vyper", "0.2.0-0.2.4,0.2.11-0.3.3".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_vyper_range_2() { - let bytecode = "341561000a"; - let expected_result = ("vyper", "0.2.5-0.2.8".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_solc_range_1() { - let bytecode = "731bf797"; - let expected_result = ("solc", "0.4.10-0.4.24".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_solc_range_2() { - let bytecode = "6080604052"; - let expected_result = ("solc", "0.4.22+".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_solc_range_3() { - let bytecode = "6060604052"; - let expected_result = ("solc", "0.4.11-0.4.21".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_vyper() { - let bytecode = r#"7679706572"#; - let expected_result = ("vyper", "unknown".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_solc() { - let bytecode = "736f6c63"; - let expected_result = ("solc", "unknown".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_solc_metadata() { - let bytecode = "736f6c63434d4e"; - let expected_result = ("solc", "unknown".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } - - #[test] - fn test_detect_compiler_vyper_metadata() { - let bytecode = "7679706572833135353030"; - let expected_result = ("vyper", "49.53.53".to_string()); - assert_eq!(detect_compiler(bytecode), expected_result); - } -} diff --git a/common/src/lib.rs b/common/src/lib.rs index ca59f849..5bab5300 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2,7 +2,5 @@ extern crate lazy_static; pub mod constants; pub mod ether; -pub mod io; pub mod resources; -pub mod testing; pub mod utils; diff --git a/common/src/resources/openai.rs b/common/src/resources/openai.rs index 7fbe7af4..739aaa53 100644 --- a/common/src/resources/openai.rs +++ b/common/src/resources/openai.rs @@ -1,6 +1,14 @@ -use crate::io::logging::Logger; +use crate::utils::io::logging::Logger; use async_openai::{types::CreateCompletionRequestArgs, Client}; +/// Complete the given prompt using the OpenAI API. +/// +/// ``` +/// use heimdall_common::resources::openai::complete; +/// +/// let prompt = "what is love?"; +/// let api_key = "your-api-key"; +/// // complete(prompt, api_key).await; pub async fn complete(prompt: &str, api_key: &str) -> Option { let client = Client::new().with_api_key(api_key); diff --git a/common/src/resources/transpose.rs b/common/src/resources/transpose.rs index edca5a37..930d9c77 100644 --- a/common/src/resources/transpose.rs +++ b/common/src/resources/transpose.rs @@ -3,7 +3,7 @@ use reqwest::header::HeaderMap; use serde_json::Value; use std::time::{Duration, Instant}; -use crate::io::logging::Logger; +use crate::utils::io::logging::Logger; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -20,6 +20,7 @@ struct TransposeResponse { results: Vec, } +/// executes a transpose SQL query and returns the response async fn _call_transpose(query: &str, api_key: &str) -> Option { // get a new logger let logger = Logger::default(); @@ -74,6 +75,19 @@ async fn _call_transpose(query: &str, api_key: &str) -> Option, reqwest::Error>` - the response body as JSON +/// let url = "https://example.com"; +/// let timeout = 5; +/// // get_json_from_url(url, timeout).await; +/// ``` pub async fn get_json_from_url(url: &str, timeout: u64) -> Result, reqwest::Error> { _get_json_from_url(url, 0, 5, timeout).await } #[async_recursion] +/// Internal function for making a GET request to the target URL and returning the response body as +/// JSON async fn _get_json_from_url( url: &str, retry_count: u8, diff --git a/common/src/utils/integers.rs b/common/src/utils/integers.rs index 58d338b9..592842c5 100644 --- a/common/src/utils/integers.rs +++ b/common/src/utils/integers.rs @@ -3,7 +3,13 @@ pub trait ToLocaleString { } impl ToLocaleString for usize { - // add commas every 3 digits, e.g. 1000000 -> 1,000,000. + /// Add commas every 3 digits, e.g. 1000000 -> 1,000,000. + /// + /// ``` + /// use heimdall_common::utils::integers::ToLocaleString; + /// + /// assert_eq!(1000000.to_locale_string(), "1,000,000"); + /// ``` fn to_locale_string(&self) -> String { let num_str = self.to_string(); let mut result = String::new(); @@ -18,3 +24,51 @@ impl ToLocaleString for usize { result.chars().rev().collect() } } + +#[cfg(test)] +mod tests { + use crate::utils::integers::ToLocaleString; + + #[test] + fn test_to_locale_string() { + // Test case: Single-digit number + let num = 5; + let expected = "5".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Three-digit number + let num = 123; + let expected = "123".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Four-digit number + let num = 1234; + let expected = "1,234".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Five-digit number + let num = 12345; + let expected = "12,345".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Six-digit number + let num = 123456; + let expected = "123,456".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Seven-digit number + let num = 1234567; + let expected = "1,234,567".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Eight-digit number + let num = 12345678; + let expected = "12,345,678".to_string(); + assert_eq!(num.to_locale_string(), expected); + + // Test case: Nine-digit number + let num = 123456789; + let expected = "123,456,789".to_string(); + assert_eq!(num.to_locale_string(), expected); + } +} diff --git a/common/src/io/file.rs b/common/src/utils/io/file.rs similarity index 63% rename from common/src/io/file.rs rename to common/src/utils/io/file.rs index 427ec303..4395c07c 100644 --- a/common/src/io/file.rs +++ b/common/src/utils/io/file.rs @@ -7,6 +7,15 @@ use std::{ process::Command, }; +/// Convert a long path to a short path. +/// +/// ```no_run +/// use heimdall_common::utils::io::file::short_path; +/// +/// let path = "/some/long/path/that/is/cwd/something.json"; +/// let short_path = short_path(path); +/// assert_eq!(short_path, "./something.json"); +/// ``` pub fn short_path(path: &str) -> String { let current_dir = match env::current_dir() { Ok(dir) => dir.into_os_string().into_string().unwrap(), @@ -15,6 +24,15 @@ pub fn short_path(path: &str) -> String { path.replace(¤t_dir, ".") } +/// Write contents to a file on the disc +/// +/// ```no_run +/// use heimdall_common::utils::io::file::write_file; +/// +/// let path = "/tmp/test.txt"; +/// let contents = "Hello, World!"; +/// let result = write_file(path, contents); +/// ``` pub fn write_file(_path: &str, contents: &str) -> String { let path = std::path::Path::new(_path); let prefix = path.parent().unwrap(); @@ -40,10 +58,27 @@ pub fn write_file(_path: &str, contents: &str) -> String { _path.to_string() } +/// Write contents to a file on the disc +/// +/// ```no_run +/// use heimdall_common::utils::io::file::write_lines_to_file; +/// +/// let path = "/tmp/test.txt"; +/// let contents = vec![String::from("Hello"), String::from("World!")]; +/// let result = write_lines_to_file(path, contents); +/// ``` pub fn write_lines_to_file(_path: &str, contents: Vec) { write_file(_path, &contents.join("\n")); } +/// Read contents from a file on the disc +/// +/// ```no_run +/// use heimdall_common::utils::io::file::read_file; +/// +/// let path = "/tmp/test.txt"; +/// let contents = read_file(path); +/// ``` pub fn read_file(_path: &str) -> String { let path = std::path::Path::new(_path); let mut file = match File::open(path) { @@ -66,6 +101,14 @@ pub fn read_file(_path: &str) -> String { contents } +/// Delete a file from the disc +/// +/// ```no_run +/// use heimdall_common::utils::io::file::delete_path; +/// +/// let path = "/tmp/test.txt"; +/// let result = delete_path(path); +/// ``` pub fn delete_path(_path: &str) -> bool { let path = std::path::Path::new(_path); Command::new("rm").args(["-rf", path.to_str().unwrap()]).output().is_ok() diff --git a/common/src/io/logging.rs b/common/src/utils/io/logging.rs similarity index 99% rename from common/src/io/logging.rs rename to common/src/utils/io/logging.rs index 0ff286e5..8d587ecf 100644 --- a/common/src/io/logging.rs +++ b/common/src/utils/io/logging.rs @@ -5,7 +5,7 @@ use colored::*; use crate::utils::time::pretty_timestamp; -use super::super::utils::strings::replace_last; +use super::super::strings::replace_last; /// A logger which can be used to log messages to the console /// in a standardized format. @@ -46,7 +46,7 @@ pub struct Trace { impl TraceFactory { /// creates a new empty trace factory pub fn new(level: i8) -> TraceFactory { - TraceFactory { level: level, traces: Vec::new() } + TraceFactory { level, traces: Vec::new() } } /// adds a new trace to the factory diff --git a/common/src/io/mod.rs b/common/src/utils/io/mod.rs similarity index 100% rename from common/src/io/mod.rs rename to common/src/utils/io/mod.rs diff --git a/common/src/utils/mod.rs b/common/src/utils/mod.rs index 8b686a03..910066c1 100644 --- a/common/src/utils/mod.rs +++ b/common/src/utils/mod.rs @@ -1,8 +1,9 @@ pub mod http; pub mod integers; +pub mod io; pub mod strings; pub mod sync; -mod tests; +pub mod testing; pub mod threading; pub mod time; pub mod version; diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index 4b53bad4..feb5595f 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -9,41 +9,18 @@ use fancy_regex::Regex; use crate::constants::REDUCE_HEX_REGEX; /// Converts a signed integer into an unsigned integer -/// -/// ## Arguments -/// signed: I256 - the signed integer to convert -/// -/// ## Returns -/// U256 - the unsigned integer -/// -/// ## Example -/// ```no_run -/// use ethers::prelude::{I256, U256}; -/// use heimdall_core::utils::strings::unsign_int; -/// -/// let signed = I256::from(-1); -/// let unsigned = unsign_int(signed); -/// -/// assert_eq!(unsigned, U256::from(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)); -/// ``` pub fn sign_uint(unsigned: U256) -> I256 { I256::from_raw(unsigned) } /// Decodes a hex string into a vector of bytes /// -/// ## Arguments -/// s: &str - the hex string to decode -/// -/// ## Returns -/// Result, ParseIntError> - the decoded vector of bytes -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::decode_hex; +/// ``` +/// use heimdall_common::utils::strings::decode_hex; /// -/// let decoded = decode_hex("00010203"); -/// assert_eq!(decoded, Ok(vec![0, 1, 2, 3])); +/// let hex = "48656c6c6f20576f726c64"; // "Hello World" in hex +/// let result = decode_hex(hex); +/// assert_eq!(result, Ok(vec![72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])); /// ``` pub fn decode_hex(s: &str) -> Result, ParseIntError> { (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16)).collect() @@ -51,18 +28,12 @@ pub fn decode_hex(s: &str) -> Result, ParseIntError> { /// Encodes a vector of bytes into a hex string /// -/// ## Arguments -/// s: Vec - the vector of bytes to encode -/// -/// ## Returns -/// String - the encoded hex string -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::encode_hex; +/// ``` +/// use heimdall_common::utils::strings::encode_hex; /// -/// let encoded = encode_hex(vec![0, 1, 2, 3]); -/// assert_eq!(encoded, String::from("00010203")); +/// let bytes = vec![72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]; +/// let result = encode_hex(bytes); +/// assert_eq!(result, "48656c6c6f20576f726c64"); /// ``` pub fn encode_hex(s: Vec) -> String { s.iter().fold(String::new(), |mut acc, b| { @@ -73,22 +44,13 @@ pub fn encode_hex(s: Vec) -> String { /// Encodes a U256 into a hex string, removing leading zeros /// -/// ## Arguments -/// s: U256 - the U256 to encode -/// -/// ## Returns -/// String - the encoded hex string -/// -/// ## Example -/// ```no_run -/// use ethers::prelude::U256; -/// use heimdall_core::utils::strings::encode_hex_reduced; -/// -/// let encoded = encode_hex_reduced(U256::from(0)); -/// assert_eq!(encoded, String::from("0")); +/// ``` +/// use ethers::types::U256; +/// use heimdall_common::utils::strings::encode_hex_reduced; /// -/// let encoded = encode_hex_reduced(U256::from(1)); -/// assert_eq!(encoded, String::from("0x01")); +/// let u256 = U256::max_value(); +/// let result = encode_hex_reduced(u256); +/// assert_eq!(result, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); /// ``` pub fn encode_hex_reduced(s: U256) -> String { if s > U256::from(0) { @@ -100,18 +62,12 @@ pub fn encode_hex_reduced(s: U256) -> String { /// Converts a hex string to an ASCII string /// -/// ## Arguments -/// s: &str - the hex string to convert -/// -/// ## Returns -/// String - the ASCII string -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::hex_to_ascii; +/// ``` +/// use heimdall_common::utils::strings::hex_to_ascii; /// -/// let ascii = hex_to_ascii("0x68656c6c6f20776f726c64"); -/// assert_eq!(ascii, String::from("hello world")); +/// let hex = "48656c6c6f20576f726c64"; // "Hello World" in hex +/// let result = hex_to_ascii(hex); +/// assert_eq!(result, "Hello World"); /// ``` pub fn hex_to_ascii(s: &str) -> String { let mut result = String::new(); @@ -129,23 +85,14 @@ pub fn hex_to_ascii(s: &str) -> String { /// Replaces the last occurrence of a substring in a string /// -/// ## Arguments -/// s: String - the string to search -/// old: &str - the substring to replace -/// new: &str - the substring to replace with -/// -/// ## Returns -/// String - the resulting string -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::replace_last; -/// -/// let replaced = replace_last(String::from("arg0 + arg1"), "arg1", "arg2"); -/// assert_eq!(replaced, String::from("arg0 + arg2")); +/// ``` +/// use heimdall_common::utils::strings::replace_last; /// -/// let replaced = replace_last(String::from("arg0 + arg1 + arg1"), "arg1", "arg2"); -/// assert_eq!(replaced, String::from("arg0 + arg1 + arg2")); +/// let s = "Hello, world!"; +/// let old = "o"; +/// let new = "0"; +/// let result = replace_last(s, old, new); +/// assert_eq!(result, String::from("Hello, w0rld!")); /// ``` pub fn replace_last(s: &str, old: &str, new: &str) -> String { let new = new.chars().rev().collect::(); @@ -154,22 +101,12 @@ pub fn replace_last(s: &str, old: &str, new: &str) -> String { /// Finds balanced encapsulator in a string /// -/// ## Arguments -/// s: String - the string to search -/// encap: (char, char) - the encapsulator to search for -/// -/// ## Returns -/// (usize, usize, bool) - the start and end indices of the balanced encapsulator, and whether or -/// not it was found -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::find_balanced_encapsulator; +/// ``` +/// use heimdall_common::utils::strings::find_balanced_encapsulator; /// -/// let (start, end, is_balanced) = find_balanced_encapsulator(String::from("arg0 + arg1"), ('(', ')')); -/// assert_eq!(start, 0); -/// assert_eq!(end, 9); -/// assert_eq!(is_balanced, true); +/// let s = "Hello (World)"; +/// let result = find_balanced_encapsulator(s, ('(', ')')); +/// assert_eq!(result, (6, 13, true)); /// ``` pub fn find_balanced_encapsulator(s: &str, encap: (char, char)) -> (usize, usize, bool) { let mut open = 0; @@ -195,22 +132,12 @@ pub fn find_balanced_encapsulator(s: &str, encap: (char, char)) -> (usize, usize /// Finds balanced parentheses in a string, starting from the end /// -/// ## Arguments -/// s: String - the string to search -/// encap: (char, char) - the encapsulator to search for -/// -/// ## Returns -/// (usize, usize, bool) - the start and end indices of the balanced parentheses, and whether or not -/// they were found -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::find_balanced_encapsulator_backwards; +/// ``` +/// use heimdall_common::utils::strings::find_balanced_encapsulator_backwards; /// -/// let (start, end, is_balanced) = find_balanced_encapsulator_backwards(String::from("arg0 + arg1"), ('(', ')')); -/// assert_eq!(start, 0); -/// assert_eq!(end, 9); -/// assert_eq!(is_balanced, true); +/// let s = "Hello (World)"; +/// let result = find_balanced_encapsulator_backwards(s, ('(', ')')); +/// assert_eq!(result, (6, 13, true)); /// ``` pub fn find_balanced_encapsulator_backwards(s: &str, encap: (char, char)) -> (usize, usize, bool) { let mut open = 0; @@ -236,24 +163,12 @@ pub fn find_balanced_encapsulator_backwards(s: &str, encap: (char, char)) -> (us /// Encodes a number into a base26 string /// -/// ## Arguments -/// n: usize - the number to encode -/// -/// ## Returns -/// String - the encoded string -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::base26_encode; -/// -/// let encoded = base26_encode(0); -/// assert_eq!(encoded, String::from("a")); -/// -/// let encoded = base26_encode(25); -/// assert_eq!(encoded, String::from("z")); +/// ``` +/// use heimdall_common::utils::strings::base26_encode; /// -/// let encoded = base26_encode(26); -/// assert_eq!(encoded, String::from("aa")); +/// let n = 123456789; +/// let result = base26_encode(n); +/// assert_eq!(result, "jjddja"); /// ``` pub fn base26_encode(n: usize) -> String { let mut s = String::new(); @@ -267,23 +182,6 @@ pub fn base26_encode(n: usize) -> String { } /// Splits a string by a regular expression -/// -/// ## Arguments -/// input: &str - the string to split -/// pattern: Regex - the regular expression to split by -/// -/// ## Returns -/// Vec - the vector of substrings -/// -/// ## Example -/// ```no_run -/// use fancy_regex::Regex; -/// use heimdall_core::utils::strings::split_string_by_regex; -/// -/// let pattern = Regex::new(r"\s+").unwrap(); -/// let substrings = split_string_by_regex("arg0 + arg1", pattern); -/// assert_eq!(substrings, vec!["arg0", "+", "arg1"]); -/// ``` pub fn split_string_by_regex(input: &str, pattern: Regex) -> Vec { // Find all matches of the pattern in the input string let matches = pattern.find_iter(input); @@ -310,27 +208,12 @@ pub fn split_string_by_regex(input: &str, pattern: Regex) -> Vec { /// Extracts the condition from a require() or if() statement /// -/// ## Arguments -/// s: &str - the string to extract the condition from -/// keyword: &str - the keyword to search for, either "require" or "if" -/// -/// ## Returns -/// Option - the extracted condition, if found -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::extract_condition; -/// -/// let condition = extract_condition("require(arg0 > 0)", "require"); -/// assert_eq!(condition, Some(String::from("arg0 > 0"))); /// ``` +/// use heimdall_common::utils::strings::extract_condition; /// -/// ## Example 2 -/// ```no_run -/// use heimdall_core::utils::strings::extract_condition; -/// -/// let condition = extract_condition("if (arg0 > 0) {", "if"); -/// assert_eq!(condition, Some(String::from("arg0 > 0"))); +/// let s = "require(a == b)"; +/// let result = extract_condition(s, "require"); +/// assert_eq!(result, Some("a == b".to_string())); /// ``` pub fn extract_condition(s: &str, keyword: &str) -> Option { // find the keyword @@ -359,24 +242,12 @@ pub fn extract_condition(s: &str, keyword: &str) -> Option { /// Tokenizes an expression into a vector of tokens /// -/// ## Arguments -/// s: &str - the expression to tokenize -/// -/// ## Returns -/// Vec - the vector of tokens -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::tokenize; -/// -/// let tokens = tokenize("arg0 + arg1"); -/// assert_eq!(tokens, vec!["arg0", "+", "arg1"]); -/// -/// let tokens = tokenize("(arg0 + arg1) > (msg.value + 1)"); -/// assert_eq!(tokens, vec!["(", "arg0", "+", "arg1", ")", ">", "(", "msg.value", "+", "1", ")"]); +/// ``` +/// use heimdall_common::utils::strings::tokenize; /// -/// let tokens = tokenize("if (arg0 >= 0) {"); -/// assert_eq!(tokens, vec!["if", "(", "arg0", ">=", "0", ")", "{"]); +/// let s = "a + b * c"; +/// let result = tokenize(s); +/// assert_eq!(result, vec!["a", "+", "b", "*", "c"]); /// ``` pub fn tokenize(s: &str) -> Vec { let mut tokens = Vec::new(); @@ -449,49 +320,6 @@ pub enum TokenType { /// Classifies a token as a variable, constant, operator, or function call, and returns its /// precedence -/// -/// ## Arguments -/// token: &str - the token to classify -/// -/// ## Returns -/// (String, usize) - the token's classification, and precedence -/// -/// ## Example -/// ```no_run -/// use heimdall_core::utils::strings::classify_token; -/// -/// let (classification, precedence) = classify_token("0x01"); -/// assert_eq!(classification, TokenType::Constant); -/// assert_eq!(precedence, 0); -/// -/// let (classification, precedence) = classify_token("arg0"); -/// assert_eq!(classification, TokenType::Variable); -/// assert_eq!(precedence, 0); -/// -/// let (classification, precedence) = classify_token("+"); -/// assert_eq!(classification, TokenType::Operator); -/// assert_eq!(precedence, 1); -/// -/// let (classification, precedence) = classify_token("*"); -/// assert_eq!(classification, TokenType::Operator); -/// assert_eq!(precedence, 2); -/// -/// let (classification, precedence) = classify_token(">"); -/// assert_eq!(classification, TokenType::Operator); -/// assert_eq!(precedence, 2); -/// -/// let (classification, precedence) = classify_token("=="); -/// assert_eq!(classification, TokenType::Operator); -/// assert_eq!(precedence, 2); -/// -/// let (classification, precedence) = classify_token("memory[0x01]"); -/// assert_eq!(classification, TokenType::Variable); -/// -/// let (classification, precedence) = classify_token("uint256"); -/// assert_eq!(classification, TokenType::Function); -/// -/// let (classification, precedence) = classify_token("keccak256"); -/// assert_eq!(classification, Token::Function); pub fn classify_token(token: &str) -> TokenType { // return if the token is a parenthesis if token == "(" || token == ")" { @@ -523,3 +351,344 @@ pub fn classify_token(token: &str) -> TokenType { // this token must be a function call TokenType::Function } + +#[cfg(test)] +mod tests { + use ethers::types::{I256, U256}; + + use crate::utils::strings::*; + + #[test] + fn test_sign_uint() { + let unsigned = U256::from(10); + let signed = sign_uint(unsigned); + assert_eq!(signed, I256::from(10)); + + let unsigned = U256::from(0); + let signed = sign_uint(unsigned); + assert_eq!(signed, I256::from(0)); + + let unsigned = U256::from(1000); + let signed = sign_uint(unsigned); + assert_eq!(signed, I256::from(1000)); + } + + #[test] + fn test_decode_hex() { + let hex = "48656c6c6f20776f726c64"; // "Hello world" + let result = decode_hex(hex); + assert_eq!(result, Ok(vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])); + + let hex = "abcdef"; + let result = decode_hex(hex); + assert_eq!(result, Ok(vec![171, 205, 239])); + + let hex = "012345"; + let result = decode_hex(hex); + assert_eq!(result, Ok(vec![1, 35, 69])); + } + + #[test] + fn test_encode_hex() { + let bytes = vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; // "Hello world" + let result = encode_hex(bytes); + assert_eq!(result, "48656c6c6f20776f726c64"); + + let bytes = vec![171, 205, 239]; + let result = encode_hex(bytes); + assert_eq!(result, "abcdef"); + + let bytes = vec![1, 35, 69]; + let result = encode_hex(bytes); + assert_eq!(result, "012345"); + } + + #[test] + fn test_encode_hex_reduced() { + let hex = U256::from(10); + let result = encode_hex_reduced(hex); + assert_eq!(result, "0x0a"); + + let hex = U256::from(0); + let result = encode_hex_reduced(hex); + assert_eq!(result, "0"); + + let hex = U256::from(1000); + let result = encode_hex_reduced(hex); + assert_eq!(result, "0x03e8"); + } + + #[test] + fn test_hex_to_ascii() { + let hex = "48656c6c6f20776f726c64"; // "Hello world" + let result = hex_to_ascii(hex); + assert_eq!(result, "Hello world"); + + let hex = "616263646566"; // "abcdef" + let result = hex_to_ascii(hex); + assert_eq!(result, "abcdef"); + + let hex = "303132333435"; // "012345" + let result = hex_to_ascii(hex); + assert_eq!(result, "012345"); + } + + #[test] + fn test_replace_last() { + let s = "Hello, world!"; + let old = "o"; + let new = "0"; + let result = replace_last(s, old, new); + assert_eq!(result, String::from("Hello, w0rld!")); + + let s = "Hello, world!"; + let old = "l"; + let new = "L"; + let result = replace_last(s, old, new); + assert_eq!(result, String::from("Hello, worLd!")); + } + + #[test] + fn test_find_balanced_encapsulator() { + let s = String::from("This is (an example) string."); + let encap = ('(', ')'); + let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); + assert_eq!(start, 8); + assert_eq!(end, 20); + assert!(is_balanced); + + let s = String::from("This is an example) string."); + let encap = ('(', ')'); + let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); + assert_eq!(start, 0); + assert_eq!(end, 1); + assert!(!is_balanced); + + let s = String::from("This is (an example string."); + let encap = ('(', ')'); + let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); + assert_eq!(start, 8); + assert_eq!(end, 1); + assert!(!is_balanced); + } + + #[test] + fn test_find_balanced_encapsulator_backwards() { + let s = String::from("This is (an example) string."); + let encap = ('(', ')'); + let (start, end, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); + assert_eq!(start, 8); + assert_eq!(end, 20); + assert!(is_balanced); + + let s = String::from("This is an example) string."); + let encap = ('(', ')'); + let (_, _, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); + assert!(!is_balanced); + + let s = String::from("This is (an example string."); + let encap = ('(', ')'); + let (_, _, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); + assert!(!is_balanced); + } + + #[test] + fn test_base26_encode() { + let n = 1; + let result = base26_encode(n); + assert_eq!(result, "a"); + + let n = 26; + let result = base26_encode(n); + assert_eq!(result, "z"); + + let n = 27; + let result = base26_encode(n); + assert_eq!(result, "aa"); + + let n = 703; + let result = base26_encode(n); + assert_eq!(result, "aaa"); + } + + #[test] + fn test_split_string_by_regex() { + let input = "Hello,world!"; + let pattern = fancy_regex::Regex::new(r",").unwrap(); + let result = split_string_by_regex(input, pattern); + assert_eq!(result, vec!["Hello", "world!"]); + + let input = "This is a test."; + let pattern = fancy_regex::Regex::new(r"\s").unwrap(); + let result = split_string_by_regex(input, pattern); + assert_eq!(result, vec!["This", "is", "a", "test."]); + + let input = "The quick brown fox jumps over the lazy dog."; + let pattern = fancy_regex::Regex::new(r"\s+").unwrap(); + let result = split_string_by_regex(input, pattern); + assert_eq!( + result, + vec!["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."] + ); + } + + #[test] + fn test_extract_condition_present_balanced() { + let s = "require(arg0 == (address(arg0)));"; + let keyword = "require"; + let expected = Some("arg0 == (address(arg0))".to_string()); + assert_eq!(extract_condition(s, keyword), expected); + } + + #[test] + fn test_extract_condition_present_unbalanced() { + let s = "require(arg0 == (address(arg0));"; + let keyword = "require"; + let expected = None; + assert_eq!(extract_condition(s, keyword), expected); + } + + #[test] + fn test_extract_condition_not_present() { + let s = "if (0x01 < var_c.length) {"; + let keyword = "require"; + let expected = None; + assert_eq!(extract_condition(s, keyword), expected); + } + + #[test] + fn test_extract_condition_multiple_keywords() { + let s = "require(var_c.length == var_c.length, \"some revert message\");"; + let keyword = "require"; + let expected = Some("var_c.length == var_c.length".to_string()); + assert_eq!(extract_condition(s, keyword), expected); + } + + #[test] + fn test_extract_condition_empty_string() { + let s = ""; + let keyword = "require"; + let expected = None; + assert_eq!(extract_condition(s, keyword), expected); + } + + #[test] + fn test_tokenize_basic_operators() { + let tokens = tokenize("arg0 + arg1"); + assert_eq!(tokens, vec!["arg0", "+", "arg1"]); + } + + #[test] + fn test_tokenize_parentheses_and_operators() { + let tokens = tokenize("(arg0 + arg1) > (msg.value + 1)"); + assert_eq!( + tokens, + vec!["(", "arg0", "+", "arg1", ")", ">", "(", "msg.value", "+", "1", ")"] + ); + } + + #[test] + fn test_tokenize_multiple_operators() { + let tokens = tokenize("a >= b && c != d"); + assert_eq!(tokens, vec!["a", ">=", "b", "&&", "c", "!=", "d"]); + } + + #[test] + fn test_tokenize_no_spaces() { + let tokens = tokenize("a+b-c*d/e"); + assert_eq!(tokens, vec!["a", "+", "b", "-", "c", "*", "d", "/", "e"]); + } + + #[test] + fn test_tokenize_whitespace_only() { + let tokens = tokenize(" "); + assert_eq!(tokens, Vec::::new()); + } + + #[test] + fn test_tokenize_empty_string() { + let tokens = tokenize(""); + assert_eq!(tokens, Vec::::new()); + } + + #[test] + fn test_tokenize_complex_expression() { + let tokens = tokenize("if (x > 10 && y < 20) || z == 0 { a = b + c }"); + assert_eq!( + tokens, + vec![ + "if", "(", "x", ">", "10", "&&", "y", "<", "20", ")", "||", "z", "==", "0", "{", + "a", "=", "b", "+", "c", "}" + ] + ); + } + + #[test] + fn test_tokenize_separators_at_start_and_end() { + let tokens = tokenize("==text=="); + assert_eq!(tokens, vec!["==", "text", "=="]); + } + + #[test] + fn test_classify_token_parenthesis() { + let classification = classify_token("("); + assert_eq!(classification, TokenType::Control); + + let classification = classify_token(")"); + assert_eq!(classification, TokenType::Control); + } + + #[test] + fn test_classify_token_operators_precedence_1() { + for operator in ["+", "-"].iter() { + let classification = classify_token(operator); + assert_eq!(classification, TokenType::Operator); + } + } + + #[test] + fn test_classify_token_operators_precedence_2() { + for operator in + ["*", "/", "%", "|", "&", "^", "==", ">=", "<=", "!=", "!", "&&", "||"].iter() + { + let classification = classify_token(operator); + assert_eq!(classification, TokenType::Operator); + } + } + + #[test] + fn test_classify_token_constant() { + let classification = classify_token("0x001234567890"); + assert_eq!(classification, TokenType::Constant); + } + + #[test] + fn test_classify_token_variable() { + for variable in [ + "memory[0x01]", + "storage", + "var", + "msg.value", + "block.timestamp", + "this.balance", + "tx.origin", + "arg0", + "ret", + "calldata", + "abi.encode", + ] + .iter() + { + let classification = classify_token(variable); + assert_eq!(classification, TokenType::Variable); + } + } + + #[test] + fn test_classify_token_function() { + for function in ["uint256", "address", "ecrecover", "if"].iter() { + let classification = classify_token(function); + assert_eq!(classification, TokenType::Function); + } + } +} diff --git a/common/src/testing/benchmarks.rs b/common/src/utils/testing/benchmarks.rs similarity index 81% rename from common/src/testing/benchmarks.rs rename to common/src/utils/testing/benchmarks.rs index e2dbd41c..ec6e36ed 100644 --- a/common/src/testing/benchmarks.rs +++ b/common/src/utils/testing/benchmarks.rs @@ -1,6 +1,8 @@ use std::{io, io::Write, thread, time::Instant}; #[allow(dead_code)] +/// asyncronous version of the benchmark function. will execute the function to_bench +/// `runs` times, and print the mean and standard deviation of the run times. pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { let mut time = 0usize; let mut times = Vec::with_capacity(runs); @@ -46,6 +48,8 @@ pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { } #[allow(dead_code)] +/// asyncronous version of the benchmark function. will execute the function to_bench +/// `runs` times, and print the mean and standard deviation of the run times. pub async fn async_bench(benchmark_name: &str, runs: usize, to_bench: F) where F: Fn() -> Fut, @@ -94,6 +98,7 @@ where } #[allow(dead_code)] +/// helper function to format nanoseconds into a human readable format fn format_nanos(nanos: usize) -> String { let mut nanos = nanos; let mut micros = 0; @@ -155,3 +160,22 @@ fn format_nanos(nanos: usize) -> String { result } + +#[cfg(test)] +mod tests { + use std::thread; + + use crate::utils::testing::benchmarks::benchmark; + + #[test] + fn test_benchmark() { + // Test case: Single run + let benchmark_name = "Test Benchmark"; + let runs = 10; + let to_bench = || { + // Code to benchmark + thread::sleep(std::time::Duration::from_millis(200)); + }; + benchmark(benchmark_name, runs, to_bench); + } +} diff --git a/common/src/testing/mod.rs b/common/src/utils/testing/mod.rs similarity index 64% rename from common/src/testing/mod.rs rename to common/src/utils/testing/mod.rs index e5ef9c16..53b3f4db 100644 --- a/common/src/testing/mod.rs +++ b/common/src/utils/testing/mod.rs @@ -1,2 +1 @@ pub mod benchmarks; -mod tests; diff --git a/common/src/utils/tests.rs b/common/src/utils/tests.rs deleted file mode 100644 index b5c69f78..00000000 --- a/common/src/utils/tests.rs +++ /dev/null @@ -1,558 +0,0 @@ -#[cfg(test)] -mod test_integers { - use crate::utils::integers::ToLocaleString; - - #[test] - fn test_to_locale_string() { - // Test case: Single-digit number - let num = 5; - let expected = "5".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Three-digit number - let num = 123; - let expected = "123".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Four-digit number - let num = 1234; - let expected = "1,234".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Five-digit number - let num = 12345; - let expected = "12,345".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Six-digit number - let num = 123456; - let expected = "123,456".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Seven-digit number - let num = 1234567; - let expected = "1,234,567".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Eight-digit number - let num = 12345678; - let expected = "12,345,678".to_string(); - assert_eq!(num.to_locale_string(), expected); - - // Test case: Nine-digit number - let num = 123456789; - let expected = "123,456,789".to_string(); - assert_eq!(num.to_locale_string(), expected); - } -} - -#[cfg(test)] -mod test_strings { - use ethers::types::{I256, U256}; - - use crate::utils::strings::*; - - #[test] - fn test_sign_uint() { - let unsigned = U256::from(10); - let signed = sign_uint(unsigned); - assert_eq!(signed, I256::from(10)); - - let unsigned = U256::from(0); - let signed = sign_uint(unsigned); - assert_eq!(signed, I256::from(0)); - - let unsigned = U256::from(1000); - let signed = sign_uint(unsigned); - assert_eq!(signed, I256::from(1000)); - } - - #[test] - fn test_decode_hex() { - let hex = "48656c6c6f20776f726c64"; // "Hello world" - let result = decode_hex(hex); - assert_eq!(result, Ok(vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])); - - let hex = "abcdef"; - let result = decode_hex(hex); - assert_eq!(result, Ok(vec![171, 205, 239])); - - let hex = "012345"; - let result = decode_hex(hex); - assert_eq!(result, Ok(vec![1, 35, 69])); - } - - #[test] - fn test_encode_hex() { - let bytes = vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; // "Hello world" - let result = encode_hex(bytes); - assert_eq!(result, "48656c6c6f20776f726c64"); - - let bytes = vec![171, 205, 239]; - let result = encode_hex(bytes); - assert_eq!(result, "abcdef"); - - let bytes = vec![1, 35, 69]; - let result = encode_hex(bytes); - assert_eq!(result, "012345"); - } - - #[test] - fn test_encode_hex_reduced() { - let hex = U256::from(10); - let result = encode_hex_reduced(hex); - assert_eq!(result, "0x0a"); - - let hex = U256::from(0); - let result = encode_hex_reduced(hex); - assert_eq!(result, "0"); - - let hex = U256::from(1000); - let result = encode_hex_reduced(hex); - assert_eq!(result, "0x03e8"); - } - - #[test] - fn test_hex_to_ascii() { - let hex = "48656c6c6f20776f726c64"; // "Hello world" - let result = hex_to_ascii(hex); - assert_eq!(result, "Hello world"); - - let hex = "616263646566"; // "abcdef" - let result = hex_to_ascii(hex); - assert_eq!(result, "abcdef"); - - let hex = "303132333435"; // "012345" - let result = hex_to_ascii(hex); - assert_eq!(result, "012345"); - } - - #[test] - fn test_replace_last() { - let s = "Hello, world!"; - let old = "o"; - let new = "0"; - let result = replace_last(s, old, new); - assert_eq!(result, String::from("Hello, w0rld!")); - - let s = "Hello, world!"; - let old = "l"; - let new = "L"; - let result = replace_last(s, old, new); - assert_eq!(result, String::from("Hello, worLd!")); - } - - #[test] - fn test_find_balanced_encapsulator() { - let s = String::from("This is (an example) string."); - let encap = ('(', ')'); - let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); - assert_eq!(start, 8); - assert_eq!(end, 20); - assert!(is_balanced); - - let s = String::from("This is an example) string."); - let encap = ('(', ')'); - let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); - assert_eq!(start, 0); - assert_eq!(end, 1); - assert!(!is_balanced); - - let s = String::from("This is (an example string."); - let encap = ('(', ')'); - let (start, end, is_balanced) = find_balanced_encapsulator(&s, encap); - assert_eq!(start, 8); - assert_eq!(end, 1); - assert!(!is_balanced); - } - - #[test] - fn test_find_balanced_encapsulator_backwards() { - let s = String::from("This is (an example) string."); - let encap = ('(', ')'); - let (start, end, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); - assert_eq!(start, 8); - assert_eq!(end, 20); - assert!(is_balanced); - - let s = String::from("This is an example) string."); - let encap = ('(', ')'); - let (_, _, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); - assert!(!is_balanced); - - let s = String::from("This is (an example string."); - let encap = ('(', ')'); - let (_, _, is_balanced) = find_balanced_encapsulator_backwards(&s, encap); - assert!(!is_balanced); - } - - #[test] - fn test_base26_encode() { - let n = 1; - let result = base26_encode(n); - assert_eq!(result, "a"); - - let n = 26; - let result = base26_encode(n); - assert_eq!(result, "z"); - - let n = 27; - let result = base26_encode(n); - assert_eq!(result, "aa"); - - let n = 703; - let result = base26_encode(n); - assert_eq!(result, "aaa"); - } - - #[test] - fn test_split_string_by_regex() { - let input = "Hello,world!"; - let pattern = fancy_regex::Regex::new(r",").unwrap(); - let result = split_string_by_regex(input, pattern); - assert_eq!(result, vec!["Hello", "world!"]); - - let input = "This is a test."; - let pattern = fancy_regex::Regex::new(r"\s").unwrap(); - let result = split_string_by_regex(input, pattern); - assert_eq!(result, vec!["This", "is", "a", "test."]); - - let input = "The quick brown fox jumps over the lazy dog."; - let pattern = fancy_regex::Regex::new(r"\s+").unwrap(); - let result = split_string_by_regex(input, pattern); - assert_eq!( - result, - vec!["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."] - ); - } - - #[test] - fn test_extract_condition_present_balanced() { - let s = "require(arg0 == (address(arg0)));"; - let keyword = "require"; - let expected = Some("arg0 == (address(arg0))".to_string()); - assert_eq!(extract_condition(s, keyword), expected); - } - - #[test] - fn test_extract_condition_present_unbalanced() { - let s = "require(arg0 == (address(arg0));"; - let keyword = "require"; - let expected = None; - assert_eq!(extract_condition(s, keyword), expected); - } - - #[test] - fn test_extract_condition_not_present() { - let s = "if (0x01 < var_c.length) {"; - let keyword = "require"; - let expected = None; - assert_eq!(extract_condition(s, keyword), expected); - } - - #[test] - fn test_extract_condition_multiple_keywords() { - let s = "require(var_c.length == var_c.length, \"some revert message\");"; - let keyword = "require"; - let expected = Some("var_c.length == var_c.length".to_string()); - assert_eq!(extract_condition(s, keyword), expected); - } - - #[test] - fn test_extract_condition_empty_string() { - let s = ""; - let keyword = "require"; - let expected = None; - assert_eq!(extract_condition(s, keyword), expected); - } - - #[test] - fn test_tokenize_basic_operators() { - let tokens = tokenize("arg0 + arg1"); - assert_eq!(tokens, vec!["arg0", "+", "arg1"]); - } - - #[test] - fn test_tokenize_parentheses_and_operators() { - let tokens = tokenize("(arg0 + arg1) > (msg.value + 1)"); - assert_eq!( - tokens, - vec!["(", "arg0", "+", "arg1", ")", ">", "(", "msg.value", "+", "1", ")"] - ); - } - - #[test] - fn test_tokenize_multiple_operators() { - let tokens = tokenize("a >= b && c != d"); - assert_eq!(tokens, vec!["a", ">=", "b", "&&", "c", "!=", "d"]); - } - - #[test] - fn test_tokenize_no_spaces() { - let tokens = tokenize("a+b-c*d/e"); - assert_eq!(tokens, vec!["a", "+", "b", "-", "c", "*", "d", "/", "e"]); - } - - #[test] - fn test_tokenize_whitespace_only() { - let tokens = tokenize(" "); - assert_eq!(tokens, Vec::::new()); - } - - #[test] - fn test_tokenize_empty_string() { - let tokens = tokenize(""); - assert_eq!(tokens, Vec::::new()); - } - - #[test] - fn test_tokenize_complex_expression() { - let tokens = tokenize("if (x > 10 && y < 20) || z == 0 { a = b + c }"); - assert_eq!( - tokens, - vec![ - "if", "(", "x", ">", "10", "&&", "y", "<", "20", ")", "||", "z", "==", "0", "{", - "a", "=", "b", "+", "c", "}" - ] - ); - } - - #[test] - fn test_tokenize_separators_at_start_and_end() { - let tokens = tokenize("==text=="); - assert_eq!(tokens, vec!["==", "text", "=="]); - } - - #[test] - fn test_classify_token_parenthesis() { - let classification = classify_token("("); - assert_eq!(classification, TokenType::Control); - - let classification = classify_token(")"); - assert_eq!(classification, TokenType::Control); - } - - #[test] - fn test_classify_token_operators_precedence_1() { - for operator in ["+", "-"].iter() { - let classification = classify_token(operator); - assert_eq!(classification, TokenType::Operator); - } - } - - #[test] - fn test_classify_token_operators_precedence_2() { - for operator in - ["*", "/", "%", "|", "&", "^", "==", ">=", "<=", "!=", "!", "&&", "||"].iter() - { - let classification = classify_token(operator); - assert_eq!(classification, TokenType::Operator); - } - } - - #[test] - fn test_classify_token_constant() { - let classification = classify_token("0x001234567890"); - assert_eq!(classification, TokenType::Constant); - } - - #[test] - fn test_classify_token_variable() { - for variable in [ - "memory[0x01]", - "storage", - "var", - "msg.value", - "block.timestamp", - "this.balance", - "tx.origin", - "arg0", - "ret", - "calldata", - "abi.encode", - ] - .iter() - { - let classification = classify_token(variable); - assert_eq!(classification, TokenType::Variable); - } - } - - #[test] - fn test_classify_token_function() { - for function in ["uint256", "address", "ecrecover", "if"].iter() { - let classification = classify_token(function); - assert_eq!(classification, TokenType::Function); - } - } -} - -#[cfg(test)] -mod test_threading { - use crate::utils::threading::*; - - #[test] - fn test_task_pool_with_single_thread() { - // Test case with a single thread - let items = vec![1, 2, 3, 4, 5]; - let num_threads = 1; - let expected_results = vec![2, 4, 6, 8, 10]; - - // Define a simple function to double the input - let f = |x: i32| x * 2; - - let mut results = task_pool(items, num_threads, f); - results.sort(); - assert_eq!(results, expected_results); - } - - #[test] - fn test_task_pool_with_multiple_threads() { - // Test case with multiple threads - let items = vec![1, 2, 3, 4, 5]; - let num_threads = 3; - let expected_results = vec![2, 4, 6, 8, 10]; - - // Define a simple function to double the input - let f = |x: i32| x * 2; - - let mut results = task_pool(items, num_threads, f); - results.sort(); - assert_eq!(results, expected_results); - } - - #[test] - fn test_task_pool_with_empty_items() { - // Test case with empty items vector - let items: Vec = Vec::new(); - let num_threads = 2; - - // Define a simple function to double the input - let f = |x: i32| x * 2; - - let results = task_pool(items, num_threads, f); - assert!(results.is_empty()); - } -} - -#[cfg(test)] -mod test_time { - use crate::utils::time::*; - - #[test] - fn test_calculate_eta() { - assert_eq!(calculate_eta(2.5, 10), 4); - assert_eq!(calculate_eta(0.5, 100), 200); - assert_eq!(calculate_eta(1.0, 0), 0); - assert_eq!(calculate_eta(0.0, 100), std::u128::MAX); - assert_eq!(calculate_eta(10.0, std::usize::MAX), 1844674407370955264); - } - - #[test] - fn test_format_eta() { - assert_eq!(format_eta(0), "0s"); - assert_eq!(format_eta(59), "59s "); - assert_eq!(format_eta(60), "1m 0s"); - assert_eq!(format_eta(3600), "1h 0s"); - assert_eq!(format_eta(3665), "1h 1m 5s "); - assert_eq!(format_eta(86400), "1d 0s"); - assert_eq!(format_eta(172800), "2d 0s"); - assert_eq!(format_eta(180065), "2d 2h 1m 5s "); - } -} - -#[cfg(test)] -mod test_version { - use crate::utils::version::*; - - #[test] - fn test_greater_than() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 3 }; - let v3 = Version { major: 2, minor: 2, patch: 5 }; - let v4 = Version { major: 1, minor: 4, patch: 4 }; - - assert!(v1.gt(&v2)); - assert!(v1.gt(&v3)); - assert!(v1.gt(&v4)); - assert!(!v2.gt(&v1)); - assert!(!v1.gt(&v1)); - } - - #[test] - fn test_greater_than_or_equal_to() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 4 }; - - assert!(v1.gte(&v2)); - assert!(v2.gte(&v1)); - assert!(v1.gte(&Version { major: 1, minor: 0, patch: 0 })); - } - - #[test] - fn test_less_than() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 5 }; - let v3 = Version { major: 2, minor: 4, patch: 4 }; - let v4 = Version { major: 3, minor: 3, patch: 4 }; - - assert!(v1.lt(&v2)); - assert!(v1.lt(&v3)); - assert!(v1.lt(&v4)); - assert!(!v2.lt(&v1)); - assert!(!v1.lt(&v1)); - } - - #[test] - fn test_less_than_or_equal_to() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 4 }; - - assert!(v1.lte(&v2)); - assert!(v2.lte(&v1)); - assert!(v1.lte(&Version { major: 3, minor: 0, patch: 0 })); - } - - #[test] - fn test_equal_to() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 4 }; - let v3 = Version { major: 2, minor: 3, patch: 5 }; - - assert!(v1.eq(&v2)); - assert!(!v1.eq(&v3)); - } - - #[test] - fn test_not_equal_to() { - let v1 = Version { major: 2, minor: 3, patch: 4 }; - let v2 = Version { major: 2, minor: 3, patch: 5 }; - let v3 = Version { major: 3, minor: 3, patch: 4 }; - - assert!(v1.ne(&v2)); - assert!(v1.ne(&v3)); - assert!(!v1.ne(&Version { major: 2, minor: 3, patch: 4 })); - } - - #[test] - fn test_version_display() { - let version = Version { major: 2, minor: 3, patch: 4 }; - - assert_eq!(version.to_string(), "2.3.4"); - } - - #[test] - fn test_version_current() { - let version = current_version(); - - assert_eq!(version.to_string(), env!("CARGO_PKG_VERSION")); - } - - #[tokio::test] - async fn test_version_remote() { - let version = remote_version().await; - - assert!(version.minor >= 0); - assert!(version.patch >= 0); - } -} diff --git a/common/src/utils/threading.rs b/common/src/utils/threading.rs index 43595da5..0c7fe456 100644 --- a/common/src/utils/threading.rs +++ b/common/src/utils/threading.rs @@ -1,6 +1,17 @@ use crossbeam_channel::unbounded; use std::{sync::Arc, thread}; +/// A simple thread pool implementation that takes a vector of items, splits them into chunks, and +/// processes each chunk in a separate thread. The results are collected and returned. +/// +/// ``` +/// use heimdall_common::utils::threading::task_pool; +/// +/// let items = vec![1, 2, 3, 4, 5]; +/// let num_threads = 2; +/// let results = task_pool(items, num_threads, |item| item * 2); +/// assert_eq!(results, vec![2, 4, 6, 8, 10]); +/// ``` pub fn task_pool< T: Clone + Send + Sync + 'static, R: Send + 'static, @@ -51,3 +62,51 @@ pub fn task_pool< results } + +#[cfg(test)] +mod tests { + use crate::utils::threading::*; + + #[test] + fn test_task_pool_with_single_thread() { + // Test case with a single thread + let items = vec![1, 2, 3, 4, 5]; + let num_threads = 1; + let expected_results = vec![2, 4, 6, 8, 10]; + + // Define a simple function to double the input + let f = |x: i32| x * 2; + + let mut results = task_pool(items, num_threads, f); + results.sort(); + assert_eq!(results, expected_results); + } + + #[test] + fn test_task_pool_with_multiple_threads() { + // Test case with multiple threads + let items = vec![1, 2, 3, 4, 5]; + let num_threads = 3; + let expected_results = vec![2, 4, 6, 8, 10]; + + // Define a simple function to double the input + let f = |x: i32| x * 2; + + let mut results = task_pool(items, num_threads, f); + results.sort(); + assert_eq!(results, expected_results); + } + + #[test] + fn test_task_pool_with_empty_items() { + // Test case with empty items vector + let items: Vec = Vec::new(); + let num_threads = 2; + + // Define a simple function to double the input + let f = |x: i32| x * 2; + + let results = task_pool(items, num_threads, f); + assert!(results.is_empty()); + } +} diff --git a/common/src/utils/time.rs b/common/src/utils/time.rs index 6265df18..59244c1d 100644 --- a/common/src/utils/time.rs +++ b/common/src/utils/time.rs @@ -1,21 +1,24 @@ use chrono::Local; /// Calculate the ETA for a process based on the number of items processed per second +/// +/// ``` +/// use heimdall_common::utils::time::calculate_eta; +/// +/// let eta = calculate_eta(1000.0, 1000); +/// assert_eq!(eta, 1); +/// ``` pub fn calculate_eta(items_per_second: f64, items_remaining: usize) -> u128 { (items_remaining as f64 / items_per_second) as u128 } /// Format seconds into a human readable ETA /// -/// ## Example /// ``` /// use heimdall_common::utils::time::format_eta; /// -/// let eta = format_eta(86400); -/// assert_eq!(eta, "1d 0s"); -/// -/// let eta = format_eta(86401); -/// assert_eq!(eta, "1d 1s"); +/// let eta = format_eta(1000); +/// assert_eq!(eta, "16m 40s "); /// ``` pub fn format_eta(seconds_remaining: u128) -> String { let days = seconds_remaining / 86400; @@ -33,7 +36,39 @@ pub fn format_eta(seconds_remaining: u128) -> String { } /// Get the current timestamp in a pretty format +/// +/// ``` +/// use heimdall_common::utils::time::pretty_timestamp; +/// +/// let timestamp = pretty_timestamp(); +/// ``` pub fn pretty_timestamp() -> String { let now = Local::now(); now.format("%d-%m-%Y %H:%M:%S.%f").to_string() } + +#[cfg(test)] +mod tests { + use crate::utils::time::*; + + #[test] + fn test_calculate_eta() { + assert_eq!(calculate_eta(2.5, 10), 4); + assert_eq!(calculate_eta(0.5, 100), 200); + assert_eq!(calculate_eta(1.0, 0), 0); + assert_eq!(calculate_eta(0.0, 100), std::u128::MAX); + assert_eq!(calculate_eta(10.0, std::usize::MAX), 1844674407370955264); + } + + #[test] + fn test_format_eta() { + assert_eq!(format_eta(0), "0s"); + assert_eq!(format_eta(59), "59s "); + assert_eq!(format_eta(60), "1m 0s"); + assert_eq!(format_eta(3600), "1h 0s"); + assert_eq!(format_eta(3665), "1h 1m 5s "); + assert_eq!(format_eta(86400), "1d 0s"); + assert_eq!(format_eta(172800), "2d 0s"); + assert_eq!(format_eta(180065), "2d 2h 1m 5s "); + } +} diff --git a/common/src/utils/version.rs b/common/src/utils/version.rs index b5539a27..30dc24d6 100644 --- a/common/src/utils/version.rs +++ b/common/src/utils/version.rs @@ -9,6 +9,7 @@ pub struct Version { pub patch: u32, } +/// get the current version from cargo pub fn current_version() -> Version { // get the current version from the cargo package let version_string = env!("CARGO_PKG_VERSION"); @@ -21,6 +22,7 @@ pub fn current_version() -> Version { } } +/// get the latest version from github pub async fn remote_version() -> Version { // get the latest release from github let remote_repository_url = @@ -54,41 +56,132 @@ impl Display for Version { } impl Version { - // greater than + /// greater than pub fn gt(&self, other: &Version) -> bool { self.major > other.major || (self.major == other.major && self.minor > other.minor) || (self.major == other.major && self.minor == other.minor && self.patch > other.patch) } - // greater than or equal to + /// greater than or equal to pub fn gte(&self, other: &Version) -> bool { self.major > other.major || (self.major == other.major && self.minor > other.minor) || (self.major == other.major && self.minor == other.minor && self.patch >= other.patch) } - // less than + /// less than pub fn lt(&self, other: &Version) -> bool { self.major < other.major || (self.major == other.major && self.minor < other.minor) || (self.major == other.major && self.minor == other.minor && self.patch < other.patch) } - // less than or equal to + /// less than or equal to pub fn lte(&self, other: &Version) -> bool { self.major < other.major || (self.major == other.major && self.minor < other.minor) || (self.major == other.major && self.minor == other.minor && self.patch <= other.patch) } - // equal to + /// equal to pub fn eq(&self, other: &Version) -> bool { self.major == other.major && self.minor == other.minor && self.patch == other.patch } - // not equal to + /// not equal to pub fn ne(&self, other: &Version) -> bool { self.major != other.major || self.minor != other.minor || self.patch != other.patch } } + +#[cfg(test)] +mod tests { + use crate::utils::version::*; + + #[test] + fn test_greater_than() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 3 }; + let v3 = Version { major: 2, minor: 2, patch: 5 }; + let v4 = Version { major: 1, minor: 4, patch: 4 }; + + assert!(v1.gt(&v2)); + assert!(v1.gt(&v3)); + assert!(v1.gt(&v4)); + assert!(!v2.gt(&v1)); + assert!(!v1.gt(&v1)); + } + + #[test] + fn test_greater_than_or_equal_to() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 4 }; + + assert!(v1.gte(&v2)); + assert!(v2.gte(&v1)); + assert!(v1.gte(&Version { major: 1, minor: 0, patch: 0 })); + } + + #[test] + fn test_less_than() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 5 }; + let v3 = Version { major: 2, minor: 4, patch: 4 }; + let v4 = Version { major: 3, minor: 3, patch: 4 }; + + assert!(v1.lt(&v2)); + assert!(v1.lt(&v3)); + assert!(v1.lt(&v4)); + assert!(!v2.lt(&v1)); + assert!(!v1.lt(&v1)); + } + + #[test] + fn test_less_than_or_equal_to() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 4 }; + + assert!(v1.lte(&v2)); + assert!(v2.lte(&v1)); + assert!(v1.lte(&Version { major: 3, minor: 0, patch: 0 })); + } + + #[test] + fn test_equal_to() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 4 }; + let v3 = Version { major: 2, minor: 3, patch: 5 }; + + assert!(v1.eq(&v2)); + assert!(!v1.eq(&v3)); + } + + #[test] + fn test_not_equal_to() { + let v1 = Version { major: 2, minor: 3, patch: 4 }; + let v2 = Version { major: 2, minor: 3, patch: 5 }; + let v3 = Version { major: 3, minor: 3, patch: 4 }; + + assert!(v1.ne(&v2)); + assert!(v1.ne(&v3)); + assert!(!v1.ne(&Version { major: 2, minor: 3, patch: 4 })); + } + + #[test] + fn test_version_display() { + let version = Version { major: 2, minor: 3, patch: 4 }; + + assert_eq!(version.to_string(), "2.3.4"); + } + + #[test] + fn test_version_current() { + let version = current_version(); + + assert_eq!(version.to_string(), env!("CARGO_PKG_VERSION")); + } + + #[tokio::test] + async fn test_version_remote() {} +} diff --git a/config/README.md b/config/README.md new file mode 100644 index 00000000..b1f3bd0d --- /dev/null +++ b/config/README.md @@ -0,0 +1,3 @@ +# heimdall-config + +This crate handles user configuration for the Heimdall library. It is not intended to be used directly, but rather as a dependency of other Heimdall crates. diff --git a/config/src/lib.rs b/config/src/lib.rs index 31ad8471..1eff40d9 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -1,5 +1,5 @@ use clap::{AppSettings, Parser}; -use heimdall_common::io::{ +use heimdall_common::utils::io::{ file::{delete_path, read_file, write_file}, logging::*, }; @@ -31,6 +31,8 @@ pub struct ConfigArgs { value: String, } +/// The [`Configuration`] struct represents the configuration of the CLI. All heimdall core modules +/// will attempt to read from this configuration when possible. #[derive(Deserialize, Serialize, Debug)] pub struct Configuration { pub rpc_url: String, @@ -41,6 +43,7 @@ pub struct Configuration { } #[allow(deprecated)] +/// Writes the given configuration to the disc at `$HOME/.bifrost/config.toml`. pub fn write_config(contents: &str) { match home_dir() { Some(mut home) => { @@ -60,6 +63,7 @@ pub fn write_config(contents: &str) { } #[allow(deprecated)] +/// Deletes the configuration file at `$HOME/.bifrost/config.toml`. pub fn delete_config() { match home_dir() { Some(mut home) => { @@ -79,6 +83,7 @@ pub fn delete_config() { } #[allow(deprecated)] +/// Reads the configuration file at `$HOME/.bifrost/config.toml`. pub fn read_config() -> String { match home_dir() { Some(mut home) => { @@ -104,6 +109,8 @@ pub fn read_config() -> String { } } +/// Returns the [`Configuration`] struct after parsing the configuration file at +/// `$HOME/.bifrost/config.toml`. pub fn get_config() -> Configuration { let contents = read_config(); @@ -121,6 +128,7 @@ pub fn get_config() -> Configuration { config } +/// update a single key/value pair in the configuration file pub fn update_config(key: &str, value: &str) { let mut contents = get_config(); @@ -153,6 +161,7 @@ pub fn update_config(key: &str, value: &str) { write_config(&serialized_config); } +/// The `config` command is used to display and edit the current configuration. pub fn config(args: ConfigArgs) { let (logger, _) = Logger::new(""); if !args.key.is_empty() { diff --git a/core/README.md b/core/README.md new file mode 100644 index 00000000..c4f8d1e9 --- /dev/null +++ b/core/README.md @@ -0,0 +1,27 @@ +# heimdall-core + +This crate is the core of the Heimdall library. It contains all module implementations, such as decompilation, disassembly, decoding, etc. + +## Crate Structure + +``` +core +├── src +│ ├── cfg # control flow graph module +│ ├── decode # calldata decoding module +│ ├── decompile # decompilation module +│ │ ├── analyzers # decompilation analyzers +│ │ └── out # decompilation output handlers +│ │ └── postprocessers +│ ├── disassemble # disassembly module +│ ├── dump # storage dump module +│ │ ├── menus # storage dump tui menus +│ │ ├── structures +│ │ └── util +│ │ └── threads +│ └── snapshot # snapshot module +│ ├── menus # snapshot tui menus +│ ├── structures +│ └── util +└── tests +``` diff --git a/core/src/cfg/graph.rs b/core/src/cfg/graph.rs index d235db74..11caf07c 100644 --- a/core/src/cfg/graph.rs +++ b/core/src/cfg/graph.rs @@ -11,7 +11,8 @@ lazy_static! { static ref CONNECTING_EDGES: Mutex> = Mutex::new(Vec::new()); } -// converts a VMTrace to a CFG graph +/// convert a symbolic execution [`VMTrace`] into a [`Graph`] of blocks, illustrating the +/// control-flow graph found by the symbolic execution engine. pub fn build_cfg( vm_trace: &VMTrace, contract_cfg: &mut Graph, @@ -64,7 +65,6 @@ pub fn build_cfg( } None => { // this node does not exist, so we need to add it to the map and the graph - println!("adding node: {}", chunk_index); let node_index = contract_cfg.add_node(cfg_node); if let Some(parent_node) = parent_node { diff --git a/core/src/cfg/mod.rs b/core/src/cfg/mod.rs index 8921f4b1..7e6d9dec 100644 --- a/core/src/cfg/mod.rs +++ b/core/src/cfg/mod.rs @@ -11,7 +11,7 @@ use clap::{AppSettings, Parser}; use heimdall_common::{ constants::{ADDRESS_REGEX, BYTECODE_REGEX}, ether::evm::core::vm::VM, - io::logging::*, + utils::io::logging::*, }; use petgraph::Graph; @@ -68,6 +68,8 @@ impl CFGArgsBuilder { } } +/// The main entry point for the CFG module. Will generate a control flow graph of the target +/// bytecode, after performing symbolic execution and discovering all possible execution paths. pub async fn cfg(args: CFGArgs) -> Result, Box> { use std::time::Instant; let now = Instant::now(); diff --git a/core/src/cfg/output.rs b/core/src/cfg/output.rs index 37c7a3ee..1219b834 100644 --- a/core/src/cfg/output.rs +++ b/core/src/cfg/output.rs @@ -1,11 +1,12 @@ use std::{process::Command, time::Duration}; -use heimdall_common::io::{file::write_file, logging::Logger}; +use heimdall_common::utils::io::{file::write_file, logging::Logger}; use indicatif::ProgressBar; use petgraph::{dot::Dot, graph::Graph}; use super::CFGArgs; +/// Write the generated CFG to a file in the `dot` graphviz format. pub fn write_cfg_to_file(contract_cfg: &Graph, args: &CFGArgs, output_dir: String) { // get a new logger let logger = Logger::default(); diff --git a/core/src/decode/mod.rs b/core/src/decode/mod.rs index 2c24668a..77db2bf9 100644 --- a/core/src/decode/mod.rs +++ b/core/src/decode/mod.rs @@ -16,8 +16,7 @@ use heimdall_common::{ rpc::get_transaction, signatures::{score_signature, ResolveSelector, ResolvedFunction}, }, - io::logging::Logger, - utils::strings::decode_hex, + utils::{io::logging::Logger, strings::decode_hex}, }; use indicatif::ProgressBar; @@ -76,6 +75,8 @@ impl DecodeArgsBuilder { } } +/// The entrypoint for the decode module. This will attempt to decode the arguments of the target +/// calldata, without the ABI of the target contract. #[allow(deprecated)] pub async fn decode(args: DecodeArgs) -> Result, Box> { // set logger environment variable if not already set diff --git a/core/src/decode/util.rs b/core/src/decode/util.rs index eea7d90a..c3e981b3 100644 --- a/core/src/decode/util.rs +++ b/core/src/decode/util.rs @@ -1,6 +1,7 @@ use ethers::types::Transaction; use heimdall_cache::util::encode_hex; +/// Get an explanation of the decoded transaction using the OpenAI API pub async fn get_explanation( decoded: String, transaction: Transaction, diff --git a/core/src/decompile/analyzers/solidity.rs b/core/src/decompile/analyzers/solidity.rs index f357e827..63968f8f 100644 --- a/core/src/decompile/analyzers/solidity.rs +++ b/core/src/decompile/analyzers/solidity.rs @@ -10,8 +10,10 @@ use heimdall_common::{ }, ext::exec::VMTrace, }, - io::logging::TraceFactory, - utils::strings::{decode_hex, encode_hex_reduced}, + utils::{ + io::logging::TraceFactory, + strings::{decode_hex, encode_hex_reduced}, + }, }; use super::super::{constants::AND_BITMASK_REGEX, precompile::decode_precompile}; @@ -27,6 +29,7 @@ use crate::decompile::{ /// - `function` - The function to be updated with the analysis results /// - `trace` - The TraceFactory to be updated with the analysis results /// - `trace_parent` - The parent of the current VMTrace +/// - `conditional_map` - A map of the conditionals in the current trace /// - `branch` - Branch metadata for the current trace. In the format of (branch_depth, /// branch_index) /// - @jon-becker: This will be used later to determin if a condition is a require @@ -384,7 +387,7 @@ pub fn analyze_sol( let operations = instruction.input_operations[1].clone(); // add the sstore to the function's storage map - function.storage.insert(key, StorageFrame { value: value, operations: operations }); + function.storage.insert(key, StorageFrame { value, operations }); function.logic.push(format!( "storage[{}] = {};", instruction.input_operations[0].solidify(), @@ -396,7 +399,7 @@ pub fn analyze_sol( let operation = instruction.input_operations[1].clone(); // add the mstore to the function's memory map - function.memory.insert(key, StorageFrame { value: value, operations: operation }); + function.memory.insert(key, StorageFrame { value, operations: operation }); function.logic.push(format!( "memory[{}] = {};", encode_hex_reduced(key), diff --git a/core/src/decompile/analyzers/yul.rs b/core/src/decompile/analyzers/yul.rs index 0c55c7c8..b56efb57 100644 --- a/core/src/decompile/analyzers/yul.rs +++ b/core/src/decompile/analyzers/yul.rs @@ -1,13 +1,25 @@ use ethers::abi::{decode, AbiEncode, ParamType}; use heimdall_common::{ ether::evm::{core::types::convert_bitmask, ext::exec::VMTrace}, - io::logging::TraceFactory, - utils::strings::{decode_hex, encode_hex_reduced}, + utils::{ + io::logging::TraceFactory, + strings::{decode_hex, encode_hex_reduced}, + }, }; use super::super::util::*; - -// converts a VMTrace to a Funciton through lexical and syntactic analysis +/// Converts a VMTrace to a Function through lexical and syntactic analysis +/// +/// ## Parameters +/// - `vm_trace` - The VMTrace to be analyzed +/// - `function` - The function to be updated with the analysis results +/// - `trace` - The TraceFactory to be updated with the analysis results +/// - `trace_parent` - The parent of the current VMTrace +/// - `conditional_map` - The map of conditionals that have been jumped +/// +/// +/// ## Returns +/// - `function` - The function updated with the analysis results pub fn analyze_yul( vm_trace: &VMTrace, function: Function, @@ -203,7 +215,7 @@ pub fn analyze_yul( let operations = instruction.input_operations[1].clone(); // add the sstore to the function's storage map - function.storage.insert(key, StorageFrame { value: value, operations: operations }); + function.storage.insert(key, StorageFrame { value, operations }); function.logic.push(format!( "sstore({}, {})", instruction.input_operations[0].yulify(), @@ -215,7 +227,7 @@ pub fn analyze_yul( let operation = instruction.input_operations[1].clone(); // add the mstore to the function's memory map - function.memory.insert(key, StorageFrame { value: value, operations: operation }); + function.memory.insert(key, StorageFrame { value, operations: operation }); function.logic.push(format!( "{}({}, {})", opcode_name.to_lowercase(), diff --git a/core/src/decompile/constants.rs b/core/src/decompile/constants.rs index 9a59419a..86ade032 100644 --- a/core/src/decompile/constants.rs +++ b/core/src/decompile/constants.rs @@ -3,40 +3,43 @@ use lazy_static::lazy_static; lazy_static! { - // The following regex is used as a detector for AND bitmasks + /// The following regex is used as a detector for AND bitmasks pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"\(0x([a-fA-F0-9]{2}){1,32}\) & ").unwrap(); + + /// The following regex is used as a detector for AND bitmasks pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & \(0x([a-fA-F0-9]{2}){1,32}\)").unwrap(); - // used to detect constant values + /// used to detect constant values pub static ref CONSTANT_REGEX: Regex = Regex::new(r"^(?:(?![memorystorage\[\]]).)*$").unwrap(); - // used to detect non-zero bytes within a word + /// used to detect non-zero bytes within a word pub static ref NON_ZERO_BYTE_REGEX: Regex = Regex::new(r"[a-fA-F0-9][a-fA-F1-9]").unwrap(); - // detects a parenthesis enclosed expression + /// detects a parenthesis enclosed expression pub static ref ENCLOSED_EXPRESSION_REGEX: Regex = Regex::new(r"\(.*\)").unwrap(); - // detects a memory access + /// detects a memory access pub static ref MEM_ACCESS_REGEX: Regex = Regex::new(r"memory\[.*\]").unwrap(); - // detects a storage access + /// detects a storage access pub static ref STORAGE_ACCESS_REGEX: Regex = Regex::new(r"storage\[.*\]").unwrap(); - // detects division by 1 + /// detects division by 1 pub static ref DIV_BY_ONE_REGEX: Regex = Regex::new(r" \/ 0x01(?!\d)").unwrap(); - // detects multiplication by 1 + /// detects multiplication by 1 pub static ref MUL_BY_ONE_REGEX: Regex = Regex::new(r"\b0x01\b\s*\*\s*| \*\s*\b0x01\b").unwrap(); - // memory variable regex + /// memory variable regex pub static ref MEM_VAR_REGEX: Regex = Regex::new(r"^var_[a-zA-Z]{1,2}$").unwrap(); - // extracts commas within a certain expression, not including commas within parentheses + /// extracts commas within a certain expression, not including commas within parentheses pub static ref ARGS_SPLIT_REGEX: Regex = Regex::new(r",\s*(?![^()]*\))").unwrap(); - // used to detect compiler size checks + /// used to detect compiler size checks pub static ref VARIABLE_SIZE_CHECK_REGEX: Regex = Regex::new(r"!?\(?0(x01)? < [a-zA-Z0-9_\[\]]+\.length\)?").unwrap(); + /// the static header for decompiled solidity contracts pub static ref DECOMPILED_SOURCE_HEADER_SOL: String = "// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; @@ -55,7 +58,8 @@ pragma solidity >=0.8.0; /// https://heimdall.rs ".to_string(); -pub static ref DECOMPILED_SOURCE_HEADER_YUL: String = + /// the static header for decompiled yul contracts + pub static ref DECOMPILED_SOURCE_HEADER_YUL: String = "/// @title Decompiled Contract /// @author Jonathan Becker /// @custom:version heimdall-rs v{} diff --git a/core/src/decompile/mod.rs b/core/src/decompile/mod.rs index b8534fbe..437f54a1 100644 --- a/core/src/decompile/mod.rs +++ b/core/src/decompile/mod.rs @@ -31,7 +31,7 @@ use clap::{AppSettings, Parser}; use heimdall_common::{ constants::{ADDRESS_REGEX, BYTECODE_REGEX}, ether::{evm::core::vm::VM, signatures::*}, - io::logging::*, + utils::io::logging::*, }; use self::out::abi::ABIStructure; diff --git a/core/src/decompile/out/abi.rs b/core/src/decompile/out/abi.rs index 9f098bfc..2663fd33 100644 --- a/core/src/decompile/out/abi.rs +++ b/core/src/decompile/out/abi.rs @@ -1,7 +1,7 @@ use std::time::Duration; use ethers::abi::AbiEncode; -use heimdall_common::io::{ +use heimdall_common::utils::io::{ file::short_path, logging::{Logger, TraceFactory}, }; @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::decompile::{util::Function, DecompilerArgs}; +/// A single named ABI token. #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct ABIToken { pub name: String, @@ -19,6 +20,7 @@ pub struct ABIToken { pub type_: String, } +/// ABI structure for a single contract function. #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct FunctionABI { #[serde(rename = "type")] @@ -31,6 +33,7 @@ pub struct FunctionABI { pub constant: bool, } +/// ABI structure for a single contract's custom error. #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct ErrorABI { #[serde(rename = "type")] @@ -39,6 +42,7 @@ pub struct ErrorABI { pub inputs: Vec, } +/// ABI structure for a single contract event. #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct EventABI { #[serde(rename = "type")] @@ -47,6 +51,7 @@ pub struct EventABI { pub inputs: Vec, } +/// An [`ABIStructure`] may be a function, error, or event #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub enum ABIStructure { Function(FunctionABI), @@ -54,6 +59,16 @@ pub enum ABIStructure { Event(EventABI), } +/// Build the ABI for a decompiled contract. +/// +/// # Arguments +/// - `args`: The [`DecompilerArgs`] used to decompile the contract. +/// - `functions`: The [`Function`]s found in the contract. +/// - `trace`: debug trace of the decompilation process. +/// - `trace_parent`: the parent of the current trace. +/// +/// # Returns +/// A [`Vec`] of [`ABIStructure`]s representing the contract's ABI. pub fn build_abi( args: &DecompilerArgs, functions: Vec, @@ -172,7 +187,7 @@ pub fn build_abi( inputs: function_inputs, outputs: function_outputs, state_mutability: state_mutability.to_string(), - constant: constant, + constant, })); // build the function's custom errors @@ -204,7 +219,7 @@ pub fn build_abi( abi.push(ABIStructure::Error(ErrorABI { type_: "error".to_string(), name: resolved_error.name.clone(), - inputs: inputs, + inputs, })); } None => { @@ -263,7 +278,7 @@ pub fn build_abi( abi.push(ABIStructure::Event(EventABI { type_: "event".to_string(), name: resolved_event.name.clone(), - inputs: inputs, + inputs, })); } None => { diff --git a/core/src/decompile/out/postprocessers/mod.rs b/core/src/decompile/out/postprocessers/mod.rs index 6025baf8..c1797a39 100644 --- a/core/src/decompile/out/postprocessers/mod.rs +++ b/core/src/decompile/out/postprocessers/mod.rs @@ -1,3 +1,2 @@ pub mod solidity; -pub mod tests; pub mod yul; diff --git a/core/src/decompile/out/postprocessers/solidity.rs b/core/src/decompile/out/postprocessers/solidity.rs index 8c310a20..6c1c51f2 100644 --- a/core/src/decompile/out/postprocessers/solidity.rs +++ b/core/src/decompile/out/postprocessers/solidity.rs @@ -31,23 +31,6 @@ lazy_static! { } /// Convert bitwise operations to a variable type cast -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);".to_string(); -/// let converted = convert_bitmask_to_casting(line); -/// assert_eq!(converted, "uint256(arg0);"); -/// -/// let line = "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);".to_string(); -/// let converted = convert_bitmask_to_casting(line); -/// assert_eq!(converted, "uint256(arg0);"); -/// ``` fn convert_bitmask_to_casting(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -154,19 +137,6 @@ fn convert_bitmask_to_casting(line: &str) -> String { } /// Removes unnecessary casts -/// -/// # Arguments -/// line: String - the line to simplify -/// -/// # Returns -/// String - the simplified line -/// -/// # Example -/// ```no_run -/// let line = "uint256(uint256(arg0))".to_string(); -/// let simplified = simplify_casts(line); -/// assert_eq!(simplified, "uint256(arg0)"); -/// ``` fn simplify_casts(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -197,27 +167,6 @@ fn simplify_casts(line: &str) -> String { } /// Simplifies expressions by removing unnecessary parentheses -/// -/// # Arguments -/// line: String - the line to simplify -/// -/// # Returns -/// String - the simplified line -/// -/// # Example -/// ```no_run -/// let line = "((a + b))".to_string(); -/// let simplified = simplify_parentheses(line); -/// assert_eq!(simplified, "a + b"); -/// -/// let line = "((a + b) * (c + d))".to_string(); -/// let simplified = simplify_parentheses(line); -/// assert_eq!(simplified, "(a + b) * (c + d)"); -/// -/// let line = "if ((((a + b) * (c + d))) > ((arg0 * 10000)) {".to_string(); -/// let simplified = simplify_parentheses(line); -/// assert_eq!(simplified, "if ((a + b) * (c + d)) > arg0 * 10000 {"); -/// ``` fn simplify_parentheses(line: &str, paren_index: usize) -> String { // helper function to determine if parentheses are necessary fn are_parentheses_unnecessary(expression: &str) -> bool { @@ -329,23 +278,6 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { } /// Converts memory and storage accesses to variables -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "memory[0x40] = 0x60;".to_string(); -/// let converted = convert_access_to_variable(line); -/// assert_eq!(converted, "var_a = 0x60;"); -/// -/// let line = "storage[0x0] = 0x0;".to_string(); -/// let converted = convert_access_to_variable(line); -/// assert_eq!(converted, "stor_a = 0x0;"); -/// ``` fn convert_access_to_variable(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -479,32 +411,6 @@ fn convert_access_to_variable(line: &str) -> String { } /// Checks if the current line contains an unnecessary assignment -/// -/// # Arguments -/// line: String - the line to check -/// lines: &Vec<&str> - the lines of the contract. only includes lines after the current line -/// -/// # Returns -/// bool - whether or not the line contains an unnecessary assignment -/// -/// # Example -/// ```no_run -/// let line = "var_a = arg0;".to_string(); -/// let lines = vec![ -/// "var_b = var_a;", -/// "var_c = var_b;", -/// ].iter().map(|x| x.to_string()).collect(); -/// -/// let contains_unnecessary = contains_unnecessary_assignment(line, &lines); -/// assert_eq!(contains_unnecessary, false); -/// -/// let line = "var_a = arg0;".to_string(); -/// let lines = vec![ -/// "var_b = arg1;", -/// "var_c = var_b;", -/// ].iter().map(|x| x.to_string()).collect(); -/// assert_eq!(contains_unnecessary_assignment(line, &lines), true); -/// ``` 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") { @@ -543,19 +449,6 @@ fn contains_unnecessary_assignment(line: &str, lines: &Vec<&str>) -> bool { } /// Moves casts to the declaration -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "var_a = uint256(arg0);".to_string(); -/// let converted = move_casts_to_declaration(line); -/// assert_eq!(converted, "uint256 var_a = arg0;"); -/// ``` fn move_casts_to_declaration(line: &str) -> String { let cleaned = line; let mut type_declaration_set = MEMORY_TYPE_DECLARATION_SET.lock().unwrap(); @@ -615,19 +508,6 @@ fn move_casts_to_declaration(line: &str) -> String { } /// Replaces an expression with a variable, if the expression matches an existing variable -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "var_a = arg0 + arg1;".to_string(); -/// let converted = replace_expression_with_var(line); -/// assert_eq!(converted, "var_a = var_b + var_c;"); -/// ``` fn replace_expression_with_var(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -876,22 +756,6 @@ fn inherit_infer_storage_type(line: &str) { } /// Replaces resolved errors and events -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "revert CustomError_00000000(arg0);".to_string(); -/// let converted = replace_resolved(line); -/// assert_eq!(converted, "CustomError_00000000(arg0);"); -/// -/// let line = "revert CustomError_00000001(arg0);".to_string(); -/// let converted = replace_resolved(line); -/// assert_eq!(converted, "UrMom(arg0);"); fn replace_resolved( line: &str, all_resolved_errors: HashMap, @@ -923,19 +787,6 @@ fn replace_resolved( } /// Simplifies arithmatic by removing unnecessary operations -/// -/// # Arguments -/// line: String - the line to convert -/// -/// # Returns -/// String - the converted line -/// -/// # Example -/// ```no_run -/// let line = "var_a = 1 * 2;".to_string(); -/// let converted = simplify_arithmatic(line); -/// assert_eq!(converted, "var_a = 2;"); -/// ``` fn simplify_arithmatic(line: &str) -> String { let cleaned = DIV_BY_ONE_REGEX.replace_all(line, ""); let cleaned = MUL_BY_ONE_REGEX.replace_all(&cleaned, ""); @@ -944,6 +795,7 @@ fn simplify_arithmatic(line: &str) -> String { cleaned.replace("!!", "") } +/// Cleans up a line using postprocessing techniques fn cleanup( line: &str, all_resolved_errors: HashMap, @@ -986,6 +838,7 @@ fn cleanup( cleaned } +/// Finalizes postprocessing by removing unnecessary assignments fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { let mut cleaned_lines: Vec = Vec::new(); let mut function_count = 0; @@ -1034,6 +887,7 @@ fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { cleaned_lines } +/// Indents lines fn indent_lines(lines: Vec) -> Vec { let mut indentation: usize = 0; let mut indented_lines: Vec = Vec::new(); @@ -1056,6 +910,7 @@ fn indent_lines(lines: Vec) -> Vec { indented_lines } +/// Postprocesses a decompiled contract pub fn postprocess( lines: Vec, all_resolved_errors: HashMap, @@ -1083,3 +938,133 @@ pub fn postprocess( // run finalizing postprocessing, which need to operate on cleaned lines indent_lines(finalize(cleaned_lines, bar)) } + +#[cfg(test)] +mod tests { + + use std::collections::HashMap; + + use indicatif::ProgressBar; + + use crate::decompile::out::postprocessers::solidity::postprocess; + + #[test] + fn test_bitmask_conversion() { + let lines = vec![String::from( + "(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);", + )]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("uint256(arg0);")] + ); + } + + #[test] + fn test_bitmask_conversion_mask_after() { + let lines = vec![String::from( + "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);", + )]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("uint256(arg0);")] + ); + } + + #[test] + fn test_bitmask_conversion_unusual_mask() { + let lines = vec![String::from( + "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);", + )]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("uint248(arg0);")] + ); + } + + #[test] + fn test_simplify_casts() { + let lines = vec![String::from("uint256(uint256(arg0));")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("uint256(arg0);")] + ); + } + + #[test] + fn test_simplify_casts_complex() { + let lines = vec![ + String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), + ]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")] + ); + } + + #[test] + fn test_iszero_flip() { + let lines = vec![String::from("if (!(arg0)) {")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("if (!arg0) {")] + ); + } + + #[test] + fn test_iszero_flip_complex() { + let lines = vec![String::from("if (!(!(arg0))) {")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("if (arg0) {")] + ); + } + + #[test] + fn test_iszero_flip_complex2() { + let lines = vec![String::from("if (!(!(!(arg0)))) {")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("if (!arg0) {")] + ); + } + + #[test] + fn test_simplify_parentheses() { + let lines = vec![String::from("((arg0))")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("arg0")] + ); + } + + #[test] + fn test_simplify_parentheses_complex() { + let lines = vec![String::from("if ((cast(((arg0) + 1) / 10))) {")]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("if (cast((arg0 + 1) / 10)) {")] + ); + } + + #[test] + fn test_simplify_parentheses_complex2() { + let lines = vec![ + String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), + ]; + + assert_eq!( + postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), + vec![String::from("if (cast(((arg0 * (arg1)) + 1) / 10)) {")] + ); + } +} diff --git a/core/src/decompile/out/postprocessers/tests.rs b/core/src/decompile/out/postprocessers/tests.rs deleted file mode 100644 index 25923570..00000000 --- a/core/src/decompile/out/postprocessers/tests.rs +++ /dev/null @@ -1,129 +0,0 @@ -#[cfg(test)] -mod tests { - - use std::collections::HashMap; - - use indicatif::ProgressBar; - - use crate::decompile::out::postprocessers::solidity::postprocess; - - #[test] - fn test_bitmask_conversion() { - let lines = vec![String::from( - "(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);", - )]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("uint256(arg0);")] - ); - } - - #[test] - fn test_bitmask_conversion_mask_after() { - let lines = vec![String::from( - "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);", - )]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("uint256(arg0);")] - ); - } - - #[test] - fn test_bitmask_conversion_unusual_mask() { - let lines = vec![String::from( - "(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);", - )]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("uint248(arg0);")] - ); - } - - #[test] - fn test_simplify_casts() { - let lines = vec![String::from("uint256(uint256(arg0));")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("uint256(arg0);")] - ); - } - - #[test] - fn test_simplify_casts_complex() { - let lines = vec![ - String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), - ]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")] - ); - } - - #[test] - fn test_iszero_flip() { - let lines = vec![String::from("if (!(arg0)) {")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("if (!arg0) {")] - ); - } - - #[test] - fn test_iszero_flip_complex() { - let lines = vec![String::from("if (!(!(arg0))) {")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("if (arg0) {")] - ); - } - - #[test] - fn test_iszero_flip_complex2() { - let lines = vec![String::from("if (!(!(!(arg0)))) {")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("if (!arg0) {")] - ); - } - - #[test] - fn test_simplify_parentheses() { - let lines = vec![String::from("((arg0))")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("arg0")] - ); - } - - #[test] - fn test_simplify_parentheses_complex() { - let lines = vec![String::from("if ((cast(((arg0) + 1) / 10))) {")]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("if (cast((arg0 + 1) / 10)) {")] - ); - } - - #[test] - fn test_simplify_parentheses_complex2() { - let lines = vec![ - String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), - ]; - - assert_eq!( - postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), - vec![String::from("if (cast(((arg0 * (arg1)) + 1) / 10)) {")] - ); - } -} diff --git a/core/src/decompile/out/postprocessers/yul.rs b/core/src/decompile/out/postprocessers/yul.rs index 720fd774..9134766c 100644 --- a/core/src/decompile/out/postprocessers/yul.rs +++ b/core/src/decompile/out/postprocessers/yul.rs @@ -14,6 +14,7 @@ lazy_static! { static ref TYPE_MAP: Mutex> = Mutex::new(HashMap::new()); } +/// Remove double negations from a line fn remove_double_negation(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -45,6 +46,7 @@ fn remove_double_negation(line: &str) -> String { cleaned } +/// Convert bitwise operations to a variable type cast fn convert_bitmask_to_casting(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -88,6 +90,7 @@ fn convert_bitmask_to_casting(line: &str) -> String { cleaned } +/// Removes unnecessary casts fn simplify_casts(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -117,6 +120,7 @@ fn simplify_casts(line: &str) -> String { cleaned } +/// Removes or replaces casts with helper functions fn remove_replace_casts(line: &str) -> String { let mut cleaned = line.to_owned(); @@ -142,6 +146,7 @@ fn remove_replace_casts(line: &str) -> String { cleaned } +/// Removes unnecessary parentheses fn simplify_parentheses(line: &str, paren_index: usize) -> String { // helper function to determine if parentheses are necessary fn are_parentheses_unnecessary(expression: &str) -> bool { @@ -250,6 +255,7 @@ fn simplify_parentheses(line: &str, paren_index: usize) -> String { cleaned } +/// Add resolved events as comments fn add_resolved_events(line: &str, all_resolved_events: HashMap) -> String { let mut cleaned = line.to_owned(); @@ -287,6 +293,7 @@ fn add_resolved_events(line: &str, all_resolved_events: HashMap) -> String { let mut cleaned = line.to_owned(); @@ -316,6 +323,7 @@ fn cleanup(line: &str, all_resolved_events: HashMap) -> Str cleaned } +/// Postprocesses the cleaned lines pub fn postprocess( lines: Vec, all_resolved_events: HashMap, diff --git a/core/src/decompile/out/solidity.rs b/core/src/decompile/out/solidity.rs index 6b3b5c77..23dbd8ba 100644 --- a/core/src/decompile/out/solidity.rs +++ b/core/src/decompile/out/solidity.rs @@ -2,11 +2,13 @@ use std::{collections::HashMap, time::Duration}; use heimdall_common::{ ether::signatures::{ResolvedError, ResolvedLog}, - io::{ - file::short_path, - logging::{Logger, TraceFactory}, + utils::{ + io::{ + file::short_path, + logging::{Logger, TraceFactory}, + }, + strings::find_balanced_encapsulator, }, - utils::strings::find_balanced_encapsulator, }; use indicatif::ProgressBar; @@ -20,6 +22,9 @@ use super::{ postprocessers::solidity::postprocess, }; +/// Build the decompiled Solidity source code from the given functions. Will piece together +/// decompiled [`Function`]s, [`ResolvedError`]s, [`ResolvedLog`]s, and [`ABIStructure`]s into a +/// Solidity contract. pub fn build_solidity_output( args: &DecompilerArgs, abi: &Vec, diff --git a/core/src/decompile/out/yul.rs b/core/src/decompile/out/yul.rs index efafd2ef..7f6e51b8 100644 --- a/core/src/decompile/out/yul.rs +++ b/core/src/decompile/out/yul.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, time::Duration}; use crate::decompile::{constants::DECOMPILED_SOURCE_HEADER_YUL, util::Function, DecompilerArgs}; use heimdall_common::{ ether::signatures::ResolvedLog, - io::{ + utils::io::{ file::short_path, logging::{Logger, TraceFactory}, }, @@ -12,6 +12,8 @@ use indicatif::ProgressBar; use super::postprocessers::yul::postprocess; +/// Build the decompiled Yul source code from the given functions. Will piece together decompiled +/// [`Function`]s and [`ResolvedLog`]s into a Yul contract. pub fn build_yul_output( args: &DecompilerArgs, functions: Vec, diff --git a/core/src/decompile/precompile.rs b/core/src/decompile/precompile.rs index 4fff33a8..6d827432 100644 --- a/core/src/decompile/precompile.rs +++ b/core/src/decompile/precompile.rs @@ -3,7 +3,12 @@ use heimdall_common::ether::evm::core::opcodes::WrappedOpcode; use super::util::StorageFrame; -// detects the usage of precompiled contracts within the EVM +/// Detects the usage of precompiled contracts within the EVM. Whenever an internal call is found +/// within symbolic execution traces, this function will attempt to detect if the call is to a +/// precompiled contract. It is relatively trivial to do this, as calls to specific addresses (i.e, +/// `0x..01`), are precompiled contracts. +/// Once a precompile has been detected, this function attempts to format it in a solidity-like +/// format. pub fn decode_precompile( precompile_address: U256, extcalldata_memory: Vec, diff --git a/core/src/decompile/resolve.rs b/core/src/decompile/resolve.rs index 4d0c780b..bb06c495 100644 --- a/core/src/decompile/resolve.rs +++ b/core/src/decompile/resolve.rs @@ -1,7 +1,9 @@ use super::util::Function; -use heimdall_common::{ether::signatures::ResolvedFunction, io::logging::Logger}; +use heimdall_common::{ether::signatures::ResolvedFunction, utils::io::logging::Logger}; -// match the ResolvedFunction to a list of Function parameters +/// Given a list of potential [`ResolvedFunction`]s and a [`Function`], return a list of +/// [`ResolvedFunction`]s (that is, resolved signatures that were found on a 4byte directory) that +/// match the parameters found during symbolic execution for said [`Function`]. pub fn match_parameters( resolved_functions: Vec, function: &Function, diff --git a/core/src/decompile/util.rs b/core/src/decompile/util.rs index ad07e2ee..f5609909 100644 --- a/core/src/decompile/util.rs +++ b/core/src/decompile/util.rs @@ -6,6 +6,9 @@ use heimdall_common::ether::{ signatures::{ResolvedError, ResolvedFunction, ResolvedLog}, }; +/// The [`Function`] struct represents a decompiled function found in the contract's bytecode. +/// Throughout the decompilation process, we will build up this function's structure, and eventually +/// write it to a file. #[derive(Clone, Debug)] pub struct Function { // the function's 4byte selector @@ -60,6 +63,7 @@ pub struct Function { pub payable: bool, } +/// #[derive(Clone, Debug)] pub struct StorageFrame { pub value: U256, diff --git a/core/src/disassemble/mod.rs b/core/src/disassemble/mod.rs index 74f39922..b75807a8 100644 --- a/core/src/disassemble/mod.rs +++ b/core/src/disassemble/mod.rs @@ -4,9 +4,11 @@ use clap::{AppSettings, Parser}; use derive_builder::Builder; use heimdall_common::{ constants::{ADDRESS_REGEX, BYTECODE_REGEX}, - ether::{evm::core::opcodes::opcode, rpc::get_code}, - io::logging::Logger, - utils::strings::{decode_hex, encode_hex}, + ether::{evm::core::opcodes::Opcode, rpc::get_code}, + utils::{ + io::logging::Logger, + strings::{decode_hex, encode_hex}, + }, }; #[derive(Debug, Clone, Parser, Builder)] @@ -43,6 +45,7 @@ impl DisassemblerArgsBuilder { } } +/// Disassemble the given target's bytecode to assembly. pub async fn disassemble(args: DisassemblerArgs) -> Result> { use std::time::Instant; let now = Instant::now(); @@ -98,7 +101,7 @@ pub async fn disassemble(args: DisassemblerArgs) -> Result = Mutex::new(DumpState::new()); + + /// The default decoding types. pub static ref DECODE_AS_TYPES: Vec = vec![ "bytes32".to_string(), "bool".to_string(), @@ -14,12 +17,14 @@ lazy_static! { "uint256".to_string() ]; + /// The default decoding types. pub static ref ABOUT_TEXT: Vec = vec![ format!("heimdall-rs v{}", env!("CARGO_PKG_VERSION")), "By Jonathan Becker ".to_string(), "The storage dump module will fetch all storage slots and values accessed by any EVM contract.".to_string(), ]; + /// constant help menu text pub static ref HELP_MENU_COMMANDS: Vec = vec![ ":q, :quit exit the program".to_string(), ":h, :help display this help menu".to_string(), @@ -28,6 +33,7 @@ lazy_static! { ":s, :seek move the cusor up or down by a specified amount".to_string(), ]; + /// constant help menu text pub static ref HELP_MENU_CONTROLS: Vec = vec![ "↑, Scroll Up move the cursor up one slot".to_string(), "↓, Scroll Down move the cursor down one slot".to_string(), diff --git a/core/src/dump/menus/command_palette.rs b/core/src/dump/menus/command_palette.rs index 809e5952..0f3b35e7 100644 --- a/core/src/dump/menus/command_palette.rs +++ b/core/src/dump/menus/command_palette.rs @@ -8,6 +8,7 @@ use tui::{ use crate::dump::{structures::dump_state::DumpState, util::table::build_rows}; +/// Render the TUI command palette pub fn render_tui_command_palette(f: &mut Frame, state: &mut DumpState) { // build main layout let main_layout = Layout::default() diff --git a/core/src/dump/menus/help.rs b/core/src/dump/menus/help.rs index b52c0458..800a5f77 100644 --- a/core/src/dump/menus/help.rs +++ b/core/src/dump/menus/help.rs @@ -12,6 +12,7 @@ use crate::dump::{ structures::dump_state::DumpState, }; +/// Render the TUI help menu pub fn render_tui_help(f: &mut Frame, _: &mut DumpState) { // build main layout let main_layout = Layout::default() diff --git a/core/src/dump/menus/main.rs b/core/src/dump/menus/main.rs index c47df273..e4d179a9 100644 --- a/core/src/dump/menus/main.rs +++ b/core/src/dump/menus/main.rs @@ -9,6 +9,7 @@ use tui::{ use crate::dump::{structures::dump_state::DumpState, util::table::build_rows}; +/// Render the TUI main view pub fn render_tui_view_main(f: &mut Frame, state: &mut DumpState) { // build main layout let main_layout = Layout::default() diff --git a/core/src/dump/menus/mod.rs b/core/src/dump/menus/mod.rs index 910c40c1..62d42b56 100644 --- a/core/src/dump/menus/mod.rs +++ b/core/src/dump/menus/mod.rs @@ -15,6 +15,7 @@ pub enum TUIView { Help, } +/// Render the TUI #[allow(unreachable_patterns)] pub fn render_ui(f: &mut Frame, state: &mut DumpState) { match state.view { diff --git a/core/src/dump/mod.rs b/core/src/dump/mod.rs index 0c7d83f5..8f44c971 100644 --- a/core/src/dump/mod.rs +++ b/core/src/dump/mod.rs @@ -7,8 +7,8 @@ use clap::{AppSettings, Parser}; use derive_builder::Builder; use ethers::types::H160; use heimdall_common::{ - io::logging::*, resources::transpose::{get_contract_creation, get_transaction_list}, + utils::io::logging::*, }; use std::{collections::HashMap, env, str::FromStr, time::Instant}; @@ -86,6 +86,8 @@ impl DumpArgsBuilder { } } +/// entry point for the dump module. Will fetch all storage slots accessed by the target contract, +/// and dump them to a CSV file or the TUI. pub async fn dump(args: DumpArgs) -> Result, Box> { // set logger environment variable if not already set if std::env::var("RUST_LOG").is_err() { @@ -179,7 +181,7 @@ pub async fn dump(args: DumpArgs) -> Result, Box, diff --git a/core/src/dump/structures/transaction.rs b/core/src/dump/structures/transaction.rs index c788bba7..dfd5ed0f 100644 --- a/core/src/dump/structures/transaction.rs +++ b/core/src/dump/structures/transaction.rs @@ -1,3 +1,5 @@ +/// A single EVM transaction, which contains the hash, block number, and whether or not it has been +/// indexed by the dump process yet. #[derive(Debug, Clone)] pub struct Transaction { pub indexed: bool, diff --git a/core/src/dump/util/csv.rs b/core/src/dump/util/csv.rs index 948e9e3f..e896ee5e 100644 --- a/core/src/dump/util/csv.rs +++ b/core/src/dump/util/csv.rs @@ -2,13 +2,14 @@ use ethers::{ abi::{decode, ParamType}, types::U256, }; -use heimdall_common::{ +use heimdall_common::utils::{ io::file::write_lines_to_file, - utils::strings::{encode_hex, hex_to_ascii}, + strings::{encode_hex, hex_to_ascii}, }; use crate::dump::{constants::DECODE_AS_TYPES, structures::dump_state::DumpState}; +/// A single row in the CSV #[derive(Debug, Clone)] pub struct DumpRow { pub last_modified: String, @@ -18,6 +19,7 @@ pub struct DumpRow { pub value: String, } +/// Convert [`DumpState`] to a Vec of [`DumpRow`]s, which can be used to build a CSV. pub fn build_csv(state: &DumpState) -> Vec { let mut lines: Vec = Vec::new(); @@ -55,6 +57,7 @@ pub fn build_csv(state: &DumpState) -> Vec { lines } +/// Write the storage to a CSV file. pub fn write_storage_to_csv(output_dir: &str, file_name: &str, state: &DumpState) { let mut csv_rows = build_csv(state); let mut lines: Vec = Vec::new(); diff --git a/core/src/dump/util/mod.rs b/core/src/dump/util/mod.rs index 57659e83..18bce332 100644 --- a/core/src/dump/util/mod.rs +++ b/core/src/dump/util/mod.rs @@ -14,12 +14,12 @@ use ethers::{ types::{StateDiff, TraceType, H256}, }; use heimdall_cache::{read_cache, store_cache}; -use heimdall_common::io::logging::Logger; +use heimdall_common::utils::io::logging::Logger; use tui::{backend::CrosstermBackend, Terminal}; use super::{structures::transaction::Transaction, DumpArgs}; -// cleanup the terminal, disable raw mode, and leave the alternate screen +/// cleanup the terminal, disable raw mode, and leave the alternate screen pub fn cleanup_terminal() { let stdout = io::stdout(); let backend = CrosstermBackend::new(stdout); @@ -29,7 +29,7 @@ pub fn cleanup_terminal() { terminal.show_cursor().unwrap(); } -// get the state diff for the given transaction +/// get the state diff for the given transaction pub async fn get_storage_diff(tx: &Transaction, args: &DumpArgs) -> Option { // create new logger let (logger, _) = Logger::new(match args.verbose.log_level() { diff --git a/core/src/dump/util/table.rs b/core/src/dump/util/table.rs index b5b73818..5838a06a 100644 --- a/core/src/dump/util/table.rs +++ b/core/src/dump/util/table.rs @@ -10,6 +10,8 @@ use tui::{ use crate::dump::{constants::DECODE_AS_TYPES, structures::dump_state::DumpState}; +/// A helper function used in many TUI views for rendering list rows, as well as handling scrolling +/// and selection. pub fn build_rows(state: &mut DumpState, max_row_height: usize) -> Vec> { // ensure scroll index is within bounds if state.scroll_index >= state.storage.len() && state.scroll_index != 0 { diff --git a/core/src/dump/util/threads/indexer.rs b/core/src/dump/util/threads/indexer.rs index fe217329..7ae4b8e9 100644 --- a/core/src/dump/util/threads/indexer.rs +++ b/core/src/dump/util/threads/indexer.rs @@ -1,13 +1,15 @@ use std::time::Duration; use ethers::types::{Diff, H160}; -use heimdall_common::{io::logging::Logger, utils::threading::task_pool}; +use heimdall_common::utils::{io::logging::Logger, threading::task_pool}; use indicatif::ProgressBar; use crate::dump::{ constants::DUMP_STATE, structures::storage_slot::StorageSlot, util::get_storage_diff, }; +/// The main function for indexing storage slots. Will fetch the storage diff for each transaction +/// in a threaded task pool, updating the state accordingly. pub async fn handle(addr_hash: H160) { let state = DUMP_STATE.lock().unwrap(); let transactions = state.transactions.clone(); diff --git a/core/src/dump/util/threads/tui.rs b/core/src/dump/util/threads/tui.rs index 0d80ab7e..47cb86d9 100644 --- a/core/src/dump/util/threads/tui.rs +++ b/core/src/dump/util/threads/tui.rs @@ -14,6 +14,7 @@ use crate::dump::{ DumpArgs, }; +/// The main function for the TUI. Will render the TUI and handle user input. pub fn handle(args: &DumpArgs, output_dir: &str) { // if no TUI is requested, just run the dump if args.no_tui { diff --git a/core/src/snapshot/analyze.rs b/core/src/snapshot/analyze.rs index 6f1d5d00..e13f7373 100644 --- a/core/src/snapshot/analyze.rs +++ b/core/src/snapshot/analyze.rs @@ -19,8 +19,7 @@ use heimdall_common::{ }, lexers::cleanup::Cleanup, }, - io::logging::TraceFactory, - utils::strings::encode_hex_reduced, + utils::{io::logging::TraceFactory, strings::encode_hex_reduced}, }; /// Generates a snapshot of a VMTrace's underlying function @@ -361,7 +360,7 @@ pub fn snapshot_trace( let operation = instruction.input_operations[1].clone(); // add the mstore to the function's memory map - snapshot.memory.insert(key, StorageFrame { value: value, operations: operation }); + snapshot.memory.insert(key, StorageFrame { value, operations: operation }); } else if opcode_name == "CODECOPY" { let memory_offset = &instruction.inputs[0]; let source_offset = instruction.inputs[1].try_into().unwrap_or(usize::MAX); @@ -377,7 +376,7 @@ pub fn snapshot_trace( snapshot.memory.insert( key, - StorageFrame { value: value, operations: WrappedOpcode::new(0x39, vec![]) }, + StorageFrame { value, operations: WrappedOpcode::new(0x39, vec![]) }, ); } } else if opcode_name == "STATICCALL" { diff --git a/core/src/snapshot/constants.rs b/core/src/snapshot/constants.rs index 21a08165..52586f60 100644 --- a/core/src/snapshot/constants.rs +++ b/core/src/snapshot/constants.rs @@ -6,19 +6,23 @@ use lazy_static::lazy_static; use crate::snapshot::structures::state::State; lazy_static! { + /// global state for the snapshot module pub static ref STATE: Mutex = Mutex::new(State::new()); + /// constant about text pub static ref ABOUT_TEXT: Vec = vec![ format!("heimdall-rs v{}", env!("CARGO_PKG_VERSION")), "By Jonathan Becker ".to_string(), "The snapshot module allows users to quickly generate an overview of a contract's bytecode, without the need for the contract's source code.".to_string(), ]; + /// constant help menu text pub static ref HELP_MENU_COMMANDS: Vec = vec![ ":q, :quit exit the program".to_string(), ":h, :help display this help menu".to_string(), ]; + /// constant help menu text pub static ref HELP_MENU_CONTROLS: Vec = vec![ "↑, Scroll Up move the cursor up".to_string(), "↓, Scroll Down move the cursor down".to_string(), @@ -26,6 +30,6 @@ lazy_static! { "ESC clear the search filter".to_string(), ]; - // used to detect compiler size checks + /// used to detect compiler size checks pub static ref VARIABLE_SIZE_CHECK_REGEX: Regex = Regex::new(r"!?\(?0(x01)? < [a-zA-Z0-9_\[\]]+\.length\)?").unwrap(); } diff --git a/core/src/snapshot/menus/command_palette.rs b/core/src/snapshot/menus/command_palette.rs index 57e744cd..33323f52 100644 --- a/core/src/snapshot/menus/command_palette.rs +++ b/core/src/snapshot/menus/command_palette.rs @@ -10,6 +10,7 @@ use tui::{ use crate::snapshot::{structures::state::State, util::table::build_rows}; +/// Render the TUI command palette pub fn render_tui_command_palette(f: &mut Frame, state: &mut State) { // creates a new block with the given title // https://github.com/fdehau/tui-rs/blob/master/examples/paragraph.rs diff --git a/core/src/snapshot/menus/help.rs b/core/src/snapshot/menus/help.rs index 0654d190..68a01cd8 100644 --- a/core/src/snapshot/menus/help.rs +++ b/core/src/snapshot/menus/help.rs @@ -12,6 +12,7 @@ use crate::snapshot::{ structures::state::State, }; +/// Render the TUI help menu pub fn render_tui_help(f: &mut Frame, _: &mut State) { // build main layout let main_layout = Layout::default() diff --git a/core/src/snapshot/menus/main.rs b/core/src/snapshot/menus/main.rs index b6b568e5..c4876526 100644 --- a/core/src/snapshot/menus/main.rs +++ b/core/src/snapshot/menus/main.rs @@ -10,6 +10,7 @@ use tui::{ use crate::snapshot::{structures::state::State, util::table::build_rows}; +/// Render the TUI main view pub fn render_tui_view_main(f: &mut Frame, state: &mut State) { // creates a new block with the given title // https://github.com/fdehau/tui-rs/blob/master/examples/paragraph.rs diff --git a/core/src/snapshot/menus/mod.rs b/core/src/snapshot/menus/mod.rs index 5ee4bcbb..d64bc2ce 100644 --- a/core/src/snapshot/menus/mod.rs +++ b/core/src/snapshot/menus/mod.rs @@ -16,6 +16,7 @@ pub enum TUIView { } #[allow(unreachable_patterns)] +/// Render the TUI view based on the current state pub fn render_ui(f: &mut Frame, state: &mut State) { match state.view { TUIView::Main => main::render_tui_view_main(f, state), diff --git a/core/src/snapshot/mod.rs b/core/src/snapshot/mod.rs index aec664e1..da34f13c 100644 --- a/core/src/snapshot/mod.rs +++ b/core/src/snapshot/mod.rs @@ -22,8 +22,10 @@ use heimdall_common::{ selectors::{find_function_selectors, resolve_selectors}, signatures::{score_signature, ResolvedError, ResolvedFunction, ResolvedLog}, }, - io::logging::*, - utils::strings::{decode_hex, encode_hex_reduced}, + utils::{ + io::logging::*, + strings::{decode_hex, encode_hex_reduced}, + }, }; use indicatif::ProgressBar; @@ -89,6 +91,9 @@ pub struct SnapshotResult { pub resolved_events: HashMap, } +/// The main snapshot function, which will be called from the main thread. This module is +/// responsible for generating a high-level overview of the target contract, including function +/// signatures, access control, gas consumption, storage accesses, event emissions, and more. pub async fn snapshot(args: SnapshotArgs) -> Result> { use std::time::Instant; let now = Instant::now(); diff --git a/core/src/snapshot/resolve.rs b/core/src/snapshot/resolve.rs index c12a8017..a4c6508d 100644 --- a/core/src/snapshot/resolve.rs +++ b/core/src/snapshot/resolve.rs @@ -1,8 +1,10 @@ -use heimdall_common::{ether::signatures::ResolvedFunction, io::logging::Logger}; +use heimdall_common::{ether::signatures::ResolvedFunction, utils::io::logging::Logger}; use super::structures::snapshot::Snapshot; -// match the ResolvedFunction to a list of Function parameters +/// Given a list of potential [`ResolvedFunction`]s and a [`Snapshot`], return a list of +/// [`ResolvedFunction`]s (that is, resolved signatures that were found on a 4byte directory) that +/// match the parameters found during symbolic execution for said [`Snapshot`]. pub fn match_parameters( resolved_functions: Vec, function: &Snapshot, diff --git a/core/src/snapshot/structures/snapshot.rs b/core/src/snapshot/structures/snapshot.rs index f6327b50..e246f65d 100644 --- a/core/src/snapshot/structures/snapshot.rs +++ b/core/src/snapshot/structures/snapshot.rs @@ -6,6 +6,8 @@ use heimdall_common::ether::{ signatures::{ResolvedError, ResolvedFunction, ResolvedLog}, }; +/// A snapshot of a contract's state at a given point in time. Will be built over the process of +/// symbolic-execution analysis. #[derive(Clone, Debug)] pub struct Snapshot { // the function's 4byte selector diff --git a/core/src/snapshot/structures/state.rs b/core/src/snapshot/structures/state.rs index 8e72af9f..9416a90b 100644 --- a/core/src/snapshot/structures/state.rs +++ b/core/src/snapshot/structures/state.rs @@ -4,6 +4,9 @@ use heimdall_common::ether::signatures::{ResolvedError, ResolvedLog}; use crate::snapshot::{menus::TUIView, structures::snapshot::Snapshot}; +/// The state of the snapshot process, which will be updated as the process continues. +/// This struct is also used to store the state of the TUI, and is often passed to the TUI renderer +/// as a mutable reference. #[derive(Debug, Clone)] pub struct State { pub function_index: usize, diff --git a/core/src/snapshot/util/csv.rs b/core/src/snapshot/util/csv.rs index 772819ea..c3b4e296 100644 --- a/core/src/snapshot/util/csv.rs +++ b/core/src/snapshot/util/csv.rs @@ -2,12 +2,12 @@ use std::collections::HashMap; use heimdall_common::{ ether::signatures::{ResolvedError, ResolvedLog}, - io::file::write_lines_to_file, - utils::strings::encode_hex_reduced, + utils::{io::file::write_lines_to_file, strings::encode_hex_reduced}, }; use crate::snapshot::structures::snapshot::Snapshot; +/// Write the snapshot data to a CSV file pub fn generate_and_write_contract_csv( snapshots: &Vec, resolved_errors: &HashMap, diff --git a/core/src/snapshot/util/table.rs b/core/src/snapshot/util/table.rs index 9f347f95..9594735f 100644 --- a/core/src/snapshot/util/table.rs +++ b/core/src/snapshot/util/table.rs @@ -5,6 +5,8 @@ use tui::{ use crate::snapshot::structures::state::State; +/// A helper function used in many TUI views for rendering list rows, as well as handling scrolling +/// and selection. pub fn build_rows(state: &mut State, max_row_height: usize) -> Vec> { // ensure scroll index is within bounds if state.function_index >= state.snapshots.len() && state.function_index != 0 { diff --git a/core/src/snapshot/util/tui.rs b/core/src/snapshot/util/tui.rs index 2054cf0a..06b31e69 100644 --- a/core/src/snapshot/util/tui.rs +++ b/core/src/snapshot/util/tui.rs @@ -14,7 +14,7 @@ use crate::snapshot::{ structures::snapshot::Snapshot, }; -// cleanup the terminal, disable raw mode, and leave the alternate screen +/// cleanup the terminal, disable raw mode, and leave the alternate screen pub fn cleanup_terminal() { let stdout = io::stdout(); let backend = CrosstermBackend::new(stdout); @@ -24,6 +24,7 @@ pub fn cleanup_terminal() { terminal.show_cursor().unwrap(); } +/// The TUI thread handler, which will be called from the main thread. pub fn handle( snapshots: Vec, resolved_errors: &HashMap, diff --git a/core/tests/test_cfg.rs b/core/tests/test_cfg.rs index 5649bcf5..94873a5c 100644 --- a/core/tests/test_cfg.rs +++ b/core/tests/test_cfg.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod benchmark { use clap_verbosity_flag::Verbosity; - use heimdall_common::testing::benchmarks::async_bench; + use heimdall_common::utils::testing::benchmarks::async_bench; use heimdall_core::cfg::CFGArgs; diff --git a/core/tests/test_decode.rs b/core/tests/test_decode.rs index d1a00ec2..312d3e36 100644 --- a/core/tests/test_decode.rs +++ b/core/tests/test_decode.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod benchmark { use clap_verbosity_flag::Verbosity; - use heimdall_common::testing::benchmarks::async_bench; + use heimdall_common::utils::testing::benchmarks::async_bench; use heimdall_core::decode::DecodeArgs; diff --git a/core/tests/test_decompile.rs b/core/tests/test_decompile.rs index f30a64e1..b8580e8e 100644 --- a/core/tests/test_decompile.rs +++ b/core/tests/test_decompile.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod benchmark { use clap_verbosity_flag::Verbosity; - use heimdall_common::testing::benchmarks::async_bench; + use heimdall_common::utils::testing::benchmarks::async_bench; use heimdall_core::decompile::DecompilerArgs; @@ -117,7 +117,7 @@ mod benchmark { #[cfg(test)] mod integration_tests { use clap_verbosity_flag::Verbosity; - use heimdall_common::io::file::delete_path; + use heimdall_common::utils::io::file::delete_path; use heimdall_core::decompile::DecompilerArgs; #[tokio::test] diff --git a/core/tests/test_disassemble.rs b/core/tests/test_disassemble.rs index 5f70c186..44be15f3 100644 --- a/core/tests/test_disassemble.rs +++ b/core/tests/test_disassemble.rs @@ -2,7 +2,7 @@ mod benchmarks { use clap_verbosity_flag::Verbosity; - use heimdall_common::testing::benchmarks::async_bench; + use heimdall_common::utils::testing::benchmarks::async_bench; use heimdall_core::disassemble::{disassemble, DisassemblerArgs}; #[tokio::test] diff --git a/core/tests/test_snapshot.rs b/core/tests/test_snapshot.rs index 9c17c02e..82a520d6 100644 --- a/core/tests/test_snapshot.rs +++ b/core/tests/test_snapshot.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod benchmark { use clap_verbosity_flag::Verbosity; - use heimdall_common::testing::benchmarks::async_bench; + use heimdall_common::utils::testing::benchmarks::async_bench; use heimdall_core::snapshot::SnapshotArgs; @@ -43,7 +43,7 @@ mod benchmark { #[cfg(test)] mod integration_tests { use clap_verbosity_flag::Verbosity; - use heimdall_common::io::file::delete_path; + use heimdall_common::utils::io::file::delete_path; use heimdall_core::snapshot::SnapshotArgs; #[tokio::test] diff --git a/scripts/clippy b/scripts/clippy index d0481a4c..815b556b 100644 --- a/scripts/clippy +++ b/scripts/clippy @@ -1,8 +1,8 @@ -cargo clippy --all-features -- --allow clippy::new_without_default --allow clippy::redundant_field_names --allow clippy::too_many_arguments --allow clippy::format_in_format_args --allow clippy::should_implement_trait +cargo clippy --all-features -- --allow clippy::new_without_default --allow clippy::too_many_arguments --allow clippy::format_in_format_args --allow clippy::should_implement_trait # If --fix is used, run the above command with `--fix` instead of `--all-features` # to fix the code. if [ "$1" = "--fix" ]; then - cargo +nightly clippy --fix --all-features -Z unstable-options --allow-dirty --allow-staged -- --allow clippy::new_without_default --allow clippy::redundant_field_names --allow clippy::too_many_arguments --allow clippy::format_in_format_args --allow clippy::should_implement_trait + cargo +nightly clippy --fix --all-features -Z unstable-options --allow-dirty --allow-staged -- --allow clippy::new_without_default --allow clippy::too_many_arguments --allow clippy::format_in_format_args --allow clippy::should_implement_trait fi diff --git a/scripts/test b/scripts/test index b25e5e68..b5714126 100644 --- a/scripts/test +++ b/scripts/test @@ -32,5 +32,5 @@ if [ "$1" = "--cov" ]; then exit 1 fi else - cargo nextest r --no-fail-fast + cargo nextest r --no-fail-fast --release fi