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/opcodes.rs b/common/src/ether/evm/core/opcodes.rs index a24580cc..8a5f3835 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 }, + _ => panic!("Invalid opcode: {}", code), + } } } -// 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,39 @@ 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(0x00); + assert_eq!(unknown_opcode.code, 0x00); + assert_eq!(unknown_opcode.name, "INVALID"); + } + + #[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..8e281356 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(), 0x...); + /// ``` 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/tests.rs b/common/src/ether/evm/core/tests.rs index d786d92e..90520984 100644 --- a/common/src/ether/evm/core/tests.rs +++ b/common/src/ether/evm/core/tests.rs @@ -540,179 +540,6 @@ mod test_vm { } } -#[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; @@ -896,76 +723,6 @@ mod test_storage { } } -#[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; diff --git a/common/src/ether/evm/core/vm.rs b/common/src/ether/evm/core/vm.rs index 13d63a2d..30d99efb 100644 --- a/common/src/ether/evm/core/vm.rs +++ b/common/src/ether/evm/core/vm.rs @@ -140,7 +140,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::>(); diff --git a/common/src/ether/lexers/solidity.rs b/common/src/ether/lexers/solidity.rs index 9f4711d5..47fc0cb4 100644 --- a/common/src/ether/lexers/solidity.rs +++ b/common/src/ether/lexers/solidity.rs @@ -384,7 +384,7 @@ impl WrappedOpcode { // 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: inputs } } } diff --git a/core/src/disassemble/mod.rs b/core/src/disassemble/mod.rs index 19d7dc83..052eea36 100644 --- a/core/src/disassemble/mod.rs +++ b/core/src/disassemble/mod.rs @@ -4,7 +4,7 @@ use clap::{AppSettings, Parser}; use derive_builder::Builder; use heimdall_common::{ constants::{ADDRESS_REGEX, BYTECODE_REGEX}, - ether::{evm::core::opcodes::opcode, rpc::get_code}, + ether::{evm::core::opcodes::Opcode, rpc::get_code}, utils::{ io::logging::Logger, strings::{decode_hex, encode_hex}, @@ -100,7 +100,7 @@ pub async fn disassemble(args: DisassemblerArgs) -> Result