diff --git a/crates/levm/.gitignore b/crates/levm/.gitignore new file mode 100644 index 000000000..e9d75b20e --- /dev/null +++ b/crates/levm/.gitignore @@ -0,0 +1 @@ +ethtests diff --git a/crates/levm/src/call_frame.rs b/crates/levm/src/call_frame.rs index bfc7d7570..22acd2728 100644 --- a/crates/levm/src/call_frame.rs +++ b/crates/levm/src/call_frame.rs @@ -18,14 +18,15 @@ pub struct Log { pub data: Bytes, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct CallFrame { pub gas: U256, pub pc: usize, pub msg_sender: Address, - pub callee: Address, - pub bytecode: Bytes, + pub to: Address, + pub code_address: Address, pub delegate: Option
, + pub bytecode: Bytes, pub msg_value: U256, pub stack: Vec, // max 1024 in the future pub memory: Memory, @@ -34,15 +35,41 @@ pub struct CallFrame { // where to store return data of subcall pub return_data_offset: Option, pub return_data_size: Option, + pub is_static: bool, pub transient_storage: TransientStorage, pub logs: Vec, - pub is_static: bool, } impl CallFrame { - pub fn new(bytecode: Bytes) -> Self { + pub fn new_from_bytecode(bytecode: Bytes) -> Self { + Self { + bytecode, + ..Default::default() + } + } + + #[allow(clippy::too_many_arguments)] + pub fn new( + gas: U256, + msg_sender: Address, + to: Address, + code_address: Address, + delegate: Option
, + bytecode: Bytes, + msg_value: U256, + calldata: Bytes, + is_static: bool, + ) -> Self { Self { + gas, + msg_sender, + to, + code_address, + delegate, bytecode, + msg_value, + calldata, + is_static, ..Default::default() } } diff --git a/crates/levm/src/memory.rs b/crates/levm/src/memory.rs index 09d2513b9..9dc899272 100644 --- a/crates/levm/src/memory.rs +++ b/crates/levm/src/memory.rs @@ -1,6 +1,6 @@ use crate::primitives::U256; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct Memory { data: Vec, } @@ -16,6 +16,10 @@ impl Memory { Self { data: Vec::new() } } + pub fn new_from_vec(data: Vec) -> Self { + Self { data } + } + fn resize(&mut self, offset: usize) { if offset.next_multiple_of(32) > self.data.len() { self.data.resize(offset.next_multiple_of(32), 0); diff --git a/crates/levm/src/opcodes.rs b/crates/levm/src/opcodes.rs index ce01260e1..608c8e689 100644 --- a/crates/levm/src/opcodes.rs +++ b/crates/levm/src/opcodes.rs @@ -158,11 +158,11 @@ pub enum Opcode { // // System Operations // CREATE = 0xF0, CALL = 0xF1, - // CALLCODE = 0xF2, + CALLCODE = 0xF2, RETURN = 0xF3, - // DELEGATECALL = 0xF4, + DELEGATECALL = 0xF4, // CREATE2 = 0xF5, - // STATICCALL = 0xFA, + STATICCALL = 0xFA, // REVERT = 0xFD, // INVALID = 0xFE, // SELFDESTRUCT = 0xFF, @@ -301,7 +301,10 @@ impl From for Opcode { x if x == Opcode::TLOAD as u8 => Opcode::TLOAD, x if x == Opcode::TSTORE as u8 => Opcode::TSTORE, 0xF1 => Opcode::CALL, + 0xF2 => Opcode::CALLCODE, 0xF3 => Opcode::RETURN, + 0xF4 => Opcode::DELEGATECALL, + 0xFA => Opcode::STATICCALL, _ => panic!("Unknown opcode: 0x{:02X}", byte), } } diff --git a/crates/levm/src/operations.rs b/crates/levm/src/operations.rs index 0a6672003..3b967970d 100644 --- a/crates/levm/src/operations.rs +++ b/crates/levm/src/operations.rs @@ -91,11 +91,11 @@ pub enum Operation { Log(u8), // Create, Call, - // CallCode, + CallCode, Return, - // DelegateCall, + DelegateCall, // Create2, - // StaticCall, + StaticCall, // Revert, // Invalid, // SelfDestruct, @@ -222,11 +222,11 @@ impl Operation { Bytes::copy_from_slice(&[Opcode::LOG0 as u8 + n]) } // Operation::Create => Bytes::copy_from_slice(&[Opcode::CREATE as u8]), Operation::Call => Bytes::copy_from_slice(&[Opcode::CALL as u8]), - // Operation::CallCode => Bytes::copy_from_slice(&[Opcode::CALLCODE as u8]), + Operation::CallCode => Bytes::copy_from_slice(&[Opcode::CALLCODE as u8]), Operation::Return => Bytes::copy_from_slice(&[Opcode::RETURN as u8]), - // Operation::DelegateCall => Bytes::copy_from_slice(&[Opcode::DELEGATECALL as u8]), + Operation::DelegateCall => Bytes::copy_from_slice(&[Opcode::DELEGATECALL as u8]), // Operation::Create2 => Bytes::copy_from_slice(&[Opcode::CREATE2 as u8]), - // Operation::StaticCall => Bytes::copy_from_slice(&[Opcode::STATICCALL as u8]), + Operation::StaticCall => Bytes::copy_from_slice(&[Opcode::STATICCALL as u8]), // Operation::Revert => Bytes::copy_from_slice(&[Opcode::REVERT as u8]), // Operation::Invalid => Bytes::copy_from_slice(&[Opcode::INVALID as u8]), // Operation::SelfDestruct => Bytes::copy_from_slice(&[Opcode::SELFDESTRUCT as u8]), diff --git a/crates/levm/src/primitives.rs b/crates/levm/src/primitives.rs index 9499d0242..fe32edef8 100644 --- a/crates/levm/src/primitives.rs +++ b/crates/levm/src/primitives.rs @@ -1,2 +1,2 @@ pub use bytes::Bytes; -pub use ethereum_types::{Address, H160, H256, U256, U512}; +pub use ethereum_types::{Address, H160, H256, H32, U256, U512}; diff --git a/crates/levm/src/vm.rs b/crates/levm/src/vm.rs index 2711acc60..9d584d468 100644 --- a/crates/levm/src/vm.rs +++ b/crates/levm/src/vm.rs @@ -5,9 +5,8 @@ use crate::{ call_frame::{CallFrame, Log}, constants::{REVERT_FOR_CALL, SUCCESS_FOR_CALL, SUCCESS_FOR_RETURN}, opcodes::Opcode, - primitives::{Address, Bytes, U256, U512}, + primitives::{Address, Bytes, H256, H32, U256, U512}, }; -use ethereum_types::{H256, H32}; use sha3::{Digest, Keccak256}; #[derive(Clone, Default, Debug)] @@ -27,7 +26,7 @@ impl Account { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct StorageSlot { pub original_value: U256, pub current_value: U256, @@ -97,7 +96,7 @@ impl VM { pub fn new(bytecode: Bytes, address: Address, balance: U256) -> Self { let initial_account = Account::new(balance, bytecode.clone()); - let initial_call_frame = CallFrame::new(bytecode); + let initial_call_frame = CallFrame::new_from_bytecode(bytecode); let mut db: Db = Default::default(); db.accounts.insert(address, initial_account); Self { @@ -663,9 +662,15 @@ impl VM { } Opcode::SLOAD => { let key = current_call_frame.stack.pop().unwrap(); + let address = if let Some(delegate) = current_call_frame.delegate { + delegate + } else { + current_call_frame.code_address + }; + let current_value = self .db - .read_account_storage(¤t_call_frame.msg_sender, &key) + .read_account_storage(&address, &key) .unwrap_or_default() .current_value; current_call_frame.stack.push(current_value); @@ -679,15 +684,21 @@ impl VM { let value = current_call_frame.stack.pop().unwrap(); // maybe we need the journal struct as accessing the Db could be slow, with the journal // we can have prefetched values directly in memory and only commits the values to the db once everything is done - let address = ¤t_call_frame.msg_sender; // should change when we have create/call transactions - let slot = self.db.read_account_storage(address, &key); + + let address = if let Some(delegate) = current_call_frame.delegate { + delegate + } else { + current_call_frame.code_address + }; + + let slot = self.db.read_account_storage(&address, &key); let (original_value, _) = match slot { Some(slot) => (slot.original_value, slot.current_value), None => (value, value), }; self.db.write_account_storage( - address, + &address, key, StorageSlot { original_value, @@ -715,43 +726,64 @@ impl VM { } Opcode::CALL => { let gas = current_call_frame.stack.pop().unwrap(); - let address = + let code_address = Address::from_low_u64_be(current_call_frame.stack.pop().unwrap().low_u64()); let value = current_call_frame.stack.pop().unwrap(); let args_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let args_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let ret_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let ret_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); - // check balance - if self.db.balance(¤t_call_frame.msg_sender) < value { - current_call_frame.stack.push(U256::from(REVERT_FOR_CALL)); - continue; - } - // transfer value - // transfer(¤t_call_frame.msg_sender, &address, value); - let callee_bytecode = self.db.get_account_bytecode(&address); - if callee_bytecode.is_empty() { - current_call_frame.stack.push(U256::from(SUCCESS_FOR_CALL)); - continue; - } - let calldata = current_call_frame - .memory - .load_range(args_offset, args_size) - .into(); - let new_call_frame = CallFrame { + let msg_sender = current_call_frame.msg_sender; // caller remains the msg_sender + let to = current_call_frame.to; // to remains the same + let is_static = current_call_frame.is_static; + + self.generic_call( + &mut current_call_frame, gas, - msg_sender: current_call_frame.msg_sender, // caller remains the msg_sender - callee: address, - bytecode: callee_bytecode, - msg_value: value, - calldata, - ..Default::default() - }; - current_call_frame.return_data_offset = Some(ret_offset); - current_call_frame.return_data_size = Some(ret_size); - self.call_frames.push(current_call_frame.clone()); - current_call_frame = new_call_frame; + value, + msg_sender, + to, + code_address, + None, + false, + is_static, + args_offset, + args_size, + ret_offset, + ret_size, + ); + } + Opcode::CALLCODE => { + // Creates a new sub context as if calling itself, but with the code of the given account. In particular the storage remains the same. Note that an account with no code will return success as true. + let gas = current_call_frame.stack.pop().unwrap(); + let code_address = + Address::from_low_u64_be(current_call_frame.stack.pop().unwrap().low_u64()); + let value = current_call_frame.stack.pop().unwrap(); + let args_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let args_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let msg_sender = current_call_frame.msg_sender; // msg_sender is changed to the proxy's address + let to = current_call_frame.to; // to remains the same + let is_static = current_call_frame.is_static; + + self.generic_call( + &mut current_call_frame, + gas, + value, + code_address, + to, + code_address, + Some(msg_sender), + false, + is_static, + args_offset, + args_size, + ret_offset, + ret_size, + ); } Opcode::RETURN => { let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); @@ -776,6 +808,67 @@ impl VM { break; } } + Opcode::DELEGATECALL => { + // The delegatecall executes the setVars(uint256) code from Contract B but updates Contract A’s storage. The execution has the same storage, msg.sender & msg.value as its parent call setVarsDelegateCall. + // Creates a new sub context as if calling itself, but with the code of the given account. In particular the storage, the current sender and the current value remain the same. Note that an account with no code will return success as true. + let gas = current_call_frame.stack.pop().unwrap(); + let code_address = + Address::from_low_u64_be(current_call_frame.stack.pop().unwrap().low_u64()); + let args_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let args_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let value = current_call_frame.msg_value; // value remains the same + let msg_sender = current_call_frame.msg_sender; // caller remains the msg_sender + let to = current_call_frame.to; // to remains the same + let is_static = current_call_frame.is_static; + + self.generic_call( + &mut current_call_frame, + gas, + value, + msg_sender, + to, + code_address, + Some(msg_sender), + false, + is_static, + args_offset, + args_size, + ret_offset, + ret_size, + ); + } + Opcode::STATICCALL => { + // it cannot be used to transfer Ether + let gas = current_call_frame.stack.pop().unwrap(); + let code_address = + Address::from_low_u64_be(current_call_frame.stack.pop().unwrap().low_u64()); + let args_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let args_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let ret_size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let msg_sender = current_call_frame.msg_sender; // caller remains the msg_sender + let value = current_call_frame.msg_value; + + self.generic_call( + &mut current_call_frame, + gas, + value, // check + msg_sender, + code_address, + code_address, + None, + false, + true, + args_offset, + args_size, + ret_offset, + ret_size, + ); + } Opcode::TLOAD => { let key = current_call_frame.stack.pop().unwrap(); let value = current_call_frame @@ -803,6 +896,63 @@ impl VM { pub fn current_call_frame_mut(&mut self) -> &mut CallFrame { self.call_frames.last_mut().unwrap() } + + #[allow(clippy::too_many_arguments)] + pub fn generic_call( + &mut self, + current_call_frame: &mut CallFrame, + gas: U256, + value: U256, + msg_sender: Address, + to: Address, + code_address: Address, + delegate: Option
, + _should_transfer_value: bool, + is_static: bool, + args_offset: usize, + args_size: usize, + ret_offset: usize, + ret_size: usize, + ) { + // check balance + if self.db.balance(¤t_call_frame.msg_sender) < value { + current_call_frame.stack.push(U256::from(REVERT_FOR_CALL)); + return; + } + + // transfer value + // transfer(¤t_call_frame.msg_sender, &address, value); + + let callee_bytecode = self.db.get_account_bytecode(&code_address); + + if callee_bytecode.is_empty() { + current_call_frame.stack.push(U256::from(SUCCESS_FOR_CALL)); + return; + } + + let calldata = current_call_frame + .memory + .load_range(args_offset, args_size) + .into(); + + let new_call_frame = CallFrame::new( + gas, + msg_sender, + to, + code_address, + delegate, + callee_bytecode, + value, + calldata, + is_static, + ); + + current_call_frame.return_data_offset = Some(ret_offset); + current_call_frame.return_data_size = Some(ret_size); + + self.call_frames.push(current_call_frame.clone()); + *current_call_frame = new_call_frame; + } } pub fn arithmetic_shift_right(value: U256, shift: U256) -> U256 { diff --git a/crates/levm/tests/tests.rs b/crates/levm/tests/tests.rs index e8e733ec1..32b2bf1f5 100644 --- a/crates/levm/tests/tests.rs +++ b/crates/levm/tests/tests.rs @@ -1,9 +1,8 @@ -use ethereum_types::H32; use levm::{ block::TARGET_BLOB_GAS_PER_BLOCK, operations::Operation, - primitives::{Address, Bytes, H256, U256}, - vm::{Account, VM}, + primitives::{Address, Bytes, H256, H32, U256}, + vm::{Account, StorageSlot, VM}, }; // cargo test -p 'levm' @@ -1197,7 +1196,6 @@ fn call_returns_if_bytecode_empty() { ); vm.db.add_account(callee_address, callee_account); - println!("to excec"); vm.execute(); let success = vm.current_call_frame_mut().stack.pop().unwrap(); @@ -1341,6 +1339,280 @@ fn nested_calls() { assert_eq!(return_data, expected_bytes); } +#[test] +fn staticcall_changes_callframe_is_static() { + let callee_return_value = U256::from(0xAAAAAAA); + let callee_ops = [ + Operation::Push32(callee_return_value), // value + Operation::Push32(U256::zero()), // offset + Operation::Mstore, + Operation::Stop, + ]; + + let callee_bytecode = callee_ops + .iter() + .flat_map(Operation::to_bytecode) + .collect::(); + + let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); + let callee_address_u256 = U256::from(2); + let callee_account = Account::new(U256::from(500000), callee_bytecode); + + let caller_ops = vec![ + Operation::Push32(U256::from(32)), // ret_size + Operation::Push32(U256::from(0)), // ret_offset + Operation::Push32(U256::from(0)), // args_size + Operation::Push32(U256::from(0)), // args_offset + Operation::Push32(U256::zero()), // value + Operation::Push32(callee_address_u256), // address + Operation::Push32(U256::from(100_000)), // gas + Operation::StaticCall, + ]; + + let mut vm = new_vm_with_ops_addr_bal( + &caller_ops, + Address::from_low_u64_be(U256::from(1).low_u64()), + U256::zero(), + ); + + vm.db.add_account(callee_address, callee_account); + + vm.execute(); + + let current_call_frame = vm.current_call_frame_mut(); + + let ret_offset = 0; + let ret_size = 32; + let return_data = current_call_frame.memory.load_range(ret_offset, ret_size); + + assert_eq!(U256::from_big_endian(&return_data), U256::from(0xAAAAAAA)); + assert!(current_call_frame.is_static); +} + +#[test] +fn delegatecall_changes_own_storage_and_regular_call_doesnt() { + // --- DELEGATECALL --- changes account 1 storage + let callee_return_value = U256::from(0xBBBBBBB); + let callee_ops = [ + Operation::Push32(callee_return_value), // value + Operation::Push32(U256::zero()), // key + Operation::Sstore, + Operation::Stop, + ]; + + let callee_bytecode = callee_ops + .iter() + .flat_map(Operation::to_bytecode) + .collect::(); + + let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); + let callee_address_u256 = U256::from(2); + let callee_account = Account::new(U256::from(500000), callee_bytecode.clone()); + + let caller_ops = vec![ + Operation::Push32(U256::from(32)), // ret_size + Operation::Push32(U256::from(0)), // ret_offset + Operation::Push32(U256::from(0)), // args_size + Operation::Push32(U256::from(0)), // args_offset + Operation::Push32(callee_address_u256), // code address + Operation::Push32(U256::from(100_000)), // gas + Operation::DelegateCall, + ]; + + let mut vm = new_vm_with_ops_addr_bal( + &caller_ops, + Address::from_low_u64_be(U256::from(1).low_u64()), + U256::from(1000), + ); + + vm.db.add_account(callee_address, callee_account); + + let current_call_frame = vm.current_call_frame_mut(); + current_call_frame.msg_sender = Address::from_low_u64_be(U256::from(1).low_u64()); + current_call_frame.to = Address::from_low_u64_be(U256::from(5).low_u64()); + + vm.execute(); + + let storage_slot = vm.db.read_account_storage( + &Address::from_low_u64_be(U256::from(1).low_u64()), + &U256::zero(), + ); + let slot = StorageSlot { + original_value: U256::from(0xBBBBBBB), + current_value: U256::from(0xBBBBBBB), + }; + + assert_eq!(storage_slot, Some(slot)); + + // --- CALL --- changes account 2 storage + + let callee_return_value = U256::from(0xAAAAAAA); + let callee_ops = [ + Operation::Push32(callee_return_value), // value + Operation::Push32(U256::zero()), // key + Operation::Sstore, + Operation::Stop, + ]; + + let callee_bytecode = callee_ops + .iter() + .flat_map(Operation::to_bytecode) + .collect::(); + + let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); + let callee_address_u256 = U256::from(2); + let callee_account = Account::new(U256::from(500000), callee_bytecode); + + let caller_ops = vec![ + Operation::Push32(U256::from(32)), // ret_size + Operation::Push32(U256::from(0)), // ret_offset + Operation::Push32(U256::from(0)), // args_size + Operation::Push32(U256::from(0)), // args_offset + Operation::Push32(U256::zero()), // value + Operation::Push32(callee_address_u256), // address + Operation::Push32(U256::from(100_000)), // gas + Operation::Call, + ]; + + let mut vm = new_vm_with_ops_addr_bal( + &caller_ops, + Address::from_low_u64_be(U256::from(1).low_u64()), + U256::zero(), + ); + + vm.db.add_account(callee_address, callee_account); + + let current_call_frame = vm.current_call_frame_mut(); + current_call_frame.msg_sender = Address::from_low_u64_be(U256::from(1).low_u64()); + current_call_frame.to = Address::from_low_u64_be(U256::from(5).low_u64()); + + vm.execute(); + + let storage_slot = vm.db.read_account_storage(&callee_address, &U256::zero()); + let slot = StorageSlot { + original_value: U256::from(0xAAAAAAA), + current_value: U256::from(0xAAAAAAA), + }; + + assert_eq!(storage_slot, Some(slot)); +} + +#[test] +fn delegatecall_and_callcode_differ_on_value_and_msg_sender() { + // --- DELEGATECALL + let callee_return_value = U256::from(0xBBBBBBB); + let callee_ops = [ + Operation::Push32(callee_return_value), // value + Operation::Push32(U256::zero()), // key + Operation::Sstore, + Operation::Stop, + ]; + + let callee_bytecode = callee_ops + .iter() + .flat_map(Operation::to_bytecode) + .collect::(); + + let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); + let callee_address_u256 = U256::from(2); + let callee_account = Account::new(U256::from(500000), callee_bytecode.clone()); + + let caller_ops = vec![ + Operation::Push32(U256::from(32)), // ret_size + Operation::Push32(U256::from(0)), // ret_offset + Operation::Push32(U256::from(0)), // args_size + Operation::Push32(U256::from(0)), // args_offset + Operation::Push32(callee_address_u256), // code address + Operation::Push32(U256::from(100_000)), // gas + Operation::DelegateCall, + ]; + + let mut vm = new_vm_with_ops_addr_bal( + &caller_ops, + Address::from_low_u64_be(U256::from(1).low_u64()), + U256::from(1000), + ); + + vm.db.add_account(callee_address, callee_account); + + let current_call_frame = vm.current_call_frame_mut(); + current_call_frame.msg_sender = Address::from_low_u64_be(U256::from(1).low_u64()); + current_call_frame.to = Address::from_low_u64_be(U256::from(5).low_u64()); + + vm.execute(); + + let current_call_frame = vm.current_call_frame_mut(); + + assert_eq!( + current_call_frame.msg_sender, + Address::from_low_u64_be(U256::from(1).low_u64()) + ); + assert_eq!(current_call_frame.msg_value, U256::from(0)); + + // --- CALLCODE --- + + let callee_return_value = U256::from(0xAAAAAAA); + let callee_ops = [ + Operation::Push32(callee_return_value), // value + Operation::Push32(U256::zero()), // key + Operation::Sstore, + Operation::Stop, + ]; + + let callee_bytecode = callee_ops + .iter() + .flat_map(Operation::to_bytecode) + .collect::(); + + let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); + let callee_address_u256 = U256::from(2); + let callee_account = Account::new(U256::from(500000), callee_bytecode); + + let caller_ops = vec![ + Operation::Push32(U256::from(0)), // ret_size + Operation::Push32(U256::from(0)), // ret_offset + Operation::Push32(U256::from(0)), // args_size + Operation::Push32(U256::from(0)), // args_offset + Operation::Push32(U256::from(100)), // value + Operation::Push32(callee_address_u256), // address + Operation::Push32(U256::from(100_000)), // gas + Operation::CallCode, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops_addr_bal( + &caller_ops, + Address::from_low_u64_be(U256::from(1).low_u64()), + U256::from(1000), + ); + + vm.db.add_account(callee_address, callee_account); + + let current_call_frame = vm.current_call_frame_mut(); + current_call_frame.msg_sender = Address::from_low_u64_be(U256::from(1).low_u64()); + current_call_frame.to = Address::from_low_u64_be(U256::from(5).low_u64()); + current_call_frame.code_address = Address::from_low_u64_be(U256::from(2).low_u64()); + + vm.execute(); + + let current_call_frame = vm.current_call_frame_mut().clone(); + + let storage_slot = vm.db.read_account_storage( + &Address::from_low_u64_be(U256::from(1).low_u64()), + &U256::zero(), + ); + let slot = StorageSlot { + original_value: U256::from(0xAAAAAAA), + current_value: U256::from(0xAAAAAAA), + }; + assert_eq!(storage_slot, Some(slot)); + assert_eq!( + current_call_frame.msg_sender, + Address::from_low_u64_be(U256::from(2).low_u64()) + ); + assert_eq!(current_call_frame.msg_value, U256::from(100)); +} + #[test] fn pop_op() { let operations = [ @@ -1925,6 +2197,104 @@ fn blob_base_fee_minimun_cost() { ); } +#[test] +fn sstore_op() { + let key = U256::from(80); + let value = U256::from(100); + let sender_address = Address::from_low_u64_be(3000); + let operations = vec![ + Operation::Push((1, value)), + Operation::Push((1, key)), + Operation::Sstore, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + vm.current_call_frame_mut().code_address = sender_address; + vm.db.accounts.insert(sender_address, Account::default()); + + vm.execute(); + + let account = vm.db.accounts.get(&sender_address).unwrap(); + let stored_value = account.storage.get(&key).unwrap(); + assert_eq!(value, stored_value.current_value); +} + +#[test] +#[should_panic] +fn sstore_reverts_when_called_in_static() { + let key = U256::from(80); + let value = U256::from(100); + let operations = vec![ + Operation::Push((1, value)), + Operation::Push((1, key)), + Operation::Sstore, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + vm.current_call_frame_mut().is_static = true; + vm.execute(); +} + +#[test] +fn sload_op() { + let key = U256::from(80); + let value = U256::from(100); + let sender_address = Address::from_low_u64_be(3000); + let operations = vec![ + Operation::Push((1, value)), + Operation::Push((1, key)), + Operation::Sstore, + Operation::Push((1, key)), + Operation::Sload, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + vm.current_call_frame_mut().msg_sender = sender_address; + vm.db.accounts.insert(sender_address, Account::default()); + + vm.execute(); + + assert_eq!(value, vm.current_call_frame_mut().stack.pop().unwrap()); +} + +#[test] +fn sload_untouched_key_of_storage() { + let key = U256::from(404); + let sender_address = Address::from_low_u64_be(3000); + let operations = vec![Operation::Push((2, key)), Operation::Sload, Operation::Stop]; + + let mut vm = new_vm_with_ops(&operations); + vm.current_call_frame_mut().msg_sender = sender_address; + vm.db.accounts.insert(sender_address, Account::default()); + + vm.execute(); + + assert_eq!( + U256::zero(), + vm.current_call_frame_mut().stack.pop().unwrap() + ); +} + +#[test] +fn sload_on_not_existing_account() { + let key = U256::from(80); + let sender_address = Address::from_low_u64_be(3000); + let operations = vec![Operation::Push((2, key)), Operation::Sload, Operation::Stop]; + + let mut vm = new_vm_with_ops(&operations); + vm.current_call_frame_mut().msg_sender = sender_address; + + vm.execute(); + + assert_eq!( + U256::zero(), + vm.current_call_frame_mut().stack.pop().unwrap() + ); +} + #[test] fn log0() { let data: [u8; 32] = [0xff; 32]; @@ -2198,101 +2568,3 @@ fn multiple_logs_of_different_types() { assert_eq!(logs[0].topics, vec![H32::from_slice(&topic1)]); assert_eq!(logs[1].topics.len(), 0); } - -#[test] -fn sstore_op() { - let key = U256::from(80); - let value = U256::from(100); - let sender_address = Address::from_low_u64_be(3000); - let operations = vec![ - Operation::Push((1, value)), - Operation::Push((1, key)), - Operation::Sstore, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - vm.current_call_frame_mut().msg_sender = sender_address; - vm.db.accounts.insert(sender_address, Account::default()); - - vm.execute(); - - let account = vm.db.accounts.get(&sender_address).unwrap(); - let stored_value = account.storage.get(&key).unwrap(); - assert_eq!(value, stored_value.current_value); -} - -#[test] -#[should_panic] -fn sstore_reverts_when_called_in_static() { - let key = U256::from(80); - let value = U256::from(100); - let operations = vec![ - Operation::Push((1, value)), - Operation::Push((1, key)), - Operation::Sstore, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - vm.current_call_frame_mut().is_static = true; - vm.execute(); -} - -#[test] -fn sload_op() { - let key = U256::from(80); - let value = U256::from(100); - let sender_address = Address::from_low_u64_be(3000); - let operations = vec![ - Operation::Push((1, value)), - Operation::Push((1, key)), - Operation::Sstore, - Operation::Push((1, key)), - Operation::Sload, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - vm.current_call_frame_mut().msg_sender = sender_address; - vm.db.accounts.insert(sender_address, Account::default()); - - vm.execute(); - - assert_eq!(value, vm.current_call_frame_mut().stack.pop().unwrap()); -} - -#[test] -fn sload_untouched_key_of_storage() { - let key = U256::from(404); - let sender_address = Address::from_low_u64_be(3000); - let operations = vec![Operation::Push((2, key)), Operation::Sload, Operation::Stop]; - - let mut vm = new_vm_with_ops(&operations); - vm.current_call_frame_mut().msg_sender = sender_address; - vm.db.accounts.insert(sender_address, Account::default()); - - vm.execute(); - - assert_eq!( - U256::zero(), - vm.current_call_frame_mut().stack.pop().unwrap() - ); -} - -#[test] -fn sload_on_not_existing_account() { - let key = U256::from(80); - let sender_address = Address::from_low_u64_be(3000); - let operations = vec![Operation::Push((2, key)), Operation::Sload, Operation::Stop]; - - let mut vm = new_vm_with_ops(&operations); - vm.current_call_frame_mut().msg_sender = sender_address; - - vm.execute(); - - assert_eq!( - U256::zero(), - vm.current_call_frame_mut().stack.pop().unwrap() - ); -}