diff --git a/.vscode/settings.json b/.vscode/settings.json index 48f49ca..dd6e594 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "hasher", "keccak", "llcversion" ] diff --git a/TODO.md b/TODO.md index 3d00a27..3454883 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,6 @@ # TODO +- [_] Fix calling costs and split up function - [_] Fix Calldata load, Code load etc. - [_] Do general clean up - [_] Better error handling diff --git a/src/configs/gas_costs.rs b/src/configs/gas_costs.rs index fd54786..ba20c75 100644 --- a/src/configs/gas_costs.rs +++ b/src/configs/gas_costs.rs @@ -54,27 +54,9 @@ pub enum DynamicCosts { }, Call { value: U256, - gas: U256, - target_is_cold: bool, - target_exists: bool, - }, - CallCode { - /// Call value. - value: U256, - /// Call gas. - gas: U256, - /// True if target has not been previously accessed in this transaction target_is_cold: bool, - /// Whether the target exists. - target_exists: bool, - }, - DelegateCall { - /// Call gas. - gas: U256, - /// True if target has not been previously accessed in this transaction - target_is_cold: bool, - /// Whether the target exists. - target_exists: bool, + empty_account: bool, + is_delegate: bool, }, StaticCall { /// Call gas. @@ -102,9 +84,9 @@ pub enum DynamicCosts { /// Gas cost for `LOG`. Log { /// Topic length. - n: u8, + topic_length: u8, /// Data length. - len: U256, + size: usize, }, /// Gas cost for `EXTCODECOPY`. Exp { @@ -115,6 +97,9 @@ pub enum DynamicCosts { /// Length. len: U256, }, + Create { + deployed_code_size: usize, + }, /// Gas cost for `SLOAD`. SLoad { /// True if target has not been previously accessed in this transaction @@ -156,38 +141,23 @@ impl DynamicCosts { } DynamicCosts::Call { value, - gas, - target_is_cold, - target_exists, - } => { - if *value != ZERO { - static_costs::G_CALL_VALUE - } else { - static_costs::G_CALL_STIPEND - } - } - DynamicCosts::CallCode { - value, - gas, + empty_account, target_is_cold, - target_exists, + is_delegate, } => { - if *value != ZERO { - static_costs::G_CALL_VALUE + + 0 + if *target_is_cold { + static_costs::G_COLD_ACCOUNT_ACCESS } else { - static_costs::G_CALL_STIPEND - } + static_costs::G_WARM_ACCESS + } + + if !value.eq(&ZERO) & !is_delegate { + static_costs::G_CALL_VALUE - static_costs::G_CALL_STIPEND + } else {0} + + if *empty_account { + static_costs::G_NEW_ACCOUNT + } else {0} } - DynamicCosts::DelegateCall { - gas, - target_is_cold, - target_exists, - } => static_costs::G_CALL_STIPEND, - DynamicCosts::StaticCall { - gas, - target_is_cold, - target_exists, - } => static_costs::G_CALL_STIPEND, DynamicCosts::SStore { original, current, @@ -203,10 +173,10 @@ impl DynamicCosts { DynamicCosts::Keccak256 { len } => { static_costs::G_KECCAK256 + (len.div_ceil(32)) * static_costs::G_KECCAK256_WORD } - DynamicCosts::Log { n, len } => { + DynamicCosts::Log { topic_length, size } => { static_costs::G_LOG - + static_costs::G_LOG_TOPIC * (*n as u64) - + static_costs::G_LOG_DATA * (len.as_u64() / 32) + + static_costs::G_LOG_TOPIC * (*topic_length as u64) + + static_costs::G_LOG_DATA * (*size as u64) } DynamicCosts::Exp { power } => { @@ -229,6 +199,9 @@ impl DynamicCosts { static_costs::G_WARM_ACCESS } } + DynamicCosts::Create {deployed_code_size} => { + static_costs::G_CREATE + static_costs::G_KECCAK256_WORD * (*deployed_code_size as u64).div_ceil(32) + } _ => 0, } } diff --git a/src/evm_logic/evm.rs b/src/evm_logic/evm.rs index d7bf4b3..ee6fe1b 100644 --- a/src/evm_logic/evm.rs +++ b/src/evm_logic/evm.rs @@ -1,6 +1,7 @@ mod call; mod decoder; mod macros; +mod create; use crate::evm_logic::evm::macros::{break_if_error, return_if_error}; use crate::evm_logic::gas_calculator::{call_data_gas_cost, GasRecorder}; @@ -38,6 +39,7 @@ pub struct EVMContext { stopped: bool, nested_index: usize, gas_recorder: GasRecorder, + is_static: bool } impl EVMContext { @@ -69,8 +71,16 @@ impl EVMContext { transaction, gas_price, 0, + false, ); evm.gas_recorder.record_gas(21000); + if evm.message.data.len() != 0 { + evm.gas_recorder + .record_gas(call_data_gas_cost(&evm.message.data)); + } + if debug { + println!("Call Data Gas Cost: {:x}", evm.gas_recorder.gas_usage); + } let result = evm.execute_program(runtime, debug); // TODO move this into gas_recorder let gas_usage = evm.gas_recorder.gas_usage @@ -87,6 +97,7 @@ impl EVMContext { transaction: Transaction, gas_price: U256, nested_index: usize, + is_static: bool, ) -> EVMContext { EVMContext { stack: Stack::new(), @@ -113,6 +124,7 @@ impl EVMContext { gas_usage: 0, gas_refunds: 0, }, + is_static: is_static } } @@ -122,12 +134,8 @@ impl EVMContext { let result = { let mut result = ExecutionResult::Success; - if self.message.data.len() != 0 { - self.gas_recorder - .record_gas(call_data_gas_cost(&self.message.data)); - } - if debug { - println!("Call Data Gas Cost: {:x}", self.gas_recorder.gas_usage); + if self.program.len() == 0 { + self.stopped = true; } while !self.stopped { result = self.execute_next_instruction(runtime, debug); diff --git a/src/evm_logic/evm/call.rs b/src/evm_logic/evm/call.rs index 6610554..5f81568 100644 --- a/src/evm_logic/evm/call.rs +++ b/src/evm_logic/evm/call.rs @@ -4,84 +4,344 @@ Should be converted to function once proper error handling is introduced */ use primitive_types::U256; +use serde::de::value; -use crate::result::{ExecutionResult, Error}; -use crate::runtime::Runtime; use super::{macros::pop, EVMContext, Message}; +use crate::configs::gas_costs::{static_costs, DynamicCosts}; +use crate::evm_logic::util::ZERO; +use crate::result::{Error, ExecutionResult}; +use crate::runtime::Runtime; #[inline] -pub fn make_call(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool, maintain_storage: bool) -> ExecutionResult { - let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size) = ( - pop!(evm).as_u64(), - pop!(evm), - pop!(evm), - pop!(evm).as_usize(), - pop!(evm).as_usize(), - pop!(evm).as_usize(), - pop!(evm).as_usize(), - ); - let code: Vec = runtime.code(address); - if !value.eq(&U256::zero()) { - evm.gas_recorder.record_gas(2300); +pub fn call(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> ExecutionResult { + let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size) = ( + pop!(evm).as_u64(), + pop!(evm), + pop!(evm), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + ); + if value.eq(&ZERO) { + gas += static_costs::G_CALL_STIPEND; + } + let mut call_args = CallArgs { + gas: gas, + code_address: address, + contract_address: address, + caller_address: evm.contract_address, + value: value, + args_offset: args_offset, + args_size: args_size, + ret_offset: ret_offset, + ret_size: ret_size, + }; + make_call(evm, runtime, debug, call_args, false); + evm.gas_recorder.record_gas( + DynamicCosts::Call { + value: value, + target_is_cold: runtime.is_cold(address), + empty_account: !value.eq(&U256::zero()) + && runtime.nonce(address).eq(&U256::zero()) + && runtime.code_size(address).eq(&U256::zero()) + && runtime.balance(address).eq(&U256::zero()), + is_delegate: false, } - 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; + .cost(), + ); + ExecutionResult::Success +} + +#[inline] +pub fn call_code(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> ExecutionResult { + let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size) = ( + pop!(evm).as_u64(), + pop!(evm), + pop!(evm), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + ); + if value.eq(&ZERO) { + gas += static_costs::G_CALL_STIPEND; + } + let call_args = CallArgs { + gas: gas, + code_address: address, + contract_address: evm.contract_address, + caller_address: evm.contract_address, + value: value, + args_offset: args_offset, + args_size: args_size, + ret_offset: ret_offset, + ret_size: ret_size, + }; + make_call(evm, runtime, debug, call_args, false); + evm.gas_recorder.record_gas( + DynamicCosts::Call { + value: value, + target_is_cold: runtime.is_cold(address), + empty_account: !value.eq(&U256::zero()) + && runtime.nonce(address).eq(&U256::zero()) + && runtime.code_size(address).eq(&U256::zero()) + && runtime.balance(address).eq(&U256::zero()), + is_delegate: false, } - 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: evm.memory.bytes[args_offset..args_offset + args_size].to_vec(), - value: value, - }, - gas, - code, - evm.transaction.clone(), - evm.gas_price, - evm.nested_index + 1, - ); - // TODO calculate cost of call data + .cost(), + ); + ExecutionResult::Success +} - let execution_result = 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(match execution_result { ExecutionResult::Success => {true} _ => {false}} 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 u64, - ); - execution_result +#[inline] +pub fn delegate_call( + evm: &mut EVMContext, + runtime: &mut impl Runtime, + debug: bool, +) -> ExecutionResult { + let (gas, address, args_offset, args_size, ret_offset, ret_size) = ( + pop!(evm).as_u64(), + pop!(evm), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + ); + let call_args = CallArgs { + gas: gas, + code_address: address, + contract_address: evm.contract_address, + caller_address: evm.message.caller, + value: evm.message.value, + args_offset: args_offset, + args_size: args_size, + ret_offset: ret_offset, + ret_size: ret_size, + }; + make_call(evm, runtime, debug, call_args, false); + evm.gas_recorder.record_gas( + DynamicCosts::Call { + value: evm.message.value, + target_is_cold: runtime.is_cold(address), + empty_account: !evm.message.value.eq(&U256::zero()) + && runtime.nonce(address).eq(&U256::zero()) + && runtime.code_size(address).eq(&U256::zero()) + && runtime.balance(address).eq(&U256::zero()), + is_delegate: true, + } + .cost(), + ); + ExecutionResult::Success } + + +#[inline] +pub fn static_call(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> ExecutionResult { + let (mut gas, address, args_offset, args_size, ret_offset, ret_size) = ( + pop!(evm).as_u64(), + pop!(evm), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + ); + let mut call_args = CallArgs { + gas: gas, + code_address: address, + contract_address: address, + caller_address: evm.contract_address, + value: ZERO, + args_offset: args_offset, + args_size: args_size, + ret_offset: ret_offset, + ret_size: ret_size, + }; + make_call(evm, runtime, debug, call_args, true); + evm.gas_recorder.record_gas( + DynamicCosts::Call { + value: ZERO, + target_is_cold: runtime.is_cold(address), + empty_account: false, + is_delegate: false, + } + .cost(), + ); + runtime.mark_hot(address); + ExecutionResult::Success +} + + +pub struct CallArgs { + pub gas: u64, + pub code_address: U256, + pub contract_address: U256, + pub caller_address: U256, + pub value: U256, + pub args_offset: usize, + pub args_size: usize, + pub ret_offset: usize, + pub ret_size: usize, +} + +#[inline] +pub fn make_call( + evm: &mut EVMContext, + runtime: &mut impl Runtime, + debug: bool, + args: CallArgs, + is_static: bool +) { + let code: Vec = runtime.code(args.code_address); + let gas = args + .gas + .min((evm.gas_input - evm.gas_recorder.gas_usage.clone() as u64) * 63 / 64); + let mut sub_evm = EVMContext::create_sub_context( + args.contract_address, + Message { + caller: args.caller_address, + data: evm.memory.bytes[args.args_offset..args.args_offset + args.args_size].to_vec(), + value: args.value, + }, + gas, + code, + evm.transaction.clone(), + evm.gas_price, + evm.nested_index + 1, + is_static + ); + let execution_result = sub_evm.execute_program(runtime, debug); + evm.last_return_data = sub_evm.result; + evm.memory.copy_from( + &mut evm.last_return_data, + 0, + args.ret_offset, + args.ret_size, + &mut evm.gas_recorder, + ); + evm.gas_recorder + .record_gas((sub_evm.gas_recorder.gas_usage - sub_evm.gas_recorder.gas_refunds) as u64); + evm.stack.push(U256::from(match execution_result { + ExecutionResult::Success => true, + _ => false, + } as u64)); +} + +// #[inline] +// pub fn _( +// evm: &mut EVMContext, +// runtime: &mut impl Runtime, +// debug: bool, +// maintain_storage: bool, +// maintain_caller: bool, +// ) -> ExecutionResult { +// let (mut gas, address, value, args_offset, args_size, ret_offset, ret_size); +// println!("Calling"); +// if maintain_caller { +// (gas, address, args_offset, args_size, ret_offset, ret_size) = ( +// pop!(evm).as_u64(), +// pop!(evm), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// ); +// value = evm.message.value; +// } else { +// ( +// gas, +// address, +// value, +// args_offset, +// args_size, +// ret_offset, +// ret_size, +// ) = ( +// pop!(evm).as_u64(), +// pop!(evm), +// pop!(evm), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// pop!(evm).as_usize(), +// ); +// } +// println!("Calling"); +// let code: Vec = runtime.code(address); +// if !value.eq(&U256::zero()) & !maintain_caller { +// 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;maintain_caller +// } +// let address_access_cost = if runtime.is_hot(address) { +// 100 +// } else { +// runtime.mark_hot(address); +// 2600 +// }; +// println!("Gas: {:x}", gas); +// println!("args_size: {:x}", args_size); +// // TODO check gas is okay +// let mut sub_evm = EVMContext::create_sub_context( +// if maintain_storage { +// evm.contract_address +// } else { +// address +// }, +// Message { +// caller: if maintain_caller { +// evm.message.caller +// } else { +// evm.contract_address +// }, +// data: evm.memory.bytes[args_offset..args_offset + args_size].to_vec(), +// value: value, +// }, +// gas, +// code, +// evm.transaction.clone(), +// evm.gas_price, +// evm.nested_index + 1, +// ); +// // TODO calculate cost of call data + +// let execution_result = 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(match execution_result { +// ExecutionResult::Success => true, +// _ => false, +// } as u64)); +// let code_execution_cost = sub_evm.gas_recorder.gas_usage; +// let positive_value_cost = if !value.eq(&U256::zero()) & !maintain_caller { +// 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 u64, +// ); +// println!("execution_result: {:?}", execution_result); +// execution_result +// } diff --git a/src/evm_logic/evm/create.rs b/src/evm_logic/evm/create.rs new file mode 100644 index 0000000..030ba4c --- /dev/null +++ b/src/evm_logic/evm/create.rs @@ -0,0 +1,111 @@ +use primitive_types::{H160, H256, U256}; +use sha3::{Digest, Keccak256}; + +use crate::{ + configs::gas_costs::DynamicCosts, + evm_logic::{evm::call::CallArgs, util::{h256_to_u256, keccak256, u256_to_array, u256_to_h256, ZERO}}, + result::{Error, ExecutionResult}, + runtime::{self, Runtime}, +}; + +use super::{ + call::{self, make_call}, + macros::{pop, return_if_error}, + EVMContext, +}; + +pub fn create( + evm: &mut EVMContext, + address: U256, + runtime: &mut impl Runtime, + debug: bool, + value: U256, + offset: usize, + size: usize, +) -> ExecutionResult { + let code = evm.memory.read_bytes(offset, size, &mut evm.gas_recorder); + runtime.create_contract(address, code); + println!("Created: {:?}", address); + // evm.stack.push(ZERO); + // evm.stack.push(ZERO); + // evm.stack.push(ZERO); + // evm.stack.push(value); + // evm.stack.push(ZERO); + // evm.stack.push(address); + // evm.stack.push(U256::from( + // evm.gas_input - evm.gas_recorder.gas_usage as u64, + // )); + make_call(evm, runtime, debug, CallArgs{ + gas: evm.gas_input - evm.gas_recorder.gas_usage as u64, + contract_address: address, + code_address: address, + caller_address: evm.message.caller, + value: ZERO, + args_offset: 0, + args_size: 0, + ret_offset: 0, + ret_size: 0, + }, false); + // return_if_error!(make_call(evm, runtime, debug, false, true)); + // Undo the call gas cost + // evm.gas_recorder.gas_usage -= 100; + println!("Created: {:?}", address); + let deployed_code_size = runtime.code_size(address).as_usize(); + println!("Deployed code size: {}", deployed_code_size); + println!( + "Gas {:x}", + DynamicCosts::Create { + deployed_code_size: deployed_code_size + } + .cost() + ); + evm.gas_recorder.record_gas( + DynamicCosts::Create { + deployed_code_size: deployed_code_size, + } + .cost(), + ); + runtime.increase_nonce(evm.message.caller); + runtime.increase_nonce(address); + runtime.set_contract_code(address, evm.last_return_data.bytes.clone()); + ExecutionResult::Success +} + +pub fn create_1(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> ExecutionResult { + let (value, offset, size) = (pop!(evm), pop!(evm).as_usize(), pop!(evm).as_usize()); + let sender_address = evm.message.caller; + let sender_nonce = runtime.nonce(sender_address); + let mut encodable = u256_to_array(sender_address).to_vec(); + encodable.append(&mut u256_to_array(sender_nonce).to_vec()); + let address: U256 = h256_to_u256(keccak256(&rlp::encode(&encodable).to_vec())); + let mut address_mod = Vec::from([0u8; 12]); + address_mod.append(&mut u256_to_array(address).as_slice()[12..].to_vec()); + let address = U256::from_big_endian(address_mod.as_slice()); + create(evm, address, runtime, debug, value, offset, size) +} + +pub fn create_2(evm: &mut EVMContext, runtime: &mut impl Runtime, debug: bool) -> ExecutionResult { + let (value, offset, size, salt) = ( + pop!(evm), + pop!(evm).as_usize(), + pop!(evm).as_usize(), + pop!(evm), + ); + let code = evm.memory.read_bytes(offset, size, &mut evm.gas_recorder); + let code_hash = keccak256(&code); + println!("Code Hash: {:x}", code_hash); + let address: H160 = { + let mut hasher = Keccak256::new(); + hasher.update([0xff]); + hasher.update(&H160::from(u256_to_h256(evm.message.caller))[..]); + hasher.update(&u256_to_h256(salt)[..]); + hasher.update(&code_hash[..]); + H256::from_slice(hasher.finalize().as_slice()).into() + }; + println!( + "Value: {}, Offset: {}, Size: {}, Salt: {}", + value, offset, size, salt + ); + println!("Address: {:x}", address); + create(evm, h256_to_u256(H256::from(address)), runtime, debug, value, offset, size) +} diff --git a/src/evm_logic/evm/decoder.rs b/src/evm_logic/evm/decoder.rs index be7cf62..76467bd 100644 --- a/src/evm_logic/evm/decoder.rs +++ b/src/evm_logic/evm/decoder.rs @@ -1,7 +1,8 @@ use crate::configs::bytecode_spec::opcodes; use crate::configs::gas_costs::{static_costs, DynamicCosts}; -use crate::evm_logic::evm::call::make_call; -use crate::evm_logic::evm::macros::{debug_match, pop, return_if_error}; +use crate::evm_logic::evm::call::{call, call_code, delegate_call, make_call, static_call}; +use crate::evm_logic::evm::create::{create, create_1, create_2}; +use crate::evm_logic::evm::macros::{debug_match, pop, return_error_if_static, return_if_error}; use crate::evm_logic::evm::EVMContext; use crate::evm_logic::util::{ self, h256_to_u256, int256_to_uint256, keccak256, u256_to_h256, u256_to_uint256, @@ -24,7 +25,6 @@ pub fn decode_instruction(evm: &mut EVMContext, runtime: &mut impl Runtime, debu // Not a function as need to be able to return from caller function // Provides debug data around each branches block - let opcode: u8 = evm.program[evm.program_counter]; debug_match!(evm, debug, opcode, { @@ -506,6 +506,7 @@ pub fn decode_instruction(evm: &mut EVMContext, runtime: &mut impl Runtime, debu }, opcodes::SSTORE => { + return_error_if_static!(evm); let (key, value) = (pop!(evm), pop!(evm)); if !runtime.is_hot_index(evm.contract_address, key){ evm.gas_recorder.record_gas(2100); @@ -595,20 +596,31 @@ pub fn decode_instruction(evm: &mut EVMContext, runtime: &mut impl Runtime, debu }, // TODO log - opcodes::LOG_0 => { - // TODO + opcodes::LOG_0..=opcodes::LOG_4 => { + return_error_if_static!(evm); + // TODO implement properly + let (offset, size) = (pop!(evm).as_usize(), pop!(evm).as_usize()); + let mut topics: Vec = Vec::new(); + for num_topic in 0..opcode - opcodes::LOG_0 { + topics.push(pop!(evm)); + } + let mut log_mem = Memory::new(); + log_mem.copy_from_with_local_cost(&mut evm.memory, offset, 0, size, &mut evm.gas_recorder); + evm.gas_recorder.record_gas(DynamicCosts::Log { topic_length: topics.len() as u8, size: size }.cost()) }, opcodes::CREATE => { - // TODO + return_error_if_static!(evm); + // return_if_error!(create(evm, runtime, debug)); }, opcodes::CALL => { - return_if_error!(make_call(evm, runtime, debug, false)); + return_error_if_static!(evm); + return_if_error!(call(evm, runtime, debug)); }, opcodes::CALLCODE => { - return_if_error!(make_call(evm, runtime, debug, true)); + return_if_error!(call_code(evm, runtime, debug)); }, opcodes::RETURN => { @@ -621,13 +633,16 @@ pub fn decode_instruction(evm: &mut EVMContext, runtime: &mut impl Runtime, debu opcodes::DELEGATECALL => { // TODO // Same as call but storage, sender and value remain the same - evm.gas_recorder.record_gas(100); + return_if_error!(delegate_call(evm, runtime, debug)); }, 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. + return_error_if_static!(evm); + return_if_error!(create_2(evm, runtime, debug)); + }, + + opcodes::STATICCALL => { + return_if_error!(static_call(evm, runtime, debug)); } }); diff --git a/src/evm_logic/evm/macros.rs b/src/evm_logic/evm/macros.rs index fd4fe0a..3431979 100644 --- a/src/evm_logic/evm/macros.rs +++ b/src/evm_logic/evm/macros.rs @@ -29,7 +29,9 @@ pub(crate) use debug_match; macro_rules! return_if_error { ($evm_val:expr) => { match $evm_val { - ExecutionResult::Err(err) => return ExecutionResult::Err(err), + ExecutionResult::Err(err) => { + println!("Error: {:?}", err); + return ExecutionResult::Err(err)}, _ => {} } }; @@ -38,20 +40,33 @@ pub(crate) use return_if_error; macro_rules! break_if_error { ($evm_val:expr) => { + #[allow(dead_code)] match $evm_val { - ExecutionResult::Err(err) => {break;}, + ExecutionResult::Err(_) => {break;}, _ => {} } }; } pub(crate) use break_if_error; +macro_rules! return_error_if_static { + ($evm_val:expr) => { + if $evm_val.is_static { + return ExecutionResult::Err(Error::ModifyStaticState); + } + + }; +} +pub(crate) use return_error_if_static; + + macro_rules! pop { ($evm_val:tt) => {{ let result = $evm_val.stack.pop(); let result = match result { Err(()) => { + println!("Error: {:?}", Error::InsufficientValuesOnStack); return ExecutionResult::Err(Error::InsufficientValuesOnStack); } diff --git a/src/evm_logic/util.rs b/src/evm_logic/util.rs index cce3d09..2f655aa 100644 --- a/src/evm_logic/util.rs +++ b/src/evm_logic/util.rs @@ -22,7 +22,7 @@ lazy_static! { pub static ref ZERO_H256: H256 = u256_to_h256(U256::zero()); } -fn vec_to_fixed_array(bytes: Vec) -> [u8; 32] { +pub fn vec_to_fixed_array(bytes: Vec) -> [u8; 32] { let mut result = [0u8; 32]; let len = bytes.len().min(32); // Take minimum to avoid out-of-bounds access // Copy bytes into the result array diff --git a/src/result.rs b/src/result.rs index ed8c7ff..5f58a68 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,12 +1,13 @@ -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum Error { InsufficientValuesOnStack, - InsufficientGas + InsufficientGas, + ModifyStaticState, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum ExecutionResult { Success, Err(Error) diff --git a/src/runtime.rs b/src/runtime.rs index f0fc5a7..c07d839 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -48,6 +48,18 @@ pub trait Runtime { fn withdrawal(&mut self, source: U256, value: U256); fn increase_nonce(&mut self, address: U256); + // Create Contract + fn create_contract( + &mut self, + address: U256, + code: Vec, + ); + fn set_contract_code( + &mut self, + address: U256, + code: Vec, + ); + fn add_context(&mut self); fn merge_context(&mut self); fn revert_context(&mut self); diff --git a/src/state/memory.rs b/src/state/memory.rs index d3ca8a1..129619f 100644 --- a/src/state/memory.rs +++ b/src/state/memory.rs @@ -70,14 +70,14 @@ impl Memory { ) { println!("Write addresses: {}", write_address); if write_address + length > self.max_index { + self.expand(write_address + length, &mut GasRecorder{gas_usage: 0, gas_refunds: 0}); + } + if memory.bytes.len() < read_address + length { println!("Expanding memory"); println!("memory bytes length: {}", memory.bytes.len()); println!("read address: {}", read_address); println!("length: {}", length); println!("max index: {}", self.max_index); - self.expand(write_address + length, &mut GasRecorder{gas_usage: 0, gas_refunds: 0}); - } - if memory.bytes.len() < read_address + length { memory.expand(read_address + length, gas_recorder) } self.bytes[write_address..write_address + length] diff --git a/test_gen/src/lib.rs b/test_gen/src/lib.rs index cc946b2..22e730a 100644 --- a/test_gen/src/lib.rs +++ b/test_gen/src/lib.rs @@ -223,7 +223,7 @@ pub fn generate_official_tests_from_file(input: TokenStream) -> TokenStream { } } for i in 0..num_tests { - // for i in 10..20 { + // for i in 70..80 { let test_name = Ident::new( format!("run_test_{}", i).as_str(), proc_macro2::Span::call_site(), diff --git a/tests/mocks/mock_runtime.rs b/tests/mocks/mock_runtime.rs index d57e822..6188b0f 100644 --- a/tests/mocks/mock_runtime.rs +++ b/tests/mocks/mock_runtime.rs @@ -1,5 +1,5 @@ -use ethereum_evm::runtime::Runtime; use ethereum_evm::evm_logic::util::{h256_to_u256, keccak256, u256_to_h256}; +use ethereum_evm::runtime::Runtime; use hex::encode; use primitive_types::{H160, H256, U256}; use rlp::{Encodable, RlpStream}; @@ -95,7 +95,7 @@ impl MockRuntime { contract .storage .iter() - .filter(|(_,value)| {**value != H256::zero()}) + .filter(|(_, value)| **value != H256::zero()) .map(|(key, value)| (key, rlp::encode(&h256_to_u256(*value)))), ), code_hash: keccak256(&contract.code), @@ -103,7 +103,10 @@ impl MockRuntime { balance: contract.balance, code_version: U256::zero(), }); - println!("encoded_contract: {:x?}", encode(keccak256(&encoded_contract.to_vec()))); + println!( + "encoded_contract: {:x?}", + encode(keccak256(&encoded_contract.to_vec())) + ); encoded_contract }) }) @@ -173,7 +176,11 @@ impl Runtime for MockRuntime { } fn read_original_storage(&self, address: U256, index: U256) -> H256 { - self.contracts[&address].storage.get(&u256_to_h256(index)).unwrap_or(&H256::zero()).clone() + self.contracts[&address] + .storage + .get(&u256_to_h256(index)) + .unwrap_or(&H256::zero()) + .clone() } fn read_storage(&self, address: U256, index: U256) -> H256 { self.current_context.as_ref().unwrap().contracts[&address] @@ -292,6 +299,38 @@ impl Runtime for MockRuntime { .nonce += U256::from(1); } + fn create_contract(&mut self, address: U256, code: Vec) { + println!("Created1: {:?}", address); + let contract = Contract { + balance: U256::from(0 as u64), + code_size: U256::from(code.len() as u64), + code_hash: keccak256(&code), + code: code, + nonce: U256::from(0 as u64), + storage: BTreeMap::new(), + is_deleted: false, + is_cold: false, + hot_keys: HashSet::new(), + }; + println!("Created2: {:?}", address); + match &mut self.current_context { + Some(context) => { + context.as_mut().contracts.insert(address, contract); + } + None => {} + }; + } + fn set_contract_code(&mut self, address: U256, code: Vec) { + match &mut self.current_context { + Some(context) => { + let contract = context.as_mut().contracts.get_mut(&address).unwrap(); + contract.code = code.clone(); + contract.code_hash = keccak256(&code); + contract.code_size = U256::from(code.len() as u64); + } + None => {} + }; + } // Modify context stack fn add_context(&mut self) { // Could be slightly faster with a swap perhaps @@ -299,7 +338,7 @@ impl Runtime for MockRuntime { Some(context) => { self.current_context = Some(Box::new(Context { contracts: context.contracts.clone(), - prev_context: Some(context) + prev_context: Some(context), })); } None => {