diff --git a/1000.pdf b/1000.pdf new file mode 100644 index 0000000..ce29d3e Binary files /dev/null and b/1000.pdf differ diff --git a/Cargo.lock b/Cargo.lock index 720f908..6f3e9bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,13 +183,16 @@ dependencies = [ "lazy_static", "num256", "paste", + "phf", "primitive-types", + "quote", "rlp", "serde", "serde_json", "sha3", "test_gen", "thiserror", + "util", ] [[package]] @@ -397,6 +400,48 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -591,6 +636,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "static_assertions" version = "1.1.0" @@ -726,6 +777,17 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.53", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index d366a54..374b3a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ +workspace = { members = ["util"] } [package] name = "ethereum_evm" version = "0.1.0" @@ -6,6 +7,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +test_gen = { path = "./test_gen" } +util = { path = "./util" } array-init = "2.1.0" ethereum = "0.15.0" primitive-types = { version = "0.12", features = ["rlp", "serde"] } @@ -15,7 +18,8 @@ rlp = "0.5.2" serde = { version = "1", features = ["derive"] } serde_json = "1" sha3 = "0.10.8" -test_gen = { path = "./test_gen" } thiserror = "1.0.58" num256 = "0.5.1" lazy_static = "1.4.0" +phf = {version = "0.11.2", features = ["macros"]} +quote = "1.0.35" diff --git a/src/bytecode_spec.rs b/src/bytecode_spec.rs index 655dde0..99dae92 100644 --- a/src/bytecode_spec.rs +++ b/src/bytecode_spec.rs @@ -1,161 +1,164 @@ pub mod opcodes { + use lazy_static::lazy_static; + use util::opcode_map; + opcode_map! { + pub const STOP: u8 = 0x00u8; + pub const ADD: u8 = 0x01u8; + pub const MUL: u8 = 0x02u8; + pub const SUB: u8 = 0x03u8; + pub const DIV: u8 = 0x04u8; + pub const SDIV: u8 = 0x05u8; + pub const MOD: u8 = 0x06u8; + pub const SMOD: u8 = 0x07u8; + pub const ADDMOD: u8 = 0x08u8; + pub const MULMOD: u8 = 0x09u8; + pub const EXP: u8 = 0x0Au8; + pub const SIGNEXTEND: u8 = 0x0Bu8; - pub const STOP: u8 = 0x00; - pub const ADD: u8 = 0x01; - pub const MUL: u8 = 0x02; - pub const SUB: u8 = 0x03; - pub const DIV: u8 = 0x04; - pub const SDIV: u8 = 0x05; - pub const MOD: u8 = 0x06; - pub const SMOD: u8 = 0x07; - pub const ADDMOD: u8 = 0x08; - pub const MULMOD: u8 = 0x09; - pub const EXP: u8 = 0x0A; - pub const SIGNEXTEND: u8 = 0x0B; + pub const LT: u8 = 0x10u8; + pub const GT: u8 = 0x11u8; + pub const SLT: u8 = 0x12u8; + pub const SGT: u8 = 0x13u8; + pub const EQ: u8 = 0x14u8; + pub const ISZERO: u8 = 0x15u8; + pub const AND: u8 = 0x16u8; + pub const OR: u8 = 0x17u8; + pub const XOR: u8 = 0x18u8; + pub const NOT: u8 = 0x19u8; + pub const BYTE: u8 = 0x1Au8; + pub const SHL: u8 = 0x1Bu8; + pub const SHR: u8 = 0x1Cu8; + pub const SAR: u8 = 0x1Du8; - pub const LT: u8 = 0x10; - pub const GT: u8 = 0x11; - pub const SLT: u8 = 0x12; - pub const SGT: u8 = 0x13; - pub const EQ: u8 = 0x14; - pub const ISZERO: u8 = 0x15; - pub const AND: u8 = 0x16; - pub const OR: u8 = 0x17; - pub const XOR: u8 = 0x18; - pub const NOT: u8 = 0x19; - pub const BYTE: u8 = 0x1A; - pub const SHL: u8 = 0x1B; - pub const SHR: u8 = 0x1C; - pub const SAR: u8 = 0x1D; + pub const KECCAK256: u8 = 0x20u8; - pub const KECCAK256: u8 = 0x20; + pub const ADDRESS: u8 = 0x30u8; + pub const BALANCE: u8 = 0x31u8; + pub const ORIGIN: u8 = 0x32u8; + pub const CALLER: u8 = 0x33u8; + pub const CALLVALUE: u8 = 0x34u8; + pub const CALLDATALOAD: u8 = 0x35u8; + pub const CALLDATASIZE: u8 = 0x36u8; + pub const CALLDATACOPY: u8 = 0x37u8; + pub const CODESIZE: u8 = 0x38u8; + pub const CODECOPY: u8 = 0x39u8; + pub const GASPRICE: u8 = 0x3Au8; + pub const EXTCODESIZE: u8 = 0x3Bu8; + pub const EXTCODECOPY: u8 = 0x3Cu8; + pub const RETURNDATASIZE: u8 = 0x3Du8; + pub const RETURNDATACOPY: u8 = 0x3Eu8; + pub const EXTCODEHASH: u8 = 0x3Fu8; + pub const BLOCKHASH: u8 = 0x40u8; + pub const COINBASE: u8 = 0x41u8; + pub const TIMESTAMP: u8 = 0x42u8; + pub const NUMBER: u8 = 0x43u8; + pub const DIFFICULTY: u8 = 0x44u8; + pub const GASLIMIT: u8 = 0x45u8; + pub const CHAINID: u8 = 0x46u8; + pub const SELFBALANCE: u8 = 0x47u8; + pub const BASEFEE: u8 = 0x48u8; - pub const ADDRESS: u8 = 0x30; - pub const BALANCE: u8 = 0x31; - pub const ORIGIN: u8 = 0x32; - pub const CALLER: u8 = 0x33; - pub const CALLVALUE: u8 = 0x34; - pub const CALLDATALOAD: u8 = 0x35; - pub const CALLDATASIZE: u8 = 0x36; - pub const CALLDATACOPY: u8 = 0x37; - pub const CODESIZE: u8 = 0x38; - pub const CODECOPY: u8 = 0x39; - pub const GASPRICE: u8 = 0x3A; - pub const EXTCODESIZE: u8 = 0x3B; - pub const EXTCODECOPY: u8 = 0x3C; - pub const RETURNDATASIZE: u8 = 0x3D; - pub const RETURNDATACOPY: u8 = 0x3E; - pub const EXTCODEHASH: u8 = 0x3F; - pub const BLOCKHASH: u8 = 0x40; - pub const COINBASE: u8 = 0x41; - pub const TIMESTAMP: u8 = 0x42; - pub const NUMBER: u8 = 0x43; - pub const DIFFICULTY: u8 = 0x44; - pub const GASLIMIT: u8 = 0x45; - pub const CHAINID: u8 = 0x46; - pub const SELFBALANCE: u8 = 0x47; - pub const BASEFEE: u8 = 0x48; + pub const POP: u8 = 0x50u8; + pub const MLOAD: u8 = 0x51u8; + pub const MSTORE: u8 = 0x52u8; + pub const MSTORE8: u8 = 0x53u8; + pub const SLOAD: u8 = 0x54u8; + pub const SSTORE: u8 = 0x55u8; + pub const JUMP: u8 = 0x56u8; + pub const JUMPI: u8 = 0x57u8; + pub const PC: u8 = 0x58u8; + pub const MSIZE: u8 = 0x59u8; + pub const GAS: u8 = 0x5Au8; + pub const JUMPDEST: u8 = 0x5Bu8; - pub const POP: u8 = 0x50; - pub const MLOAD: u8 = 0x51; - pub const MSTORE: u8 = 0x52; - pub const MSTORE8: u8 = 0x53; - pub const SLOAD: u8 = 0x54; - pub const SSTORE: u8 = 0x55; - pub const JUMP: u8 = 0x56; - pub const JUMPI: u8 = 0x57; - pub const PC: u8 = 0x58; - pub const MSIZE: u8 = 0x59; - pub const GAS: u8 = 0x5A; - pub const JUMPDEST: u8 = 0x5B; + pub const PUSH_1: u8 = 0x60u8; + pub const PUSH_2: u8 = 0x61u8; + pub const PUSH_3: u8 = 0x62u8; + pub const PUSH_4: u8 = 0x63u8; + pub const PUSH_5: u8 = 0x64u8; + pub const PUSH_6: u8 = 0x65u8; + pub const PUSH_7: u8 = 0x66u8; + pub const PUSH_8: u8 = 0x67u8; + pub const PUSH_9: u8 = 0x68u8; + pub const PUSH_10: u8 = 0x69u8; + pub const PUSH_11: u8 = 0x6au8; + pub const PUSH_12: u8 = 0x6bu8; + pub const PUSH_13: u8 = 0x6cu8; + pub const PUSH_14: u8 = 0x6du8; + pub const PUSH_15: u8 = 0x6eu8; + pub const PUSH_16: u8 = 0x6fu8; + pub const PUSH_17: u8 = 0x70u8; + pub const PUSH_18: u8 = 0x71u8; + pub const PUSH_19: u8 = 0x72u8; + pub const PUSH_20: u8 = 0x73u8; + pub const PUSH_21: u8 = 0x74u8; + pub const PUSH_22: u8 = 0x75u8; + pub const PUSH_23: u8 = 0x76u8; + pub const PUSH_24: u8 = 0x77u8; + pub const PUSH_25: u8 = 0x78u8; + pub const PUSH_26: u8 = 0x79u8; + pub const PUSH_27: u8 = 0x7au8; + pub const PUSH_28: u8 = 0x7bu8; + pub const PUSH_29: u8 = 0x7cu8; + pub const PUSH_30: u8 = 0x7du8; + pub const PUSH_31: u8 = 0x7eu8; + pub const PUSH_32: u8 = 0x7fu8; - pub const PUSH_1: u8 = 0x60; - pub const PUSH_2: u8 = 0x61; - pub const PUSH_3: u8 = 0x62; - pub const PUSH_4: u8 = 0x63; - pub const PUSH_5: u8 = 0x64; - pub const PUSH_6: u8 = 0x65; - pub const PUSH_7: u8 = 0x66; - pub const PUSH_8: u8 = 0x67; - pub const PUSH_9: u8 = 0x68; - pub const PUSH_10: u8 = 0x69; - pub const PUSH_11: u8 = 0x6a; - pub const PUSH_12: u8 = 0x6b; - pub const PUSH_13: u8 = 0x6c; - pub const PUSH_14: u8 = 0x6d; - pub const PUSH_15: u8 = 0x6e; - pub const PUSH_16: u8 = 0x6f; - pub const PUSH_17: u8 = 0x70; - pub const PUSH_18: u8 = 0x71; - pub const PUSH_19: u8 = 0x72; - pub const PUSH_20: u8 = 0x73; - pub const PUSH_21: u8 = 0x74; - pub const PUSH_22: u8 = 0x75; - pub const PUSH_23: u8 = 0x76; - pub const PUSH_24: u8 = 0x77; - pub const PUSH_25: u8 = 0x78; - pub const PUSH_26: u8 = 0x79; - pub const PUSH_27: u8 = 0x7a; - pub const PUSH_28: u8 = 0x7b; - pub const PUSH_29: u8 = 0x7c; - pub const PUSH_30: u8 = 0x7d; - pub const PUSH_31: u8 = 0x7e; - pub const PUSH_32: u8 = 0x7f; + pub const DUP_1: u8 = 0x80u8; + pub const DUP_2: u8 = 0x81u8; + pub const DUP_3: u8 = 0x82u8; + pub const DUP_4: u8 = 0x83u8; + pub const DUP_5: u8 = 0x84u8; + pub const DUP_6: u8 = 0x85u8; + pub const DUP_7: u8 = 0x86u8; + pub const DUP_8: u8 = 0x87u8; + pub const DUP_9: u8 = 0x88u8; + pub const DUP_10: u8 = 0x89u8; + pub const DUP_11: u8 = 0x8au8; + pub const DUP_12: u8 = 0x8bu8; + pub const DUP_13: u8 = 0x8cu8; + pub const DUP_14: u8 = 0x8du8; + pub const DUP_15: u8 = 0x8eu8; + pub const DUP_16: u8 = 0x8fu8; - pub const DUP_1: u8 = 0x80; - pub const DUP_2: u8 = 0x81; - pub const DUP_3: u8 = 0x82; - pub const DUP_4: u8 = 0x83; - pub const DUP_5: u8 = 0x84; - pub const DUP_6: u8 = 0x85; - pub const DUP_7: u8 = 0x86; - pub const DUP_8: u8 = 0x87; - pub const DUP_9: u8 = 0x88; - pub const DUP_10: u8 = 0x89; - pub const DUP_11: u8 = 0x8a; - pub const DUP_12: u8 = 0x8b; - pub const DUP_13: u8 = 0x8c; - pub const DUP_14: u8 = 0x8d; - pub const DUP_15: u8 = 0x8e; - pub const DUP_16: u8 = 0x8f; + pub const SWAP_1: u8 = 0x90u8; + pub const SWAP_2: u8 = 0x91u8; + pub const SWAP_3: u8 = 0x92u8; + pub const SWAP_4: u8 = 0x93u8; + pub const SWAP_5: u8 = 0x94u8; + pub const SWAP_6: u8 = 0x95u8; + pub const SWAP_7: u8 = 0x96u8; + pub const SWAP_8: u8 = 0x97u8; + pub const SWAP_9: u8 = 0x98u8; + pub const SWAP_10: u8 = 0x99u8; + pub const SWAP_11: u8 = 0x9au8; + pub const SWAP_12: u8 = 0x9bu8; + pub const SWAP_13: u8 = 0x9cu8; + pub const SWAP_14: u8 = 0x9du8; + pub const SWAP_15: u8 = 0x9eu8; + pub const SWAP_16: u8 = 0x9fu8; - pub const SWAP_1: u8 = 0x90; - pub const SWAP_2: u8 = 0x91; - pub const SWAP_3: u8 = 0x92; - pub const SWAP_4: u8 = 0x93; - pub const SWAP_5: u8 = 0x94; - pub const SWAP_6: u8 = 0x95; - pub const SWAP_7: u8 = 0x96; - pub const SWAP_8: u8 = 0x97; - pub const SWAP_9: u8 = 0x98; - pub const SWAP_10: u8 = 0x99; - pub const SWAP_11: u8 = 0x9a; - pub const SWAP_12: u8 = 0x9b; - pub const SWAP_13: u8 = 0x9c; - pub const SWAP_14: u8 = 0x9d; - pub const SWAP_15: u8 = 0x9e; - pub const SWAP_16: u8 = 0x9f; + pub const LOG_0: u8 = 0xa0u8; + pub const LOG_1: u8 = 0xa1u8; + pub const LOG_2: u8 = 0xa2u8; + pub const LOG_3: u8 = 0xa3u8; + pub const LOG_4: u8 = 0xa4u8; - pub const LOG_0: u8 = 0xa0; - pub const LOG_1: u8 = 0xa1; - pub const LOG_2: u8 = 0xa2; - pub const LOG_3: u8 = 0xa3; - pub const LOG_4: u8 = 0xa4; + pub const PUSH: u8 = 0xB0u8; + pub const DUP: u8 = 0xB1u8; + pub const SWAP: u8 = 0xB2u8; - pub const PUSH: u8 = 0xB0; - pub const DUP: u8 = 0xB1; - pub const SWAP: u8 = 0xB2; + pub const CREATE: u8 = 0xF0u8; + pub const CALL: u8 = 0xF1u8; + pub const CALLCODE: u8 = 0xF2u8; + pub const RETURN: u8 = 0xF3u8; + pub const DELEGATECALL: u8 = 0xF4u8; + pub const CREATE2: u8 = 0xF5u8; - pub const CREATE: u8 = 0xF0; - pub const CALL: u8 = 0xF1; - pub const CALLCODE: u8 = 0xF2; - pub const RETURN: u8 = 0xF3; - pub const DELEGATECALL: u8 = 0xF4; - pub const CREATE2: u8 = 0xF5; + pub const STATICCALL: u8 = 0xFAu8; - pub const STATICCALL: u8 = 0xFA; + pub const REVERT: u8 = 0xFDu8; - pub const REVERT: u8 = 0xFD; - - pub const SELFDESTRUCT: u8 = 0xFF; + pub const SELFDESTRUCT: u8 = 0xFFu8; + } } diff --git a/src/evm.rs b/src/evm.rs deleted file mode 100644 index 06ce704..0000000 --- a/src/evm.rs +++ /dev/null @@ -1,909 +0,0 @@ -use std::ops::Rem; - -use crate::gas_calculator::{call_data_gas_cost, GasRecorder}; -use crate::runtime; -// use crate::main; -use crate::state::memory::Memory; -use crate::state::stack::Stack; -use crate::util::{ - self, h256_to_u256, int256_to_uint256, keccak256, u256_to_array, u256_to_h256, u256_to_uint256, - uint256_to_int256, MAX_UINT256, MAX_UINT256_COMPLEMENT, ZERO, -}; -use crate::{bytecode_spec::opcodes, runtime::Runtime}; -use num256::{Int256, Uint256}; -use paste::paste; -use primitive_types::U256; -#[derive(Clone)] -struct Transaction { - pub origin: U256, - pub gas_price: U256, -} -struct Message { - pub caller: U256, - pub value: U256, - pub data: Memory, -} -pub struct EVMContext { - // stack_pointer: usize, - stack: Stack, - memory: Memory, - program: Memory, - program_counter: usize, - contract_address: U256, - transaction: Transaction, - message: Message, - last_return_data: Memory, - result: Memory, - gas_input: u64, - gas_price: U256, - stopped: bool, - nested_index: usize, - gas_recorder: GasRecorder, -} - -impl EVMContext { - #[inline] - pub fn execute_transaction( - runtime: &mut impl Runtime, - contract: U256, - origin: U256, - gas: u64, - gas_price: U256, - value: U256, - data: Vec, - debug: bool, - ) -> usize { - let message = Message { - caller: contract, - value: value, - data: Memory::from(data, &mut GasRecorder { gas_usage: 0 }), - }; - let transaction = Transaction { - origin: origin, - gas_price: gas_price, - }; - let mut evm = EVMContext::create_sub_context( - contract, - message, - gas, - runtime.code(contract), - transaction, - gas_price, - 0, - ); - evm.gas_recorder.record_gas(21000); - evm.execute_program(runtime, debug); - evm.gas_recorder.gas_usage - } - - #[inline] - fn create_sub_context( - address: U256, - message: Message, - gas: u64, - code: Vec, - transaction: Transaction, - gas_price: U256, - nested_index: usize, - ) -> EVMContext { - EVMContext { - stack: Stack::new(), - memory: Memory::new(), - program: Memory::from(code, &mut GasRecorder { gas_usage: 0 }), - program_counter: 0, - contract_address: address, - // TODO remove need to clone here - transaction: transaction, - message: message, - last_return_data: Memory::new(), - result: Memory::new(), - gas_input: gas, - gas_price: gas_price, - stopped: false, - nested_index: nested_index, - gas_recorder: GasRecorder { gas_usage: 0 }, - } - } - - #[inline] - fn execute_program(&mut self, runtime: &mut impl Runtime, debug: bool) -> bool { - runtime.add_context(); - - let result = || -> bool { - self.gas_recorder - .record_gas(call_data_gas_cost(&self.message.data.bytes)); - if debug { - println!("Call Data Gas Cost: {}", self.gas_recorder.gas_usage); - } - while !self.stopped { - let result = self.execute_next_instruction(runtime, debug); - if !result { - return false; - } - } - if debug { - println!( - "Gas : {:x}", - self.gas_input - self.gas_recorder.gas_usage as u64 - ); - } - true - }(); - if result { - runtime.revert_context(); - } else { - runtime.merge_context(); - } - - result - } - - #[inline] - fn execute_next_instruction(&mut self, runtime: &mut impl Runtime, debug: bool) -> bool { - /* - Run the next instruction, adjusting gas usage and return a bool that is true if okay, false if exception - */ - - macro_rules! pop { - ($($input:tt)*) => {{ - let result = self.stack.pop(); - let result = match result { - Err(()) => { - return false; - } - - Ok(value) => value, - }; - result - }}; - } - - - let mut make_call = | - this: &mut EVMContext, - maintain_storage: bool| -> bool { - let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size) = ( - pop!().as_u64(), - pop!(), - pop!(), - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - ); - let code: Vec = runtime.code(address); - if !value.eq(&U256::zero()) { - this.gas_recorder.record_gas(2300); - } - let one_64th_value = (this.gas_input - this.gas_recorder.gas_usage.clone() as u64) * 63 / 64; - if gas > one_64th_value { - gas = one_64th_value; - } - let address_access_cost = if runtime.is_hot(address) { - 100 - } else { - runtime.mark_hot(address); - 2600 - }; - // TODO check gas is okay - let mut sub_evm = EVMContext::create_sub_context( - if maintain_storage { - this.contract_address - } else { - address - }, - Message { - caller: this.contract_address, - data: { - let mut memory: Memory = Memory::new(); - memory.copy_from( - &mut this.memory, - args_offset, - 0, - args_size, - &mut this.gas_recorder, - ); - memory - }, - value: value, - }, - gas, - code, - this.transaction.clone(), - this.gas_price, - this.nested_index + 1, - ); - // TODO calculate cost of call data - - let response = sub_evm.execute_program(runtime, debug); - this.last_return_data = sub_evm.result; - // let current_memory_cost = self.memory.memory_cost; - this.memory.copy_from( - &mut this.last_return_data, - 0, - ret_offset, - ret_size, - &mut this.gas_recorder, - ); - this.stack.push(U256::from(response as u64)); - let code_execution_cost = sub_evm.gas_recorder.gas_usage; - let positive_value_cost = if !value.eq(&U256::zero()) { 6700 } else { 0 }; - let value_to_empty_account_cost = if !value.eq(&U256::zero()) - && runtime.nonce(address).eq(&U256::zero()) - && runtime.code_size(address).eq(&U256::zero()) - && runtime.balance(address).eq(&U256::zero()) - { - 25000 - } else { - 0 - }; - this.gas_recorder.record_gas( - (code_execution_cost - + address_access_cost - + positive_value_cost - + value_to_empty_account_cost) as usize, - ); - response - }; - - // Declared here so that self is in scope - macro_rules! debug { - ($($input:tt)*) => { - if debug { - let tabs = "\t".repeat(self.nested_index as usize); - print!("{}", tabs); - print!($($input)*); - println!(" Gas: {:x}", self.gas_input - self.gas_recorder.clone().gas_usage as u64); - } - }; - } - - macro_rules! debug_match { - ($opcode:expr, { $( $pat:pat => $block:block ),* }) => { - match $opcode { - $( - $pat => { - #[allow(unreachable_code)] - #[allow(unused_variables)]{ - { - if debug { - print!("PC: {} ", self.program_counter); - } - let current_gas_usage = self.gas_recorder.gas_usage; - - if !(stringify!($pat).contains("PUSH") || - stringify!($pat).contains("DUP") || - stringify!($pat).contains("SWAP")) { - debug!(stringify!($pat)); - // print!(" Gas: {:x}", self.gas_input - self.gas_usage); - } - // print!(" Gas: {:x}", self.gas_input - self.gas_usage); - $block - // TODO create more elegant solution to this problem - // println!(" Gas: {}", self.gas_usage - current_gas_usage); - } - } - }),* - _ => {} - } - }; - } - - - let opcode: u8 = self.program[self.program_counter]; - debug_match!(opcode, { - - opcodes::STOP => { - self.stopped = true; - return true; - }, - - opcodes::ADD => { - let (a, b) = (pop!(), pop!()); - self.stack.push(a.overflowing_add(b).0); - self.gas_recorder.record_gas(3); - }, - - opcodes::MUL => { - let (a, b) = (pop!(), pop!()); - self.stack.push(a.overflowing_mul(b).0); - self.gas_recorder.record_gas(5); - }, - - opcodes::SUB => { - let (a, b) = (pop!(), pop!()); - self.stack.push(a.overflowing_sub(b).0); - self.gas_recorder.record_gas(3); - }, - - opcodes::DIV => { - let (a, b) = (pop!(), pop!()); - match b { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - self.stack.push(a.div_mod(b).0); - } - } - self.gas_recorder.record_gas(5); - }, - - opcodes::SDIV => { - let (a, b) = (pop!(), pop!()); - match b { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - let a = u256_to_uint256(a); - let b = u256_to_uint256(b); - // Handle overflow case of -1 * MAX_POSITIVE - if a == *MAX_UINT256_COMPLEMENT && b == *MAX_UINT256 { - self.stack.push(U256::from(MAX_UINT256_COMPLEMENT.to_be_bytes())); - } - else { - let a = uint256_to_int256(a); - let b = uint256_to_int256(b); - let result: Uint256 = int256_to_uint256(a / b); - let result = U256::from(result.to_be_bytes()); - self.stack.push(result); - } - - } - } - self.gas_recorder.record_gas(5); - }, - - opcodes::MOD => { - let (a, b) = (pop!(), pop!()); - match b { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - self.stack.push(a.rem(b)); - } - } - self.gas_recorder.record_gas(5); - }, - - opcodes::SMOD => { - let (a, b) = (pop!(), pop!()); - match b { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - let a = uint256_to_int256(u256_to_uint256(a)); - let b = uint256_to_int256(u256_to_uint256(b)); - let result: Uint256 = int256_to_uint256(a.rem(b)); - let result = U256::from(result.to_be_bytes()); - self.stack.push(result); - } - } - self.gas_recorder.record_gas(5); - - }, - - opcodes::ADDMOD => { - let (a, b, c) = (pop!(), pop!(), pop!()); - match c { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - self.stack.push(a.checked_rem(c).unwrap().overflowing_add(b.checked_rem(c).unwrap()).0); - } - } - self.gas_recorder.record_gas(8); - }, - - opcodes::MULMOD => { - let (a, b, c) = (pop!(), pop!(), pop!()); - match c { - ZERO => { - self.stack.push(U256::zero()); - }, - _ => { - self.stack.push(a.checked_rem(c).unwrap().overflowing_mul(b.checked_rem(c).unwrap()).0.checked_rem(c).unwrap()); - } - } - // println!("a: {}, b: {}, c: {}", a, b, c); - self.gas_recorder.record_gas(8); - }, - - opcodes::EXP => { - let (a, exponent) = (pop!(), pop!()); - self.stack.push(a.overflowing_pow(exponent).0); - self.gas_recorder.record_gas(10 + 50 * (util::bytes_for_u256(&exponent) as usize)); - }, - - opcodes::SIGNEXTEND => { - let (x, y) = (pop!(), pop!()); - // X is the number of bytes of the input lower_mask - if x > U256::from(31) { - self.stack.push(y); - } else { - let sign = y >> (x*8 + 7) & U256::from(1 as u64); - let lower_mask = if x == U256::from(31) { - U256::max_value() - } else { - (U256::from(1) << ((x+1)*8)) - 1 - }; - if sign == ZERO { - self.stack.push(y & lower_mask); - } else { - let higher_mask = !lower_mask; - self.stack.push(y | higher_mask); - } - } - self.gas_recorder.record_gas(5); - }, - - opcodes::LT => { - let (a, b) = (pop!(), pop!()); - self.stack.push(U256::from((a < b) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::GT => { - let (a, b) = (pop!(), pop!()); - self.stack.push(U256::from((a > b) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::SLT => { - let (a, b) = (pop!(), pop!()); - let a = uint256_to_int256(u256_to_uint256(a)); - let b = uint256_to_int256(u256_to_uint256(b)); - self.stack.push(U256::from((a < b) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::SGT => { - // debug!("SGT"); - let (a, b) = (pop!(), pop!()); - let a = uint256_to_int256(u256_to_uint256(a)); - let b = uint256_to_int256(u256_to_uint256(b)); - self.stack.push(U256::from((a > b) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::EQ => { - let (a, b) = (pop!(), pop!()); - self.stack.push(U256::from((a == b) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::ISZERO => { - let data = pop!(); - self.stack.push(U256::from(data.eq(&U256::zero()) as u64)); - self.gas_recorder.record_gas(3); - }, - - opcodes::AND => { - // debug!("AND"); - let (a, b) = (pop!(), pop!()); - self.stack.push(a & b); - self.gas_recorder.record_gas(3); - }, - - opcodes::OR => { - let (a, b) = (pop!(), pop!()); - self.stack.push(a | b); - self.gas_recorder.record_gas(3); - }, - - opcodes::XOR => { - let (a, b) = (pop!(), pop!()); - self.stack.push(a ^ b); - self.gas_recorder.record_gas(3); - }, - - opcodes::NOT => { - let a = pop!(); - self.stack.push(!a); - self.gas_recorder.record_gas(3); - }, - - opcodes::BYTE => { - let (i, x) = (pop!(), pop!()); - println!("i: {}, x: {}", i, x); - if i > U256::from(31) { - self.stack.push(U256::zero()); - } else { - self.stack.push((x >> (U256::from(248) - i * 8)) & (0xFF as u64).into()); - } - self.gas_recorder.record_gas(3); - }, - - opcodes::SHL => { - let (shift, value) = (pop!(), pop!()); - if shift > 31.into() { - self.stack.push(U256::zero()); - } else { - self.stack.push(value << shift); - } - self.gas_recorder.record_gas(3); - }, - - opcodes::SHR => { - let (shift, value) = (pop!(), pop!()); - if shift > 31.into() { - self.stack.push(U256::zero()); - } else { - self.stack.push(value >> shift); - } - self.gas_recorder.record_gas(3); - }, - - opcodes::SAR => { - // TODO - // let (shift, value) = (pop!(), pop!().as_i256()); - // self.stack.push((value >> shift).as_u256()); - // self.gas_usage += 3; - }, - - opcodes::KECCAK256 => { - let (offset, length) = (pop!().as_usize(), pop!().as_usize()); - let bytes = self.memory.read_bytes(offset, length); - self.stack.push(U256::from(keccak256(&bytes).as_bytes())); - // As of the Ethereum Yellow Paper (EIP-62), the gas cost for the KECCAK256 instruction is 30 gas plus an additional 6 gas for each 256-bit word (or part thereof) of input data. - self.gas_recorder.record_gas(30 + (length.div_ceil(256) as u64 * 6) as usize); - }, - - opcodes::ADDRESS => { - self.stack.push(self.contract_address); - self.gas_recorder.record_gas(2); - }, - - opcodes::BALANCE => { - let address = pop!(); - self.stack.push(runtime.balance(address)); - if runtime.is_hot(address) { self.gas_recorder.record_gas(100); } else { self.gas_recorder.record_gas(2600); }; - runtime.mark_hot(address); - }, - - opcodes::ORIGIN => { - self.stack.push(self.transaction.origin); - self.gas_recorder.record_gas(2); - }, - - opcodes::CALLER => { - self.stack.push(self.message.caller); - self.gas_recorder.record_gas(2); - }, - - opcodes::CALLVALUE => { - self.stack.push(self.message.value); - self.gas_recorder.record_gas(2); - }, - - opcodes::CALLDATALOAD => { - // TODO fix - let index = pop!().as_u64() as usize; - self.stack.push(self.message.data.read(index)); - self.gas_recorder.record_gas(3); - }, - - opcodes::CALLDATASIZE => { - self.stack.push(U256::from(self.message.data.len() as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::CALLDATACOPY => { - let (dest_offset, offset, length) = ( - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - ); - // let current_memory_usage = self.memory.memory_cost; - self.memory - .copy_from(&mut self.message.data, offset, dest_offset, length, &mut self.gas_recorder); - // let new_usage = self.memory.memory_cost; - // self.gas_usage += - // 3 + 3 * (length as u64 + 31 / 32) + (new_usage - current_memory_usage).as_u64(); - }, - - opcodes::CODESIZE => { - self.stack.push(U256::from(self.program.len() as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::CODECOPY => { - let (dest_offset, offset, length) = ( - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - ); - - self.memory - .copy_from(&mut self.program, offset, dest_offset, length, &mut self.gas_recorder); - }, - - opcodes::GASPRICE => { - self.stack.push(self.transaction.gas_price); - self.gas_recorder.record_gas(2); - }, - - opcodes::EXTCODESIZE => { - let address = pop!(); - self.stack.push(runtime.code_size(address)); - if runtime.is_hot(address) { self.gas_recorder.record_gas(100); } else { self.gas_recorder.record_gas(2600); }; - runtime.mark_hot(address); - }, - - opcodes::EXTCODECOPY => { - let (addr, dest_offset, offset, length) = ( - pop!(), - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - ); - - self.memory.copy_from( - &mut Memory::from(runtime.code(addr), &mut self.gas_recorder), - offset, - dest_offset, - length, - &mut self.gas_recorder - ); - runtime.mark_hot(addr); - }, - - opcodes::RETURNDATASIZE => { - self.stack - .push(U256::from(self.last_return_data.len() as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::RETURNDATACOPY => { - let (dest_offset, offset, length) = ( - pop!().as_usize(), - pop!().as_usize(), - pop!().as_usize(), - ); - self.memory - .copy_from(&mut self.last_return_data, offset, dest_offset, length, &mut self.gas_recorder); - }, - - opcodes::EXTCODEHASH => { - let addr = pop!(); - self.stack.push(U256::from(util::keccak256_u256(addr).as_bytes())); - if runtime.is_hot(addr) { self.gas_recorder.record_gas(100); } else { self.gas_recorder.record_gas(2600); }; - runtime.mark_hot(addr); - }, - - opcodes::BLOCKHASH => { - let block_number = pop!(); - self.stack.push(h256_to_u256(runtime.block_hash(block_number))); - self.gas_recorder.record_gas(20); - }, - - opcodes::COINBASE => { - self.stack.push(runtime.block_coinbase()); - self.gas_recorder.record_gas(2); - }, - - opcodes::TIMESTAMP => { - self.stack.push(runtime.block_timestamp()); - self.gas_recorder.record_gas(2); - }, - - opcodes::NUMBER => { - self.stack.push(runtime.block_number()); - self.gas_recorder.record_gas(2); - }, - - opcodes::DIFFICULTY => { - self.stack.push(runtime.block_difficulty()); - self.gas_recorder.record_gas(2); - }, - - opcodes::GASLIMIT => { - self.stack.push(runtime.block_gas_limit()); - self.gas_recorder.record_gas(2); - }, - - opcodes::CHAINID => { - self.stack.push(runtime.chain_id()); - self.gas_recorder.record_gas(2); - }, - - opcodes::SELFBALANCE => { - self.stack.push(runtime.balance(self.contract_address)); - self.gas_recorder.record_gas(5); - }, - - opcodes::BASEFEE => { - self.stack.push(runtime.block_base_fee_per_gas()); - self.gas_recorder.record_gas(2); - }, - - opcodes::POP => { - pop!(); - self.gas_recorder.record_gas(2); - }, - - opcodes::MLOAD => { - let offset = pop!().as_usize(); - self.stack.push(self.memory.read(offset)); - self.gas_recorder.record_gas(3); - }, - - opcodes::MSTORE => { - let (offset, value) = (pop!().as_usize(), pop!()); - self.memory.write(offset, value, &mut self.gas_recorder); - self.gas_recorder.record_gas(3); - }, - - opcodes::MSTORE8 => { - let (offset, value) = (pop!().as_usize(), pop!()); - self.memory.write_u8(offset, (value & U256::from(0xFF as u64)).low_u32() as u8, &mut self.gas_recorder); - self.gas_recorder.record_gas(3); - }, - - opcodes::SLOAD => { - let key = pop!(); - if runtime.is_hot_index(self.contract_address, key) { - self.gas_recorder.record_gas(100); - } else { - self.gas_recorder.record_gas(2100); - runtime.mark_hot_index(self.contract_address, key); - } - self.stack - .push(h256_to_u256(runtime.storage(self.contract_address)[&u256_to_h256(key)])); - // self.gas_usage += if runtime.is_hot(self.contract_address) { - // 100 - // } else { - // 2600 - // }; - // runtime.mark_hot(self.contract_address); - }, - - opcodes::SSTORE => { - let (key, value) = (pop!(), pop!()); - if !runtime.is_hot_index(self.contract_address, key){ - self.gas_recorder.record_gas(2100); - runtime.mark_hot_index(self.contract_address, key); - } - let base_dynamic_gas; - if !runtime.storage(self.contract_address).contains_key(&u256_to_h256(key)) && value.eq(&U256::zero()) { - base_dynamic_gas = 100; - } - else { - base_dynamic_gas = if (runtime.storage(self.contract_address).contains_key(&u256_to_h256(key)) - && !h256_to_u256(runtime.storage(self.contract_address)[&u256_to_h256(key)]).eq(&U256::zero())) || value.eq(&U256::zero()) - { - 5000 - } else { - 20000 - }; - runtime.set_storage(self.contract_address, key, u256_to_h256(value)); - } - // TODO already written slot should always be 100 - self.gas_recorder.record_gas(base_dynamic_gas); - }, - - opcodes::JUMP => { - let destination = pop!().as_usize(); - self.program_counter = destination; - self.program_counter -= 1; - self.gas_recorder.record_gas(8); - }, - - - opcodes::JUMPI => { - let (destination, condition) = (pop!().as_usize(), pop!()); - if !condition.eq(&U256::zero()) { - self.program_counter = destination - 1; - } - self.gas_recorder.record_gas(10); - }, - - opcodes::PC => { - self.stack.push(U256::from(self.program_counter as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::MSIZE => { - self.stack.push(U256::from(self.memory.max_index as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::GAS => { - self.stack.push(U256::from(self.gas_input - self.gas_recorder.gas_usage as u64)); - self.gas_recorder.record_gas(2); - }, - - opcodes::JUMPDEST => { - self.gas_recorder.record_gas(1); - }, - - opcodes::PUSH_1..=opcodes::PUSH_32 => { - let push_number = opcode - opcodes::PUSH_1 + 1; - debug!("opcodes::PUSH_{}", push_number); - let bytes = self - .program - .read_bytes(self.program_counter + 1, push_number as usize); - self.program_counter += push_number as usize; - self.stack.push_bytes(&bytes); - self.gas_recorder.record_gas(3); - }, - - opcodes::DUP_1..=opcodes::DUP_16 => { - let dup_number = opcode - opcodes::DUP_1 + 1; - debug!("opcodes::DUP_{}", dup_number); - let value = self.stack.read_nth(dup_number as usize); - self.stack.push(value); - self.gas_recorder.record_gas(3); - }, - - opcodes::SWAP_1..=opcodes::SWAP_16 => { - let swap_number: usize = (opcode - opcodes::SWAP_1 + 1) as usize; - debug!("opcodes::SWAP_{}", swap_number); - let bottom_value = self.stack.read_nth(swap_number); - let top_value = pop!(); - self.stack.write_nth(swap_number - 1, top_value); - self.stack.push(bottom_value); - self.gas_recorder.record_gas(3); - }, - - // TODO log - opcodes::LOG_0 => { - // TODO - }, - - opcodes::CREATE => { - // TODO - }, - - opcodes::CALL => { - make_call(self,false); - }, - - opcodes::CALLCODE => { - make_call(self,true); - }, - - opcodes::RETURN => { - let (offset, size) = (pop!().as_usize(), pop!().as_usize()); - self.result.set_length(size); - self.result.copy_from(&mut self.memory, offset, 0, size, &mut self.gas_recorder); - self.stopped = true; - return true; - }, - - opcodes::DELEGATECALL => { - // TODO - // Same as call but storage, sender and value remain the same - self.gas_recorder.record_gas(100); - }, - - opcodes::CREATE2 => { - // TODO - // Same as create but except the salt allows the new contract to be deployed at a consistent, deterministic address. - // Should deployment succeed, the account's code is set to the return data resulting from executing the initialisation code. - } - }); - - self.program_counter += 1; - if self.check_gas_usage() { - // TODO add revert logic etc. - return false; - } - - true - } - - #[inline] - fn check_gas_usage(&self) -> bool { - self.gas_recorder.gas_usage > self.gas_input as usize - } -} - -// copy between mem objects -// message data -// program data -// mem data diff --git a/src/evm_logic/evm.rs b/src/evm_logic/evm.rs new file mode 100644 index 0000000..85a99fb --- /dev/null +++ b/src/evm_logic/evm.rs @@ -0,0 +1,149 @@ +mod decoder; + +use crate::gas_calculator::{call_data_gas_cost, GasRecorder}; +use crate::state::memory::Memory; +use crate::state::stack::Stack; +use crate::runtime::Runtime; + +use primitive_types::U256; + +#[derive(Clone)] +struct Transaction { + pub origin: U256, + pub gas_price: U256, +} + +struct Message { + pub caller: U256, + pub value: U256, + pub data: Memory, +} + +pub struct EVMContext { + stack: Stack, + memory: Memory, + program: Memory, + program_counter: usize, + contract_address: U256, + transaction: Transaction, + message: Message, + last_return_data: Memory, + result: Memory, + gas_input: u64, + gas_price: U256, + stopped: bool, + nested_index: usize, + gas_recorder: GasRecorder, +} + +impl EVMContext { + #[inline] + pub fn execute_transaction( + runtime: &mut impl Runtime, + contract: U256, + origin: U256, + gas: u64, + gas_price: U256, + value: U256, + data: Vec, + debug: bool, + ) -> usize { + let message = Message { + caller: contract, + value: value, + data: Memory::from(data, &mut GasRecorder { gas_usage: 0 }), + }; + let transaction = Transaction { + origin: origin, + gas_price: gas_price, + }; + let mut evm = EVMContext::create_sub_context( + contract, + message, + gas, + runtime.code(contract), + transaction, + gas_price, + 0, + ); + evm.gas_recorder.record_gas(21000); + evm.execute_program(runtime, debug); + evm.gas_recorder.gas_usage + } + + #[inline] + fn create_sub_context( + address: U256, + message: Message, + gas: u64, + code: Vec, + transaction: Transaction, + gas_price: U256, + nested_index: usize, + ) -> EVMContext { + EVMContext { + stack: Stack::new(), + memory: Memory::new(), + program: Memory::from(code, &mut GasRecorder { gas_usage: 0 }), + program_counter: 0, + contract_address: address, + // TODO remove need to clone here + transaction: transaction, + message: message, + last_return_data: Memory::new(), + result: Memory::new(), + gas_input: gas, + gas_price: gas_price, + stopped: false, + nested_index: nested_index, + gas_recorder: GasRecorder { gas_usage: 0 }, + } + } + + #[inline] + fn execute_program(&mut self, runtime: &mut impl Runtime, debug: bool) -> bool { + runtime.add_context(); + + let result = || -> bool { + self.gas_recorder + .record_gas(call_data_gas_cost(&self.message.data.bytes)); + if debug { + println!("Call Data Gas Cost: {}", self.gas_recorder.gas_usage); + } + while !self.stopped { + let result = self.execute_next_instruction(runtime, debug); + if !result { + return false; + } + } + if debug { + println!( + "Gas : {:x}", + self.gas_input - self.gas_recorder.gas_usage as u64 + ); + } + true + }(); + if result { + runtime.merge_context(); + } else { + runtime.revert_context(); + } + + result + } + + #[inline] + fn execute_next_instruction(&mut self, runtime: &mut impl Runtime, debug: bool) -> bool { + decoder::decode_instruction(self, runtime, debug) + } + #[inline] + fn check_gas_usage(&self) -> bool { + self.gas_recorder.gas_usage > self.gas_input as usize + } +} + +// copy between mem objects +// message data +// program data +// mem data diff --git a/src/evm_logic/evm/decoder.rs b/src/evm_logic/evm/decoder.rs new file mode 100644 index 0000000..6b04122 --- /dev/null +++ b/src/evm_logic/evm/decoder.rs @@ -0,0 +1,753 @@ +use crate::bytecode_spec::opcodes; +use crate::evm_logic::evm::{EVMContext, Message}; +use crate::runtime::Runtime; +use crate::state::memory::Memory; +use crate::util::{ + self, h256_to_u256, int256_to_uint256, keccak256, u256_to_h256, u256_to_uint256, + uint256_to_int256, MAX_UINT256, MAX_UINT256_COMPLEMENT, ZERO, +}; + +use num256::Uint256; +use primitive_types::U256; +use std::ops::Rem; + +#[inline] +pub fn decode_instruction(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> bool { + /* + Run the next instruction, adjusting gas usage and return a bool that is true if okay, false if exception + */ + + // Not a function as need to be able to return from caller function + macro_rules! pop { + ($($input:tt)*) => {{ + let result = evm.stack.pop(); + let result = match result { + Err(()) => { + return false; + } + + Ok(value) => value, + }; + result + }}; + } + + // TODO move into separate function instead of macro + macro_rules! make_call { + ($maintain_storage:tt) => {{ + let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size) = ( + pop!().as_u64(), + pop!(), + pop!(), + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + ); + let code: Vec = runtime.code(address); + if !value.eq(&U256::zero()) { + evm.gas_recorder.record_gas(2300); + } + let one_64th_value = + (evm.gas_input - evm.gas_recorder.gas_usage.clone() as u64) * 63 / 64; + if gas > one_64th_value { + gas = one_64th_value; + } + let address_access_cost = if runtime.is_hot(address) { + 100 + } else { + runtime.mark_hot(address); + 2600 + }; + // TODO check gas is okay + let mut sub_evm = EVMContext::create_sub_context( + if $maintain_storage { + evm.contract_address + } else { + address + }, + Message { + caller: evm.contract_address, + data: { + let mut memory: Memory = Memory::new(); + memory.copy_from( + &mut evm.memory, + args_offset, + 0, + args_size, + &mut evm.gas_recorder, + ); + memory + }, + value: value, + }, + gas, + code, + evm.transaction.clone(), + evm.gas_price, + evm.nested_index + 1, + ); + // TODO calculate cost of call data + + let response = sub_evm.execute_program(runtime, debug); + evm.last_return_data = sub_evm.result; + // let current_memory_cost = evm.memory.memory_cost; + evm.memory.copy_from( + &mut evm.last_return_data, + 0, + ret_offset, + ret_size, + &mut evm.gas_recorder, + ); + evm.stack.push(U256::from(response as u64)); + let code_execution_cost = sub_evm.gas_recorder.gas_usage; + let positive_value_cost = if !value.eq(&U256::zero()) { 6700 } else { 0 }; + let value_to_empty_account_cost = if !value.eq(&U256::zero()) + && runtime.nonce(address).eq(&U256::zero()) + && runtime.code_size(address).eq(&U256::zero()) + && runtime.balance(address).eq(&U256::zero()) + { + 25000 + } else { + 0 + }; + evm.gas_recorder.record_gas( + (code_execution_cost + + address_access_cost + + positive_value_cost + + value_to_empty_account_cost) as usize, + ); + response + }}; + } + + // Provides debug data around each branches block + macro_rules! debug_match { + ($opcode:expr, { $( $pat:pat => $block:block ),* }) => { + match $opcode { + $( + $pat => { + #[allow(unreachable_code)] + #[allow(unused_variables)]{ + { + if debug { + print!("{}", "\t".repeat(evm.nested_index as usize)); + println!( + "PC : {:<5} | Opcode: {:<15} | Gas: {:<10}", + evm.program_counter, + opcodes::OPCODE_MAP[&($opcode as u8)], + format!{"{:x}",evm.gas_input - evm.gas_recorder.clone().gas_usage as u64} + ); + } + $block + } + } + }),* + _ => {} + } + }; + } + + let opcode: u8 = evm.program[evm.program_counter]; + debug_match!(opcode, { + + opcodes::STOP => { + evm.stopped = true; + return true; + }, + + opcodes::ADD => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(a.overflowing_add(b).0); + evm.gas_recorder.record_gas(3); + }, + + opcodes::MUL => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(a.overflowing_mul(b).0); + evm.gas_recorder.record_gas(5); + }, + + opcodes::SUB => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(a.overflowing_sub(b).0); + evm.gas_recorder.record_gas(3); + }, + + opcodes::DIV => { + let (a, b) = (pop!(), pop!()); + match b { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + evm.stack.push(a.div_mod(b).0); + } + } + evm.gas_recorder.record_gas(5); + }, + + opcodes::SDIV => { + let (a, b) = (pop!(), pop!()); + match b { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + let a = u256_to_uint256(a); + let b = u256_to_uint256(b); + // Handle overflow case of -1 * MAX_POSITIVE + if a == *MAX_UINT256_COMPLEMENT && b == *MAX_UINT256 { + evm.stack.push(U256::from(MAX_UINT256_COMPLEMENT.to_be_bytes())); + } + else { + let a = uint256_to_int256(a); + let b = uint256_to_int256(b); + let result: Uint256 = int256_to_uint256(a / b); + let result = U256::from(result.to_be_bytes()); + evm.stack.push(result); + } + + } + } + evm.gas_recorder.record_gas(5); + }, + + opcodes::MOD => { + let (a, b) = (pop!(), pop!()); + match b { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + evm.stack.push(a.rem(b)); + } + } + evm.gas_recorder.record_gas(5); + }, + + opcodes::SMOD => { + let (a, b) = (pop!(), pop!()); + match b { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + let a = uint256_to_int256(u256_to_uint256(a)); + let b = uint256_to_int256(u256_to_uint256(b)); + let result: Uint256 = int256_to_uint256(a.rem(b)); + let result = U256::from(result.to_be_bytes()); + evm.stack.push(result); + } + } + evm.gas_recorder.record_gas(5); + + }, + + opcodes::ADDMOD => { + let (a, b, c) = (pop!(), pop!(), pop!()); + match c { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + evm.stack.push(a.checked_rem(c).unwrap().overflowing_add(b.checked_rem(c).unwrap()).0); + } + } + evm.gas_recorder.record_gas(8); + }, + + opcodes::MULMOD => { + let (a, b, c) = (pop!(), pop!(), pop!()); + match c { + ZERO => { + evm.stack.push(U256::zero()); + }, + _ => { + evm.stack.push(a.checked_rem(c).unwrap().overflowing_mul(b.checked_rem(c).unwrap()).0.checked_rem(c).unwrap()); + } + } + // println!("a: {}, b: {}, c: {}", a, b, c); + evm.gas_recorder.record_gas(8); + }, + + opcodes::EXP => { + let (a, exponent) = (pop!(), pop!()); + evm.stack.push(a.overflowing_pow(exponent).0); + evm.gas_recorder.record_gas(10 + 50 * (util::bytes_for_u256(&exponent) as usize)); + }, + + opcodes::SIGNEXTEND => { + let (x, y) = (pop!(), pop!()); + // X is the number of bytes of the input lower_mask + if x > U256::from(31) { + evm.stack.push(y); + } else { + let sign = y >> (x*8 + 7) & U256::from(1 as u64); + let lower_mask = if x == U256::from(31) { + U256::max_value() + } else { + (U256::from(1) << ((x+1)*8)) - 1 + }; + if sign == ZERO { + evm.stack.push(y & lower_mask); + } else { + let higher_mask = !lower_mask; + evm.stack.push(y | higher_mask); + } + } + evm.gas_recorder.record_gas(5); + }, + + opcodes::LT => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(U256::from((a < b) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::GT => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(U256::from((a > b) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::SLT => { + let (a, b) = (pop!(), pop!()); + let a = uint256_to_int256(u256_to_uint256(a)); + let b = uint256_to_int256(u256_to_uint256(b)); + evm.stack.push(U256::from((a < b) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::SGT => { + // debug!("SGT"); + let (a, b) = (pop!(), pop!()); + let a = uint256_to_int256(u256_to_uint256(a)); + let b = uint256_to_int256(u256_to_uint256(b)); + evm.stack.push(U256::from((a > b) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::EQ => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(U256::from((a == b) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::ISZERO => { + let data = pop!(); + evm.stack.push(U256::from(data.eq(&U256::zero()) as u64)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::AND => { + // debug!("AND"); + let (a, b) = (pop!(), pop!()); + evm.stack.push(a & b); + evm.gas_recorder.record_gas(3); + }, + + opcodes::OR => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(a | b); + evm.gas_recorder.record_gas(3); + }, + + opcodes::XOR => { + let (a, b) = (pop!(), pop!()); + evm.stack.push(a ^ b); + evm.gas_recorder.record_gas(3); + }, + + opcodes::NOT => { + let a = pop!(); + evm.stack.push(!a); + evm.gas_recorder.record_gas(3); + }, + + opcodes::BYTE => { + let (i, x) = (pop!(), pop!()); + if i > U256::from(31) { + evm.stack.push(U256::zero()); + } else { + evm.stack.push((x >> (U256::from(248) - i * 8)) & (0xFF as u64).into()); + } + evm.gas_recorder.record_gas(3); + }, + + opcodes::SHL => { + let (shift, value) = (pop!(), pop!()); + if shift > 31.into() { + evm.stack.push(U256::zero()); + } else { + evm.stack.push(value << shift); + } + evm.gas_recorder.record_gas(3); + }, + + opcodes::SHR => { + let (shift, value) = (pop!(), pop!()); + if shift > 31.into() { + evm.stack.push(U256::zero()); + } else { + evm.stack.push(value >> shift); + } + evm.gas_recorder.record_gas(3); + }, + + opcodes::SAR => { + // TODO + // let (shift, value) = (pop!(), pop!().as_i256()); + // evm.stack.push((value >> shift).as_u256()); + // evm.gas_usage += 3; + }, + + opcodes::KECCAK256 => { + let (offset, length) = (pop!().as_usize(), pop!().as_usize()); + let bytes = evm.memory.read_bytes(offset, length); + evm.stack.push(U256::from(keccak256(&bytes).as_bytes())); + // As of the Ethereum Yellow Paper (EIP-62), the gas cost for the KECCAK256 instruction is 30 gas plus an additional 6 gas for each 256-bit word (or part thereof) of input data. + evm.gas_recorder.record_gas(30 + (length.div_ceil(256) as u64 * 6) as usize); + }, + + opcodes::ADDRESS => { + evm.stack.push(evm.contract_address); + evm.gas_recorder.record_gas(2); + }, + + opcodes::BALANCE => { + let address = pop!(); + evm.stack.push(runtime.balance(address)); + if runtime.is_hot(address) { evm.gas_recorder.record_gas(100); } else { evm.gas_recorder.record_gas(2600); }; + runtime.mark_hot(address); + }, + + opcodes::ORIGIN => { + evm.stack.push(evm.transaction.origin); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CALLER => { + evm.stack.push(evm.message.caller); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CALLVALUE => { + evm.stack.push(evm.message.value); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CALLDATALOAD => { + // TODO fix + let index = pop!().as_u64() as usize; + evm.stack.push(evm.message.data.read(index)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::CALLDATASIZE => { + evm.stack.push(U256::from(evm.message.data.len() as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CALLDATACOPY => { + let (dest_offset, offset, length) = ( + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + ); + // let current_memory_usage = evm.memory.memory_cost; + evm.memory + .copy_from(&mut evm.message.data, offset, dest_offset, length, &mut evm.gas_recorder); + // let new_usage = evm.memory.memory_cost; + // evm.gas_usage += + // 3 + 3 * (length as u64 + 31 / 32) + (new_usage - current_memory_usage).as_u64(); + }, + + opcodes::CODESIZE => { + evm.stack.push(U256::from(evm.program.len() as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CODECOPY => { + let (dest_offset, offset, length) = ( + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + ); + + evm.memory + .copy_from(&mut evm.program, offset, dest_offset, length, &mut evm.gas_recorder); + }, + + opcodes::GASPRICE => { + evm.stack.push(evm.transaction.gas_price); + evm.gas_recorder.record_gas(2); + }, + + opcodes::EXTCODESIZE => { + let address = pop!(); + evm.stack.push(runtime.code_size(address)); + if runtime.is_hot(address) { evm.gas_recorder.record_gas(100); } else { evm.gas_recorder.record_gas(2600); }; + runtime.mark_hot(address); + }, + + opcodes::EXTCODECOPY => { + let (addr, dest_offset, offset, length) = ( + pop!(), + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + ); + + evm.memory.copy_from( + &mut Memory::from(runtime.code(addr), &mut evm.gas_recorder), + offset, + dest_offset, + length, + &mut evm.gas_recorder + ); + runtime.mark_hot(addr); + }, + + opcodes::RETURNDATASIZE => { + evm.stack + .push(U256::from(evm.last_return_data.len() as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::RETURNDATACOPY => { + let (dest_offset, offset, length) = ( + pop!().as_usize(), + pop!().as_usize(), + pop!().as_usize(), + ); + evm.memory + .copy_from(&mut evm.last_return_data, offset, dest_offset, length, &mut evm.gas_recorder); + }, + + opcodes::EXTCODEHASH => { + let addr = pop!(); + evm.stack.push(U256::from(util::keccak256_u256(addr).as_bytes())); + if runtime.is_hot(addr) { evm.gas_recorder.record_gas(100); } else { evm.gas_recorder.record_gas(2600); }; + runtime.mark_hot(addr); + }, + + opcodes::BLOCKHASH => { + let block_number = pop!(); + evm.stack.push(h256_to_u256(runtime.block_hash(block_number))); + evm.gas_recorder.record_gas(20); + }, + + opcodes::COINBASE => { + evm.stack.push(runtime.block_coinbase()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::TIMESTAMP => { + evm.stack.push(runtime.block_timestamp()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::NUMBER => { + evm.stack.push(runtime.block_number()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::DIFFICULTY => { + evm.stack.push(runtime.block_difficulty()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::GASLIMIT => { + evm.stack.push(runtime.block_gas_limit()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::CHAINID => { + evm.stack.push(runtime.chain_id()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::SELFBALANCE => { + evm.stack.push(runtime.balance(evm.contract_address)); + evm.gas_recorder.record_gas(5); + }, + + opcodes::BASEFEE => { + evm.stack.push(runtime.block_base_fee_per_gas()); + evm.gas_recorder.record_gas(2); + }, + + opcodes::POP => { + pop!(); + evm.gas_recorder.record_gas(2); + }, + + opcodes::MLOAD => { + let offset = pop!().as_usize(); + evm.stack.push(evm.memory.read(offset)); + evm.gas_recorder.record_gas(3); + }, + + opcodes::MSTORE => { + let (offset, value) = (pop!().as_usize(), pop!()); + evm.memory.write(offset, value, &mut evm.gas_recorder); + evm.gas_recorder.record_gas(3); + }, + + opcodes::MSTORE8 => { + let (offset, value) = (pop!().as_usize(), pop!()); + evm.memory.write_u8(offset, (value & U256::from(0xFF as u64)).low_u32() as u8, &mut evm.gas_recorder); + evm.gas_recorder.record_gas(3); + }, + + opcodes::SLOAD => { + let key = pop!(); + if runtime.is_hot_index(evm.contract_address, key) { + evm.gas_recorder.record_gas(100); + } else { + evm.gas_recorder.record_gas(2100); + runtime.mark_hot_index(evm.contract_address, key); + } + evm.stack + .push(h256_to_u256(runtime.storage(evm.contract_address)[&u256_to_h256(key)])); + // evm.gas_usage += if runtime.is_hot(evm.contract_address) { + // 100 + // } else { + // 2600 + // }; + // runtime.mark_hot(evm.contract_address); + }, + + opcodes::SSTORE => { + let (key, value) = (pop!(), pop!()); + if !runtime.is_hot_index(evm.contract_address, key){ + evm.gas_recorder.record_gas(2100); + runtime.mark_hot_index(evm.contract_address, key); + } + let base_dynamic_gas; + if !runtime.storage(evm.contract_address).contains_key(&u256_to_h256(key)) && value.eq(&U256::zero()) { + base_dynamic_gas = 100; + } + else { + base_dynamic_gas = if (runtime.storage(evm.contract_address).contains_key(&u256_to_h256(key)) + && !h256_to_u256(runtime.storage(evm.contract_address)[&u256_to_h256(key)]).eq(&U256::zero())) || value.eq(&U256::zero()) + { + 5000 + } else { + 20000 + }; + runtime.set_storage(evm.contract_address, key, u256_to_h256(value)); + } + // TODO already written slot should always be 100 + evm.gas_recorder.record_gas(base_dynamic_gas); + }, + + opcodes::JUMP => { + let destination = pop!().as_usize(); + evm.program_counter = destination; + evm.program_counter -= 1; + evm.gas_recorder.record_gas(8); + }, + + + opcodes::JUMPI => { + let (destination, condition) = (pop!().as_usize(), pop!()); + if !condition.eq(&U256::zero()) { + evm.program_counter = destination - 1; + } + evm.gas_recorder.record_gas(10); + }, + + opcodes::PC => { + evm.stack.push(U256::from(evm.program_counter as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::MSIZE => { + evm.stack.push(U256::from(evm.memory.max_index as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::GAS => { + evm.stack.push(U256::from(evm.gas_input - evm.gas_recorder.gas_usage as u64)); + evm.gas_recorder.record_gas(2); + }, + + opcodes::JUMPDEST => { + evm.gas_recorder.record_gas(1); + }, + + opcodes::PUSH_1..=opcodes::PUSH_32 => { + let push_number = opcode - opcodes::PUSH_1 + 1; + let bytes = evm + .program + .read_bytes(evm.program_counter + 1, push_number as usize); + evm.program_counter += push_number as usize; + evm.stack.push_bytes(&bytes); + evm.gas_recorder.record_gas(3); + }, + + opcodes::DUP_1..=opcodes::DUP_16 => { + let dup_number = opcode - opcodes::DUP_1 + 1; + let value = evm.stack.read_nth(dup_number as usize); + evm.stack.push(value); + evm.gas_recorder.record_gas(3); + }, + + opcodes::SWAP_1..=opcodes::SWAP_16 => { + let swap_number: usize = (opcode - opcodes::SWAP_1 + 1) as usize; + let bottom_value = evm.stack.read_nth(swap_number); + let top_value = pop!(); + evm.stack.write_nth(swap_number - 1, top_value); + evm.stack.push(bottom_value); + evm.gas_recorder.record_gas(3); + }, + + // TODO log + opcodes::LOG_0 => { + // TODO + }, + + opcodes::CREATE => { + // TODO + }, + + opcodes::CALL => { + make_call!(false); + }, + + opcodes::CALLCODE => { + make_call!(true); + }, + + opcodes::RETURN => { + let (offset, size) = (pop!().as_usize(), pop!().as_usize()); + evm.result.set_length(size); + evm.result.copy_from(&mut evm.memory, offset, 0, size, &mut evm.gas_recorder); + evm.stopped = true; + return true; + }, + + opcodes::DELEGATECALL => { + // TODO + // Same as call but storage, sender and value remain the same + evm.gas_recorder.record_gas(100); + }, + + opcodes::CREATE2 => { + // TODO + // Same as create but except the salt allows the new contract to be deployed at a consistent, deterministic address. + // Should deployment succeed, the account's code is set to the return data resulting from executing the initialisation code. + } + }); + + evm.program_counter += 1; + + if evm.check_gas_usage() { + // TODO add revert logic etc. + return false; + } + + true +} diff --git a/src/evm_logic/mod.rs b/src/evm_logic/mod.rs new file mode 100644 index 0000000..c469d0c --- /dev/null +++ b/src/evm_logic/mod.rs @@ -0,0 +1 @@ +pub mod evm; diff --git a/src/lib.rs b/src/lib.rs index d3e3514..d03a5e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod assembler; pub mod bytecode_spec; -pub mod evm; +pub mod evm_logic; pub mod runtime; pub mod state; pub mod util; diff --git a/test_gen/src/lib.rs b/test_gen/src/lib.rs index 8573026..12377ad 100644 --- a/test_gen/src/lib.rs +++ b/test_gen/src/lib.rs @@ -161,7 +161,7 @@ pub fn generate_official_tests_from_folder(input: TokenStream) -> TokenStream { #[test] fn #test_name() { let filename = #file_name; - run_test_file(filename.to_string(), false, #i); + run_test_file(filename.to_string(), true, #i); } }); } diff --git a/tests/mocks/mock_runtime.rs b/tests/mocks/mock_runtime.rs index d69a5e0..ae2b017 100644 --- a/tests/mocks/mock_runtime.rs +++ b/tests/mocks/mock_runtime.rs @@ -4,6 +4,7 @@ use primitive_types::{H160, H256, U256}; use rlp::{Encodable, RlpStream}; use std::collections::BTreeMap; use std::collections::HashSet; +use std::mem; #[derive(Clone)] pub struct Contract { @@ -19,6 +20,10 @@ pub struct Contract { pub hot_keys: HashSet, } +pub struct Context { + pub prev_context: Option>, + pub contracts: BTreeMap, +} pub struct MockRuntime { pub block_hashes: BTreeMap, pub block_number: U256, @@ -30,7 +35,7 @@ pub struct MockRuntime { pub block_base_fee_per_gas: U256, pub chain_id: U256, pub contracts: BTreeMap, - pub contexts: Vec>, + pub current_context: Option>, } #[derive(Debug)] @@ -134,121 +139,203 @@ impl Runtime for MockRuntime { // TODO add default values if address is not found // Context state fn balance(&self, address: U256) -> U256 { - self.last_context()[&address].balance + self.current_context.as_ref().unwrap().contracts[&address].balance } fn code_size(&self, address: U256) -> U256 { - self.last_context()[&address].code_size + self.current_context.as_ref().unwrap().contracts[&address].code_size } fn code_hash(&self, address: U256) -> H256 { - self.last_context()[&address].code_hash + self.current_context.as_ref().unwrap().contracts[&address].code_hash } fn nonce(&self, address: U256) -> U256 { - self.last_context()[&address].nonce + self.current_context.as_ref().unwrap().contracts[&address].nonce } fn code(&self, address: U256) -> Vec { - self.last_context()[&address].code.clone() + self.current_context.as_ref().unwrap().contracts[&address] + .code + .clone() } fn exists(&self, address: U256) -> bool { - self.last_context().contains_key(&address) + self.current_context + .as_ref() + .unwrap() + .contracts + .contains_key(&address) } fn storage(&mut self, address: U256) -> &BTreeMap { - &mut self.last_context().get_mut(&address).unwrap().storage - } - fn original_storage(&mut self, address: U256) -> &BTreeMap { &mut self - .penultimate_context() + .current_context + .as_mut() + .unwrap() + .contracts .get_mut(&address) .unwrap() .storage } + fn original_storage(&mut self, address: U256) -> &BTreeMap { + &self.contracts[&address].storage + } // TODO add logic if address is not found // Modify Contract State (Should always be valid addresses) fn is_deleted(&self, address: U256) -> bool { - self.last_context()[&address].is_deleted + self.current_context.as_ref().unwrap().contracts[&address].is_deleted } fn is_cold(&self, address: U256) -> bool { - self.last_context()[&address].is_cold + self.current_context.as_ref().unwrap().contracts[&address].is_cold } fn is_cold_index(&self, address: U256, index: U256) -> bool { - !self.last_context()[&address].hot_keys.contains(&index) + !self.current_context.as_ref().unwrap().contracts[&address] + .hot_keys + .contains(&index) } fn mark_hot(&mut self, address: U256) { - self.last_context().get_mut(&address).unwrap().is_cold = false; + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .is_cold = false; } fn mark_hot_index(&mut self, address: U256, index: U256) { - self.last_context() + self.current_context + .as_mut() + .unwrap() + .contracts .get_mut(&address) .unwrap() .hot_keys .insert(index); } fn set_storage(&mut self, address: U256, index: U256, value: H256) { - self.last_context() + self.current_context + .as_mut() + .unwrap() + .contracts .get_mut(&address) .unwrap() .storage .insert(u256_to_h256(index), value); + for (address, contract) in &self.current_context.as_ref().unwrap().contracts { + println!("Storage: {:?}", contract.storage); + } } fn mark_delete(&mut self, address: U256) { - self.last_context().get_mut(&address).unwrap().is_deleted = true; + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .is_deleted = true; } fn reset_storage(&mut self, address: U256) { - self.last_context().get_mut(&address).unwrap().storage = BTreeMap::new(); + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .storage = BTreeMap::new(); } fn set_code(&mut self, address: U256, code: Vec) { - self.last_context().get_mut(&address).unwrap().code = code; + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .code = code; } fn reset_balance(&mut self, address: U256) { - self.last_context().get_mut(&address).unwrap().balance = U256::from(0 as u64); + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .balance = U256::from(0 as u64); } fn deposit(&mut self, target: U256, value: U256) { - self.last_context().get_mut(&target).unwrap().balance += value; + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&target) + .unwrap() + .balance += value; } fn withdrawal(&mut self, source: U256, value: U256) { - self.last_context().get_mut(&source).unwrap().balance -= value; + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&source) + .unwrap() + .balance -= value; } fn increase_nonce(&mut self, address: U256) { - self.last_context().get_mut(&address).unwrap().nonce += U256::from(1); + self.current_context + .as_mut() + .unwrap() + .contracts + .get_mut(&address) + .unwrap() + .nonce += U256::from(1); } // Modify context stack fn add_context(&mut self) { - match self.contexts.last() { + println!("Adding Context"); + match mem::take(&mut self.current_context) { Some(context) => { - self.contexts.push(context.clone()); + self.current_context = Some(Box::new(Context { + contracts: context.contracts.clone(), + prev_context: Some(context) + })); } None => { - self.contexts.push(self.contracts.clone()); + self.current_context = Some(Box::new(Context { + contracts: self.contracts.clone(), + prev_context: None, + })); } - } + }; } fn merge_context(&mut self) { - // TODO + println!("Merging Context"); + + match mem::take(&mut self.current_context) { + Some(mut context) => { + for (address, contract) in &context.contracts { + println!("Storage: {:?}", contract.storage); + } + match &mut context.prev_context { + Some(prev_context) => { + prev_context.contracts = context.contracts; + } + None => { + self.contracts = context.contracts; + } + } + self.current_context = context.prev_context; + } + None => { + self.current_context = None; + } + } } fn revert_context(&mut self) { - // TODO need to know whether to maintain hot cold of contracts? - match self.contexts.last() { - Some(_) => { - self.contexts.pop(); + match mem::take(&mut self.current_context) { + Some(context) => { + self.current_context = context.prev_context; + } + None => { + self.current_context = None; } - None => {} } } } -impl MockRuntime { - fn last_context(&mut self) -> &BTreeMap { - match self.contexts.last() { - Some(context) => context, - None => &self.contracts, - } - } - - fn penultimate_context(&mut self) -> &BTreeMap { - match self.contexts.get(self.contexts.len() - 2) { - Some(context) => context, - None => &self.contracts, - } - } -} +impl MockRuntime {} diff --git a/tests/official_tests/official_tests.rs b/tests/official_tests/official_tests.rs index ccba385..2adf011 100644 --- a/tests/official_tests/official_tests.rs +++ b/tests/official_tests/official_tests.rs @@ -1,5 +1,5 @@ use ethereum_evm::{ - evm::EVMContext, + evm_logic::evm::EVMContext, runtime::Runtime, util::{keccak256, u256_to_h256}, }; @@ -11,13 +11,14 @@ use std::{ }; use test_gen::{generate_official_tests_from_file, generate_official_tests_from_folder}; -use crate::mocks::mock_runtime::{Contract, MockRuntime}; +use crate::mocks::mock_runtime::{Context, Contract, MockRuntime}; use super::types::{TestState, TestStateMulti}; pub fn run_test_file(filename: String, debug: bool, index: usize) { let tests: BTreeMap = serde_json::from_reader(BufReader::new(File::open(filename).unwrap())).unwrap(); + println!("Debug: {:?}", debug); run_test( &tests .into_iter() @@ -80,8 +81,9 @@ pub fn run_test(test: &TestState, debug: bool) { ); contracts }, - contexts: vec![], + current_context: None, }; + runtime.add_context(); // Execute the transaction let gas_usage = EVMContext::execute_transaction( @@ -97,35 +99,37 @@ pub fn run_test(test: &TestState, debug: bool) { // Calculate the gas usage let eth_usage = (gas_usage) * test.transaction.gas_price.unwrap_or_default().as_usize(); + println!("Debug: {:?}", debug); if debug { println!("Gas Usage: {}", gas_usage); println!("Eth Usage: {}", eth_usage); println!("Value: {}", test.transaction.value); } // send value the wallet - runtime - .contracts - .get_mut(&test.transaction.sender) - .unwrap() - .nonce += U256::from(1); + runtime.increase_nonce(test.transaction.sender); runtime.deposit(test.transaction.to, test.transaction.value); // withdraw the value from the sender runtime.withdrawal(test.transaction.sender, test.transaction.value); // withdraw the gas usage from the sender runtime.withdrawal(test.transaction.sender, U256::from(eth_usage as u64)); runtime.deposit(test.env.current_coinbase, U256::from(eth_usage as u64)); + runtime.merge_context(); + println!("Context {:?}", match runtime.current_context { + Some(_) => "Exists", + _ => "Doesn't Exist", + }); // Debug the balances assert_eq!(runtime.state_root_hash(), test.post.hash); } -generate_official_tests_from_folder!( - "./tests/official_tests/tests/GeneralStateTests/VMTests/vmArithmeticTest" -); +// generate_official_tests_from_folder!( +// "./tests/official_tests/tests/GeneralStateTests/VMTests/vmArithmeticTest" +// ); // generate_official_tests_from_folder!( // "./tests/official_tests/tests/GeneralStateTests/stRandom" // ); -// generate_official_tests_from_file!( -// "./tests/official_tests/tests/GeneralStateTests/VMTests/vmArithmeticTest/mul.json" -// ); +generate_official_tests_from_file!( + "./tests/official_tests/tests/GeneralStateTests/VMTests/vmArithmeticTest/mul.json" +); diff --git a/util/Cargo.toml b/util/Cargo.toml new file mode 100644 index 0000000..5644a8e --- /dev/null +++ b/util/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "util" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "1.0.79" +quote = "1.0.35" +serde = { version = "1.0", features = ["derive"]} +serde_json = "1.0.114" +syn = { version = "2.0.52", features = ["full"] } + +[lib] +proc-macro = true diff --git a/util/src/lib.rs b/util/src/lib.rs new file mode 100644 index 0000000..1e70188 --- /dev/null +++ b/util/src/lib.rs @@ -0,0 +1,58 @@ +extern crate proc_macro; + +use proc_macro::{Span, TokenStream}; +use quote::quote; +use syn::{parse_macro_input, Item, ItemStatic}; +#[proc_macro] +pub fn opcode_map(input: TokenStream) -> TokenStream { + // Parse the input tokens into a Rust syntax tree + let input = parse_macro_input!(input as syn::File); + + // Extract constant definitions from the input + let mut opcode_names = Vec::new(); + let mut opcode_values = Vec::new(); + + for item in input.items.clone() { + if let Item::Const(const_item) = item { + let byte = match *const_item.expr { + syn::Expr::Lit(lit) => { + match lit.lit { + syn::Lit::Int(byte) => {println!("{:?}", byte.to_string()); + byte} + _ => { + panic!("Expected byte literal"); + } + } + } + _ => { + panic!("Expected integer literal"); + } + }; + opcode_names.push(const_item.ident.to_string()); + opcode_values.push(byte); + } + } + + // Generate the PHF map + let map_tokens = quote! { + phf::phf_map! { + #( + #opcode_values => #opcode_names, + )* + } + }; + + // Generate the final output + let expanded = quote! { + #input + + lazy_static! { + pub static ref OPCODE_MAP: phf::Map = #map_tokens; + } + }; + + println!("{:?}", expanded.to_string()); + + // Convert the generated tokens back into a TokenStream + TokenStream::from(expanded) +}