diff --git a/crates/levm/src/call_frame.rs b/crates/levm/src/call_frame.rs index 22acd2728a..0d0e311d9d 100644 --- a/crates/levm/src/call_frame.rs +++ b/crates/levm/src/call_frame.rs @@ -97,7 +97,7 @@ impl CallFrame { // Should be a halt when we implement it panic!("Invalid jump"); } - self.pc = jump_address.as_usize() + 1; + self.pc = jump_address.as_usize(); } fn valid_jump(&self, jump_address: U256) -> bool { diff --git a/crates/levm/src/constants.rs b/crates/levm/src/constants.rs index 686876cf23..ac3c44ba21 100644 --- a/crates/levm/src/constants.rs +++ b/crates/levm/src/constants.rs @@ -1,3 +1,84 @@ pub const SUCCESS_FOR_CALL: i32 = 1; pub const REVERT_FOR_CALL: i32 = 0; pub const SUCCESS_FOR_RETURN: i32 = 1; +pub const TX_BASE_COST: u64 = 21_000; +pub const WORD_SIZE: usize = 32; + +/// Contains the gas costs of the EVM instructions +pub mod gas_cost { + pub const ADD: u64 = 3; + pub const MUL: u64 = 5; + pub const SUB: u64 = 3; + pub const DIV: u64 = 5; + pub const SDIV: u64 = 5; + pub const MOD: u64 = 5; + pub const SMOD: u64 = 5; + pub const ADDMOD: u64 = 8; + pub const MULMOD: u64 = 8; + pub const EXP_STATIC: u64 = 10; + pub const EXP_DYNAMIC_BASE: u64 = 50; + pub const SIGNEXTEND: u64 = 5; + pub const LT: u64 = 3; + pub const GT: u64 = 3; + pub const SLT: u64 = 3; + pub const SGT: u64 = 3; + pub const EQ: u64 = 3; + pub const ISZERO: u64 = 3; + pub const AND: u64 = 3; + pub const OR: u64 = 3; + pub const XOR: u64 = 3; + pub const NOT: u64 = 3; + pub const BYTE: u64 = 3; + pub const SHL: u64 = 3; + pub const SHR: u64 = 3; + pub const SAR: u64 = 3; + pub const KECCAK25_STATIC: u64 = 30; + pub const KECCAK25_DYNAMIC_BASE: u64 = 6; + pub const CALLDATALOAD: u64 = 3; + pub const CALLDATASIZE: u64 = 2; + pub const CALLDATACOPY_STATIC: u64 = 3; + pub const CALLDATACOPY_DYNAMIC_BASE: u64 = 3; + pub const RETURNDATASIZE: u64 = 2; + pub const RETURNDATACOPY_STATIC: u64 = 3; + pub const RETURNDATACOPY_DYNAMIC_BASE: u64 = 3; + pub const BLOCKHASH: u64 = 20; + pub const COINBASE: u64 = 2; + pub const TIMESTAMP: u64 = 2; + pub const NUMBER: u64 = 2; + pub const PREVRANDAO: u64 = 2; + pub const GASLIMIT: u64 = 2; + pub const CHAINID: u64 = 2; + pub const SELFBALANCE: u64 = 5; + pub const BASEFEE: u64 = 2; + pub const BLOBHASH: u64 = 3; + pub const BLOBBASEFEE: u64 = 2; + pub const POP: u64 = 2; + pub const MLOAD_STATIC: u64 = 3; + pub const MSTORE_STATIC: u64 = 3; + pub const MSTORE8_STATIC: u64 = 3; + pub const JUMP: u64 = 8; + pub const JUMPI: u64 = 10; + pub const PC: u64 = 2; + pub const MSIZE: u64 = 2; + pub const GAS: u64 = 2; + pub const JUMPDEST: u64 = 1; + pub const TLOAD: u64 = 100; + pub const TSTORE: u64 = 100; + pub const MCOPY_STATIC: u64 = 3; + pub const MCOPY_DYNAMIC_BASE: u64 = 3; + pub const PUSH0: u64 = 2; + pub const PUSHN: u64 = 3; + pub const DUPN: u64 = 3; + pub const SWAPN: u64 = 3; + pub const LOGN_STATIC: u64 = 375; + pub const LOGN_DYNAMIC_BASE: u64 = 375; + pub const LOGN_DYNAMIC_BYTE_BASE: u64 = 8; +} + +pub mod call_opcode { + pub const WARM_ADDRESS_ACCESS_COST: u64 = 100; + pub const COLD_ADDRESS_ACCESS_COST: u64 = 2_600; + pub const NON_ZERO_VALUE_COST: u64 = 9_000; + pub const BASIC_FALLBACK_FUNCTION_STIPEND: u64 = 2_300; + pub const VALUE_TO_EMPTY_ACCOUNT_COST: u64 = 25_000; +} diff --git a/crates/levm/src/memory.rs b/crates/levm/src/memory.rs index 9dc8992724..3af961b506 100644 --- a/crates/levm/src/memory.rs +++ b/crates/levm/src/memory.rs @@ -1,3 +1,4 @@ +use crate::constants::WORD_SIZE; use crate::primitives::U256; #[derive(Debug, Clone, Default, PartialEq)] @@ -62,4 +63,17 @@ impl Memory { self.data[dest_offset..dest_offset + size].copy_from_slice(&temp); } + + pub fn expansion_cost(&self, memory_byte_size: usize) -> usize { + if memory_byte_size <= self.data.len() { + return 0; + } + let new_memory_size_word = (memory_byte_size + WORD_SIZE - 1) / WORD_SIZE; + let new_memory_cost = + (new_memory_size_word * new_memory_size_word) / 512 + (3 * new_memory_size_word); + let last_memory_size_word = (self.data.len() + WORD_SIZE - 1) / WORD_SIZE; + let last_memory_cost = + (last_memory_size_word * last_memory_size_word) / 512 + (3 * last_memory_size_word); + new_memory_cost - last_memory_cost + } } diff --git a/crates/levm/src/opcodes.rs b/crates/levm/src/opcodes.rs index 608c8e689c..d28fe43885 100644 --- a/crates/levm/src/opcodes.rs +++ b/crates/levm/src/opcodes.rs @@ -33,7 +33,7 @@ pub enum Opcode { // KECCAK256 KECCAK256 = 0x20, - // // Environmental Information + // Environmental Information // ADDRESS = 0x30, // BALANCE = 0x31, // ORIGIN = 0x32, @@ -51,7 +51,7 @@ pub enum Opcode { RETURNDATACOPY = 0x3E, // EXTCODEHASH = 0x3F, - // // Block Information + // Block Information BLOCKHASH = 0x40, COINBASE = 0x41, TIMESTAMP = 0x42, @@ -64,7 +64,7 @@ pub enum Opcode { BLOBHASH = 0x49, BLOBBASEFEE = 0x4A, - // // Stack, Memory, Storage, and Flow Operations + // Stack, Memory, Storage, and Flow Operations POP = 0x50, MLOAD = 0x51, MSTORE = 0x52, @@ -75,13 +75,13 @@ pub enum Opcode { JUMPI = 0x57, PC = 0x58, MSIZE = 0x59, - // GAS = 0x5A, + GAS = 0x5A, JUMPDEST = 0x5B, TLOAD = 0x5C, TSTORE = 0x5D, MCOPY = 0x5E, - // // Push Operations + // Push Operations PUSH0 = 0x5F, PUSH1 = 0x60, PUSH2 = 0x61, @@ -115,7 +115,8 @@ pub enum Opcode { PUSH30 = 0x7D, PUSH31 = 0x7E, PUSH32 = 0x7F, - // // Duplication Operations + + // Duplication Operations DUP1 = 0x80, DUP2 = 0x81, DUP3 = 0x82, @@ -132,7 +133,8 @@ pub enum Opcode { DUP14 = 0x8D, DUP15 = 0x8E, DUP16 = 0x8F, - // // Swap Operations + + // Swap Operations SWAP1 = 0x90, SWAP2 = 0x91, SWAP3 = 0x92, @@ -149,13 +151,14 @@ pub enum Opcode { SWAP14 = 0x9D, SWAP15 = 0x9E, SWAP16 = 0x9F, - // // Logging Operations + // Logging Operations LOG0 = 0xA0, LOG1 = 0xA1, LOG2 = 0xA2, LOG3 = 0xA3, LOG4 = 0xA4, - // // System Operations + + // System Operations // CREATE = 0xF0, CALL = 0xF1, CALLCODE = 0xF2, @@ -297,6 +300,7 @@ impl From for Opcode { 0x54 => Opcode::SLOAD, 0x55 => Opcode::SSTORE, 0x59 => Opcode::MSIZE, + 0x5A => Opcode::GAS, 0x5E => Opcode::MCOPY, x if x == Opcode::TLOAD as u8 => Opcode::TLOAD, x if x == Opcode::TSTORE as u8 => Opcode::TSTORE, diff --git a/crates/levm/src/operations.rs b/crates/levm/src/operations.rs index 3b967970db..bc3e30a785 100644 --- a/crates/levm/src/operations.rs +++ b/crates/levm/src/operations.rs @@ -78,7 +78,7 @@ pub enum Operation { Jumpi, PC, Msize, - // Gas, + Gas, Jumpdest, Tload, Tstore, @@ -177,7 +177,7 @@ impl Operation { Operation::Jumpi => Bytes::copy_from_slice(&[Opcode::JUMPI as u8]), Operation::PC => Bytes::copy_from_slice(&[Opcode::PC as u8]), Operation::Msize => Bytes::copy_from_slice(&[Opcode::MSIZE as u8]), - // Operation::Gas => Bytes::copy_from_slice(&[Opcode::GAS as u8]), + Operation::Gas => Bytes::copy_from_slice(&[Opcode::GAS as u8]), Operation::Jumpdest => Bytes::copy_from_slice(&[Opcode::JUMPDEST as u8]), Operation::Tload => Bytes::copy_from_slice(&[Opcode::TLOAD as u8]), Operation::Tstore => Bytes::copy_from_slice(&[Opcode::TSTORE as u8]), diff --git a/crates/levm/src/vm.rs b/crates/levm/src/vm.rs index 9d584d4685..b74b012c3e 100644 --- a/crates/levm/src/vm.rs +++ b/crates/levm/src/vm.rs @@ -1,9 +1,12 @@ -use std::{collections::HashMap, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; use crate::{ block::{BlockEnv, LAST_AVAILABLE_BLOCK_LIMIT}, call_frame::{CallFrame, Log}, - constants::{REVERT_FOR_CALL, SUCCESS_FOR_CALL, SUCCESS_FOR_RETURN}, + constants::*, opcodes::Opcode, primitives::{Address, Bytes, H256, H32, U256, U512}, }; @@ -13,6 +16,7 @@ use sha3::{Digest, Keccak256}; pub struct Account { balance: U256, bytecode: Bytes, + nonce: u64, pub storage: HashMap, } @@ -21,9 +25,14 @@ impl Account { Self { balance, bytecode, + nonce: 0, storage: Default::default(), } } + + pub fn is_empty(&self) -> bool { + self.balance.is_zero() && self.nonce == 0 && self.bytecode.is_empty() + } } #[derive(Debug, Clone, Default, PartialEq)] @@ -76,6 +85,9 @@ pub struct VM { pub call_frames: Vec, pub block_env: BlockEnv, pub db: Db, + gas_limit: u64, + pub consumed_gas: u64, // TODO: check where to place these two in the future, probably TxEnv + warm_addresses: HashSet
, } /// Shifts the value to the right by 255 bits and checks the most significant bit is a 1 @@ -97,39 +109,64 @@ impl VM { let initial_account = Account::new(balance, bytecode.clone()); let initial_call_frame = CallFrame::new_from_bytecode(bytecode); - let mut db: Db = Default::default(); + + let mut db = Db::default(); db.accounts.insert(address, initial_account); + + let mut warm_addresses = HashSet::new(); + warm_addresses.insert(address); + Self { call_frames: vec![initial_call_frame.clone()], - block_env: BlockEnv::default(), + block_env: Default::default(), db, + gas_limit: i64::MAX as _, // it is initialized like this for testing + consumed_gas: TX_BASE_COST, + warm_addresses, } } pub fn execute(&mut self) { + let block_env = self.block_env.clone(); + let mut tx_env = self.clone(); // simulates a TxEnv let mut current_call_frame = self.call_frames.pop().unwrap(); loop { match current_call_frame.next_opcode().unwrap() { Opcode::STOP => break, Opcode::ADD => { + if tx_env.consumed_gas + gas_cost::ADD > tx_env.gas_limit { + break; // should revert the tx + } let augend = current_call_frame.stack.pop().unwrap(); let addend = current_call_frame.stack.pop().unwrap(); let sum = augend.overflowing_add(addend).0; current_call_frame.stack.push(sum); + tx_env.consumed_gas += gas_cost::ADD } Opcode::MUL => { + if tx_env.consumed_gas + gas_cost::MUL > tx_env.gas_limit { + break; // should revert the tx + } let multiplicand = current_call_frame.stack.pop().unwrap(); let multiplier = current_call_frame.stack.pop().unwrap(); let product = multiplicand.overflowing_mul(multiplier).0; current_call_frame.stack.push(product); + tx_env.consumed_gas += gas_cost::MUL } Opcode::SUB => { + if tx_env.consumed_gas + gas_cost::SUB > tx_env.gas_limit { + break; // should revert the tx + } let minuend = current_call_frame.stack.pop().unwrap(); let subtrahend = current_call_frame.stack.pop().unwrap(); let difference = minuend.overflowing_sub(subtrahend).0; current_call_frame.stack.push(difference); + tx_env.consumed_gas += gas_cost::SUB } Opcode::DIV => { + if tx_env.consumed_gas + gas_cost::DIV > tx_env.gas_limit { + break; // should revert the tx + } let dividend = current_call_frame.stack.pop().unwrap(); let divisor = current_call_frame.stack.pop().unwrap(); if divisor.is_zero() { @@ -138,8 +175,12 @@ impl VM { } let quotient = dividend / divisor; current_call_frame.stack.push(quotient); + tx_env.consumed_gas += gas_cost::DIV } Opcode::SDIV => { + if tx_env.consumed_gas + gas_cost::SDIV > tx_env.gas_limit { + break; // should revert the tx + } let dividend = current_call_frame.stack.pop().unwrap(); let divisor = current_call_frame.stack.pop().unwrap(); if divisor.is_zero() { @@ -168,8 +209,12 @@ impl VM { }; current_call_frame.stack.push(quotient); + tx_env.consumed_gas += gas_cost::SDIV } Opcode::MOD => { + if tx_env.consumed_gas + gas_cost::MOD > tx_env.gas_limit { + break; // should revert the tx + } let dividend = current_call_frame.stack.pop().unwrap(); let divisor = current_call_frame.stack.pop().unwrap(); if divisor.is_zero() { @@ -178,8 +223,12 @@ impl VM { } let remainder = dividend % divisor; current_call_frame.stack.push(remainder); + tx_env.consumed_gas += gas_cost::MOD } Opcode::SMOD => { + if tx_env.consumed_gas + gas_cost::SMOD > tx_env.gas_limit { + break; // should revert the tx + } let dividend = current_call_frame.stack.pop().unwrap(); let divisor = current_call_frame.stack.pop().unwrap(); if divisor.is_zero() { @@ -208,8 +257,12 @@ impl VM { }; current_call_frame.stack.push(remainder); + tx_env.consumed_gas += gas_cost::SMOD } Opcode::ADDMOD => { + if tx_env.consumed_gas + gas_cost::ADDMOD > tx_env.gas_limit { + break; // should revert the tx + } let augend = current_call_frame.stack.pop().unwrap(); let addend = current_call_frame.stack.pop().unwrap(); let divisor = current_call_frame.stack.pop().unwrap(); @@ -224,10 +277,13 @@ impl VM { } current_call_frame.stack.push(remainder); + tx_env.consumed_gas += gas_cost::ADDMOD } Opcode::MULMOD => { + if tx_env.consumed_gas + gas_cost::MULMOD > tx_env.gas_limit { + break; // should revert the tx + } let multiplicand = U512::from(current_call_frame.stack.pop().unwrap()); - let multiplier = U512::from(current_call_frame.stack.pop().unwrap()); let divisor = U512::from(current_call_frame.stack.pop().unwrap()); if divisor.is_zero() { @@ -250,14 +306,27 @@ impl VM { result.reverse(); let remainder = U256::from(result.as_slice()); current_call_frame.stack.push(remainder); + tx_env.consumed_gas += gas_cost::MULMOD } Opcode::EXP => { let base = current_call_frame.stack.pop().unwrap(); let exponent = current_call_frame.stack.pop().unwrap(); + + let exponent_byte_size = (exponent.bits() as u64 + 7) / 8; + let gas_cost = + gas_cost::EXP_STATIC + gas_cost::EXP_DYNAMIC_BASE * exponent_byte_size; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let power = base.overflowing_pow(exponent).0; current_call_frame.stack.push(power); + tx_env.consumed_gas += gas_cost } Opcode::SIGNEXTEND => { + if tx_env.consumed_gas + gas_cost::SIGNEXTEND > tx_env.gas_limit { + break; // should revert the tx + } let byte_size = current_call_frame.stack.pop().unwrap(); let value_to_extend = current_call_frame.stack.pop().unwrap(); @@ -275,20 +344,32 @@ impl VM { value_to_extend & sign_bit_mask }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::SIGNEXTEND } Opcode::LT => { + if tx_env.consumed_gas + gas_cost::LT > tx_env.gas_limit { + break; // should revert the tx + } let lho = current_call_frame.stack.pop().unwrap(); let rho = current_call_frame.stack.pop().unwrap(); let result = if lho < rho { U256::one() } else { U256::zero() }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::LT } Opcode::GT => { + if tx_env.consumed_gas + gas_cost::GT > tx_env.gas_limit { + break; // should revert the tx + } let lho = current_call_frame.stack.pop().unwrap(); let rho = current_call_frame.stack.pop().unwrap(); let result = if lho > rho { U256::one() } else { U256::zero() }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::GT } Opcode::SLT => { + if tx_env.consumed_gas + gas_cost::SLT > tx_env.gas_limit { + break; // should revert the tx + } let lho = current_call_frame.stack.pop().unwrap(); let rho = current_call_frame.stack.pop().unwrap(); let lho_is_negative = lho.bit(255); @@ -309,8 +390,12 @@ impl VM { } }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::SLT } Opcode::SGT => { + if tx_env.consumed_gas + gas_cost::SGT > tx_env.gas_limit { + break; // should revert the tx + } let lho = current_call_frame.stack.pop().unwrap(); let rho = current_call_frame.stack.pop().unwrap(); let lho_is_negative = lho.bit(255); @@ -331,8 +416,12 @@ impl VM { } }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::SGT } Opcode::EQ => { + if tx_env.consumed_gas + gas_cost::EQ > tx_env.gas_limit { + break; // should revert the tx + } let lho = current_call_frame.stack.pop().unwrap(); let rho = current_call_frame.stack.pop().unwrap(); let result = if lho == rho { @@ -341,8 +430,12 @@ impl VM { U256::zero() }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::EQ } Opcode::ISZERO => { + if tx_env.consumed_gas + gas_cost::ISZERO > tx_env.gas_limit { + break; // should revert the tx + } let operand = current_call_frame.stack.pop().unwrap(); let result = if operand == U256::zero() { U256::one() @@ -350,10 +443,22 @@ impl VM { U256::zero() }; current_call_frame.stack.push(result); + tx_env.consumed_gas += gas_cost::ISZERO } Opcode::KECCAK256 => { let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let minimum_word_size = (size + WORD_SIZE - 1) / WORD_SIZE; + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(offset + size); + let gas_cost = gas_cost::KECCAK25_STATIC + + gas_cost::KECCAK25_DYNAMIC_BASE * minimum_word_size as u64 + + memory_expansion_cost as u64; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let value_bytes = current_call_frame.memory.load_range(offset, size); let mut hasher = Keccak256::new(); @@ -362,24 +467,44 @@ impl VM { current_call_frame .stack .push(U256::from_big_endian(&result)); + tx_env.consumed_gas += gas_cost } Opcode::CALLDATALOAD => { + if tx_env.consumed_gas + gas_cost::CALLDATALOAD > tx_env.gas_limit { + break; // should revert the tx + } let offset: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let value = U256::from_big_endian( ¤t_call_frame.calldata.slice(offset..offset + 32), ); current_call_frame.stack.push(value); + tx_env.consumed_gas += gas_cost::CALLDATALOAD } Opcode::CALLDATASIZE => { + if tx_env.consumed_gas + gas_cost::CALLDATASIZE > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame .stack .push(U256::from(current_call_frame.calldata.len())); + tx_env.consumed_gas += gas_cost::CALLDATASIZE } Opcode::CALLDATACOPY => { let dest_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let calldata_offset: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let size: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let minimum_word_size = (size + WORD_SIZE - 1) / WORD_SIZE; + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(dest_offset + size) as u64; + let gas_cost = gas_cost::CALLDATACOPY_STATIC + + gas_cost::CALLDATACOPY_DYNAMIC_BASE * minimum_word_size as u64 + + memory_expansion_cost; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + tx_env.consumed_gas += gas_cost; if size == 0 { continue; } @@ -390,15 +515,30 @@ impl VM { current_call_frame.memory.store_bytes(dest_offset, &data); } Opcode::RETURNDATASIZE => { + if tx_env.consumed_gas + gas_cost::RETURNDATASIZE > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame .stack .push(U256::from(current_call_frame.returndata.len())); + tx_env.consumed_gas += gas_cost::RETURNDATASIZE } Opcode::RETURNDATACOPY => { let dest_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let returndata_offset: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let size: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let minimum_word_size = (size + WORD_SIZE - 1) / WORD_SIZE; + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(dest_offset + size) as u64; + let gas_cost = gas_cost::RETURNDATACOPY_STATIC + + gas_cost::RETURNDATACOPY_DYNAMIC_BASE * minimum_word_size as u64 + + memory_expansion_cost; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + tx_env.consumed_gas += gas_cost; if size == 0 { continue; } @@ -408,27 +548,47 @@ impl VM { current_call_frame.memory.store_bytes(dest_offset, &data); } Opcode::JUMP => { + if tx_env.consumed_gas + gas_cost::JUMP > tx_env.gas_limit { + break; // should revert the tx + } let jump_address = current_call_frame.stack.pop().unwrap(); current_call_frame.jump(jump_address); + tx_env.consumed_gas += gas_cost::JUMP } Opcode::JUMPI => { + if tx_env.consumed_gas + gas_cost::JUMPI > tx_env.gas_limit { + break; // should revert the tx + } let jump_address = current_call_frame.stack.pop().unwrap(); let condition = current_call_frame.stack.pop().unwrap(); if condition != U256::zero() { current_call_frame.jump(jump_address); } + tx_env.consumed_gas += gas_cost::JUMPI } Opcode::JUMPDEST => { // just consume some gas, jumptable written at the start + if tx_env.consumed_gas + gas_cost::JUMPDEST > tx_env.gas_limit { + break; // should revert the tx + } + tx_env.consumed_gas += gas_cost::JUMPDEST } Opcode::PC => { + if tx_env.consumed_gas + gas_cost::PC > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame .stack .push(U256::from(current_call_frame.pc - 1)); + tx_env.consumed_gas += gas_cost::PC } Opcode::BLOCKHASH => { + if tx_env.consumed_gas + gas_cost::BLOCKHASH > tx_env.gas_limit { + break; // should revert the tx + } let block_number = current_call_frame.stack.pop().unwrap(); + tx_env.consumed_gas += gas_cost::BLOCKHASH; // If number is not in the valid range (last 256 blocks), return zero. if block_number < self @@ -450,50 +610,97 @@ impl VM { }; } Opcode::COINBASE => { - let coinbase = self.block_env.coinbase; + if tx_env.consumed_gas + gas_cost::COINBASE > tx_env.gas_limit { + break; // should revert the tx + } + let coinbase = block_env.coinbase; current_call_frame.stack.push(address_to_word(coinbase)); + tx_env.consumed_gas += gas_cost::COINBASE } Opcode::TIMESTAMP => { - let timestamp = self.block_env.timestamp; + if tx_env.consumed_gas + gas_cost::TIMESTAMP > tx_env.gas_limit { + break; // should revert the tx + } + let timestamp = block_env.timestamp; current_call_frame.stack.push(timestamp); + tx_env.consumed_gas += gas_cost::TIMESTAMP } Opcode::NUMBER => { - let block_number = self.block_env.number; + if tx_env.consumed_gas + gas_cost::NUMBER > tx_env.gas_limit { + break; // should revert the tx + } + let block_number = block_env.number; current_call_frame.stack.push(block_number); + tx_env.consumed_gas += gas_cost::NUMBER } Opcode::PREVRANDAO => { - let randao = self.block_env.prev_randao.unwrap_or_default(); + if tx_env.consumed_gas + gas_cost::PREVRANDAO > tx_env.gas_limit { + break; // should revert the tx + } + let randao = block_env.prev_randao.unwrap_or_default(); current_call_frame .stack .push(U256::from_big_endian(randao.0.as_slice())); + tx_env.consumed_gas += gas_cost::PREVRANDAO } Opcode::GASLIMIT => { - let gas_limit = self.block_env.gas_limit; + if tx_env.consumed_gas + gas_cost::GASLIMIT > tx_env.gas_limit { + break; // should revert the tx + } + let gas_limit = block_env.gas_limit; current_call_frame.stack.push(U256::from(gas_limit)); + tx_env.consumed_gas += gas_cost::GASLIMIT } Opcode::CHAINID => { - let chain_id = self.block_env.chain_id; + if tx_env.consumed_gas + gas_cost::CHAINID > tx_env.gas_limit { + break; // should revert the tx + } + let chain_id = block_env.chain_id; current_call_frame.stack.push(U256::from(chain_id)); + tx_env.consumed_gas += gas_cost::CHAINID } Opcode::SELFBALANCE => { - todo!("when we have accounts implemented") + if tx_env.consumed_gas + gas_cost::SELFBALANCE > tx_env.gas_limit { + break; // should revert the tx + } + tx_env.consumed_gas += gas_cost::SELFBALANCE; + todo!("when we have accounts implemented"); } Opcode::BASEFEE => { - let base_fee = self.block_env.base_fee_per_gas; + if tx_env.consumed_gas + gas_cost::BASEFEE > tx_env.gas_limit { + break; // should revert the tx + } + let base_fee = block_env.base_fee_per_gas; current_call_frame.stack.push(base_fee); + tx_env.consumed_gas += gas_cost::BASEFEE } Opcode::BLOBHASH => { + if tx_env.consumed_gas + gas_cost::BLOBHASH > tx_env.gas_limit { + break; // should revert the tx + } + tx_env.consumed_gas += gas_cost::BLOBHASH; todo!("when we have tx implemented"); } Opcode::BLOBBASEFEE => { - let blob_base_fee = self.block_env.calculate_blob_gas_price(); + if tx_env.consumed_gas + gas_cost::BLOBBASEFEE > tx_env.gas_limit { + break; // should revert the tx + } + let blob_base_fee = block_env.calculate_blob_gas_price(); current_call_frame.stack.push(blob_base_fee); + tx_env.consumed_gas += gas_cost::BLOBBASEFEE } Opcode::PUSH0 => { + if tx_env.consumed_gas + gas_cost::PUSH0 > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame.stack.push(U256::zero()); + tx_env.consumed_gas += gas_cost::PUSH0 } // PUSHn op if (Opcode::PUSH1..Opcode::PUSH32).contains(&op) => { + if tx_env.consumed_gas + gas_cost::PUSHN > tx_env.gas_limit { + break; // should revert the tx + } let n_bytes = (op as u8) - (Opcode::PUSH1 as u8) + 1; let next_n_bytes = current_call_frame .bytecode @@ -502,56 +709,78 @@ impl VM { let value_to_push = U256::from(next_n_bytes); current_call_frame.stack.push(value_to_push); current_call_frame.increment_pc_by(n_bytes as usize); + tx_env.consumed_gas += gas_cost::PUSHN } Opcode::PUSH32 => { + if tx_env.consumed_gas + gas_cost::PUSHN > tx_env.gas_limit { + break; // should revert the tx + } let next_32_bytes = current_call_frame .bytecode - .get(current_call_frame.pc()..current_call_frame.pc() + 32) + .get(current_call_frame.pc()..current_call_frame.pc() + WORD_SIZE) .unwrap(); let value_to_push = U256::from(next_32_bytes); current_call_frame.stack.push(value_to_push); - current_call_frame.increment_pc_by(32); + current_call_frame.increment_pc_by(WORD_SIZE); + tx_env.consumed_gas += gas_cost::PUSHN } Opcode::AND => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::AND > tx_env.gas_limit { + break; // should revert the tx + } let a = current_call_frame.stack.pop().unwrap(); let b = current_call_frame.stack.pop().unwrap(); current_call_frame.stack.push(a & b); + tx_env.consumed_gas += gas_cost::AND } Opcode::OR => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::OR > tx_env.gas_limit { + break; // should revert the tx + } let a = current_call_frame.stack.pop().unwrap(); let b = current_call_frame.stack.pop().unwrap(); current_call_frame.stack.push(a | b); + tx_env.consumed_gas += gas_cost::OR } Opcode::XOR => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::XOR > tx_env.gas_limit { + break; // should revert the tx + } let a = current_call_frame.stack.pop().unwrap(); let b = current_call_frame.stack.pop().unwrap(); current_call_frame.stack.push(a ^ b); + tx_env.consumed_gas += gas_cost::XOR } Opcode::NOT => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::NOT > tx_env.gas_limit { + break; // should revert the tx + } let a = current_call_frame.stack.pop().unwrap(); current_call_frame.stack.push(!a); + tx_env.consumed_gas += gas_cost::NOT } Opcode::BYTE => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::BYTE > tx_env.gas_limit { + break; // should revert the tx + } let op1 = current_call_frame.stack.pop().unwrap(); let op2 = current_call_frame.stack.pop().unwrap(); let byte_index = op1.try_into().unwrap_or(usize::MAX); - if byte_index < 32 { + if byte_index < WORD_SIZE { current_call_frame .stack - .push(U256::from(op2.byte(31 - byte_index))); + .push(U256::from(op2.byte(WORD_SIZE - 1 - byte_index))); } else { current_call_frame.stack.push(U256::zero()); } + tx_env.consumed_gas += gas_cost::BYTE } Opcode::SHL => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::SHL > tx_env.gas_limit { + break; // should revert the tx + } let shift = current_call_frame.stack.pop().unwrap(); let value = current_call_frame.stack.pop().unwrap(); if shift < U256::from(256) { @@ -559,9 +788,12 @@ impl VM { } else { current_call_frame.stack.push(U256::zero()); } + tx_env.consumed_gas += gas_cost::SHL } Opcode::SHR => { - // spend_gas(3); + if tx_env.consumed_gas + gas_cost::SHR > tx_env.gas_limit { + break; // should revert the tx + } let shift = current_call_frame.stack.pop().unwrap(); let value = current_call_frame.stack.pop().unwrap(); if shift < U256::from(256) { @@ -569,8 +801,12 @@ impl VM { } else { current_call_frame.stack.push(U256::zero()); } + tx_env.consumed_gas += gas_cost::SHR } Opcode::SAR => { + if tx_env.consumed_gas + gas_cost::SAR > tx_env.gas_limit { + break; // should revert the tx + } let shift = current_call_frame.stack.pop().unwrap(); let value = current_call_frame.stack.pop().unwrap(); let res = if shift < U256::from(256) { @@ -581,9 +817,13 @@ impl VM { U256::zero() }; current_call_frame.stack.push(res); + tx_env.consumed_gas += gas_cost::SAR } // DUPn op if (Opcode::DUP1..=Opcode::DUP16).contains(&op) => { + if tx_env.consumed_gas + gas_cost::DUPN > tx_env.gas_limit { + break; // should revert the tx + } let depth = (op as u8) - (Opcode::DUP1 as u8) + 1; assert!( current_call_frame.stack.len().ge(&(depth as usize)), @@ -594,9 +834,13 @@ impl VM { .get(current_call_frame.stack.len() - depth as usize) .unwrap(); current_call_frame.stack.push(*value_at_depth); + tx_env.consumed_gas += gas_cost::DUPN } // SWAPn op if (Opcode::SWAP1..=Opcode::SWAP16).contains(&op) => { + if tx_env.consumed_gas + gas_cost::SWAPN > tx_env.gas_limit { + break; // should revert the tx + } let depth = (op as u8) - (Opcode::SWAP1 as u8) + 1; assert!( current_call_frame.stack.len().ge(&(depth as usize)), @@ -607,25 +851,40 @@ impl VM { current_call_frame .stack .swap(stack_top_index - 1, to_swap_index - 1); + tx_env.consumed_gas += gas_cost::SWAPN } Opcode::POP => { + if tx_env.consumed_gas + gas_cost::POP > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame.stack.pop().unwrap(); + tx_env.consumed_gas += gas_cost::POP } op if (Opcode::LOG0..=Opcode::LOG4).contains(&op) => { if current_call_frame.is_static { panic!("Cannot create log in static context"); // should return an error and halt } - let number_of_topics = (op as u8) - (Opcode::LOG0 as u8); + let topic_count = (op as u8) - (Opcode::LOG0 as u8); let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); - let topics = (0..number_of_topics) + let topics = (0..topic_count) .map(|_| { let topic = current_call_frame.stack.pop().unwrap().as_u32(); H32::from_slice(topic.to_be_bytes().as_ref()) }) .collect(); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(offset + size) as u64; + let gas_cost = gas_cost::LOGN_STATIC + + gas_cost::LOGN_DYNAMIC_BASE * topic_count as u64 + + gas_cost::LOGN_DYNAMIC_BYTE_BASE * size as u64 + + memory_expansion_cost; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let data = current_call_frame.memory.load_range(offset, size); let log = Log { address: current_call_frame.msg_sender, // Should change the addr if we are on a Call/Create transaction (Call should be the contract we are calling, Create should be the original caller) @@ -633,32 +892,54 @@ impl VM { data: Bytes::from(data), }; current_call_frame.logs.push(log); + tx_env.consumed_gas += gas_cost } Opcode::MLOAD => { - // spend_gas(3); let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(offset + WORD_SIZE); + let gas_cost = gas_cost::MLOAD_STATIC + memory_expansion_cost as u64; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let value = current_call_frame.memory.load(offset); current_call_frame.stack.push(value); + tx_env.consumed_gas += gas_cost } Opcode::MSTORE => { - // spend_gas(3); let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(offset + WORD_SIZE); + let gas_cost = gas_cost::MSTORE_STATIC + memory_expansion_cost as u64; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let value = current_call_frame.stack.pop().unwrap(); - let mut value_bytes = [0u8; 32]; + let mut value_bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut value_bytes); current_call_frame.memory.store_bytes(offset, &value_bytes); + tx_env.consumed_gas += gas_cost } Opcode::MSTORE8 => { - // spend_gas(3); let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(offset + 1); + let gas_cost = gas_cost::MSTORE8_STATIC + memory_expansion_cost as u64; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + let value = current_call_frame.stack.pop().unwrap(); - let mut value_bytes = [0u8; 32]; + let mut value_bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut value_bytes); current_call_frame .memory - .store_bytes(offset, value_bytes[31..32].as_ref()); + .store_bytes(offset, value_bytes[WORD_SIZE - 1..WORD_SIZE].as_ref()); + tx_env.consumed_gas += gas_cost } Opcode::SLOAD => { let key = current_call_frame.stack.pop().unwrap(); @@ -707,16 +988,37 @@ impl VM { ); } Opcode::MSIZE => { - // spend_gas(2); + if tx_env.consumed_gas + gas_cost::MSIZE > tx_env.gas_limit { + break; // should revert the tx + } current_call_frame .stack .push(current_call_frame.memory.size()); + tx_env.consumed_gas += gas_cost::MSIZE + } + Opcode::GAS => { + if tx_env.consumed_gas + gas_cost::GAS > tx_env.gas_limit { + break; // should revert the tx + } + let remaining_gas = tx_env.gas_limit - tx_env.consumed_gas - gas_cost::GAS; + current_call_frame.stack.push(remaining_gas.into()); + tx_env.consumed_gas += gas_cost::GAS } Opcode::MCOPY => { - // spend_gas(3) + dynamic gas let dest_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); - let src_offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); - let size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let src_offset: usize = + current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let size: usize = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let words_copied = (size + WORD_SIZE - 1) / WORD_SIZE; + let memory_byte_size = (src_offset + size).max(dest_offset + size); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(memory_byte_size); + let gas_cost = gas_cost::MCOPY_STATIC + + gas_cost::MCOPY_DYNAMIC_BASE * words_copied as u64 + + memory_expansion_cost as u64; + + tx_env.consumed_gas += gas_cost; if size == 0 { continue; } @@ -729,11 +1031,46 @@ impl VM { 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 args_offset: usize = + current_call_frame.stack.pop().unwrap().try_into().unwrap(); + let args_size: usize = + 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 memory_byte_size = (args_offset + args_size).max(ret_offset + ret_size); + let memory_expansion_cost = + current_call_frame.memory.expansion_cost(memory_byte_size); + let code_execution_cost = 0; // TODO + let address_access_cost = if self.warm_addresses.contains(&code_address) { + call_opcode::WARM_ADDRESS_ACCESS_COST + } else { + call_opcode::COLD_ADDRESS_ACCESS_COST + }; + let positive_value_cost = if !value.is_zero() { + call_opcode::NON_ZERO_VALUE_COST + + call_opcode::BASIC_FALLBACK_FUNCTION_STIPEND + } else { + 0 + }; + let account = self.db.accounts.get(&code_address).unwrap(); // if the account doesn't exist, it should be created + let value_to_empty_account_cost = if !value.is_zero() && account.is_empty() { + call_opcode::VALUE_TO_EMPTY_ACCOUNT_COST + } else { + 0 + }; + // has to be returned to the caller + let gas_cost = memory_expansion_cost as u64 + + code_execution_cost + + address_access_cost + + positive_value_cost + + value_to_empty_account_cost; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } + + self.warm_addresses.insert(code_address); + 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; @@ -788,6 +1125,11 @@ impl VM { Opcode::RETURN => { let offset = current_call_frame.stack.pop().unwrap().try_into().unwrap(); let size = current_call_frame.stack.pop().unwrap().try_into().unwrap(); + + let gas_cost = current_call_frame.memory.expansion_cost(offset + size) as u64; + if tx_env.consumed_gas + gas_cost > tx_env.gas_limit { + break; // should revert the tx + } let return_data = current_call_frame.memory.load_range(offset, size).into(); if let Some(mut parent_call_frame) = self.call_frames.pop() { if let (Some(_ret_offset), Some(_ret_size)) = ( @@ -807,6 +1149,7 @@ impl VM { .push(U256::from(SUCCESS_FOR_RETURN)); break; } + tx_env.consumed_gas += gas_cost; } 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. @@ -870,6 +1213,9 @@ impl VM { ); } Opcode::TLOAD => { + if tx_env.consumed_gas + gas_cost::TLOAD > tx_env.gas_limit { + break; // should revert the tx + } let key = current_call_frame.stack.pop().unwrap(); let value = current_call_frame .transient_storage @@ -878,18 +1224,24 @@ impl VM { .unwrap_or(U256::zero()); current_call_frame.stack.push(value); + tx_env.consumed_gas += gas_cost::TLOAD } Opcode::TSTORE => { + if tx_env.consumed_gas + gas_cost::TSTORE > tx_env.gas_limit { + break; // should revert the tx + } let key = current_call_frame.stack.pop().unwrap(); let value = current_call_frame.stack.pop().unwrap(); current_call_frame .transient_storage .insert((current_call_frame.msg_sender, key), value); + tx_env.consumed_gas += gas_cost::TSTORE } _ => unimplemented!(), } } + self.consumed_gas = tx_env.consumed_gas; self.call_frames.push(current_call_frame); } diff --git a/crates/levm/tests/tests.rs b/crates/levm/tests/tests.rs index 32b2bf1f5e..3bd714c760 100644 --- a/crates/levm/tests/tests.rs +++ b/crates/levm/tests/tests.rs @@ -1,8 +1,9 @@ use levm::{ block::TARGET_BLOB_GAS_PER_BLOCK, + constants::TX_BASE_COST, operations::Operation, primitives::{Address, Bytes, H256, H32, U256}, - vm::{Account, StorageSlot, VM}, + vm::{Account, VM}, }; // cargo test -p 'levm' @@ -71,6 +72,7 @@ fn and_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1000)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -86,6 +88,7 @@ fn and_binary_with_zero() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -101,6 +104,7 @@ fn and_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF0F0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xF000)), @@ -113,6 +117,7 @@ fn and_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF000)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xB020)), @@ -125,6 +130,7 @@ fn and_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1000000000000)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -140,6 +146,7 @@ fn or_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1110)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0b1010)), @@ -152,6 +159,7 @@ fn or_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1010)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(u64::MAX)), @@ -164,6 +172,7 @@ fn or_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFFFFFFFFFFFFFFFF_u64)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -179,6 +188,7 @@ fn or_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFFFF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xF000)), @@ -191,6 +201,7 @@ fn or_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF0F0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xB020)), @@ -203,6 +214,7 @@ fn or_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1011111100101111)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -218,6 +230,7 @@ fn xor_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b110)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0b1010)), @@ -230,6 +243,7 @@ fn xor_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b1010)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(u64::MAX)), @@ -242,6 +256,7 @@ fn xor_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(u64::MAX)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(u64::MAX)), @@ -254,6 +269,7 @@ fn xor_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -269,6 +285,7 @@ fn xor_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFF)), @@ -281,6 +298,7 @@ fn xor_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFFFF)), @@ -293,6 +311,7 @@ fn xor_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF0F)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xF000)), @@ -305,6 +324,7 @@ fn xor_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x4C0F)), @@ -317,6 +337,7 @@ fn xor_with_hex_numbers() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0b111011001000100)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -332,6 +353,7 @@ fn not() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); let expected = !U256::from(0b1010); assert_eq!(result, expected); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 6); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::MAX), @@ -343,6 +365,7 @@ fn not() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 6); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::zero()), @@ -354,6 +377,7 @@ fn not() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::MAX); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 6); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(1)), @@ -365,6 +389,7 @@ fn not() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::MAX - 1); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 6); } #[test] @@ -380,6 +405,7 @@ fn byte_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF1)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x33ED)), @@ -392,6 +418,7 @@ fn byte_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x33)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -407,6 +434,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::MAX), @@ -419,6 +447,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x00E0D0000)), @@ -431,6 +460,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x0D)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFDEA179)), @@ -443,6 +473,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFDEA179)), @@ -455,6 +486,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::zero()), @@ -467,6 +499,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let word = U256::from_big_endian(&[ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x57, 0x08, 0x09, 0x90, 0x0B, 0x0C, 0x0D, 0x0E, @@ -485,6 +518,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x90)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(word), @@ -497,6 +531,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x57)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(word), @@ -509,6 +544,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xDD)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(word), @@ -521,6 +557,7 @@ fn byte_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x40)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -536,6 +573,7 @@ fn shl_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xDDDD)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x12345678)), @@ -548,6 +586,7 @@ fn shl_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x2468acf0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x12345678)), @@ -560,6 +599,7 @@ fn shl_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(4886718336_u64)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFF)), @@ -572,6 +612,7 @@ fn shl_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xFF << 4)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -587,6 +628,7 @@ fn shl_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::zero()), @@ -599,6 +641,7 @@ fn shl_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::MAX), @@ -611,6 +654,7 @@ fn shl_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::MAX - 1); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -626,6 +670,7 @@ fn shr_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xDDDD)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x12345678)), @@ -638,6 +683,7 @@ fn shr_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x91a2b3c)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0x12345678)), @@ -650,6 +696,7 @@ fn shr_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x1234567)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::from(0xFF)), @@ -662,6 +709,7 @@ fn shr_basic() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0xF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -677,6 +725,7 @@ fn shr_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::zero()), @@ -689,6 +738,7 @@ fn shr_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::zero()); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); let mut vm = new_vm_with_ops(&[ Operation::Push32(U256::MAX), @@ -701,6 +751,7 @@ fn shr_edge_cases() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::MAX >> 1); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -716,6 +767,7 @@ fn sar_shift_by_0() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x12345678)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -742,6 +794,7 @@ fn sar_shifting_large_value_with_all_bits_set() { 0xff, 0xff, ]); assert_eq!(result, expected); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -768,6 +821,7 @@ fn sar_shifting_negative_value_and_small_shift() { 0x00, 0x00, ]); assert_eq!(result, expected); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -783,6 +837,7 @@ fn sar_shift_positive_value() { let result = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(result, U256::from(0x07FFFF)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -810,6 +865,7 @@ fn sar_shift_negative_value() { ]); // change 0x8f to 0xf8 assert_eq!(result, expected); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 9); } #[test] @@ -820,11 +876,11 @@ fn keccak256_zero_offset_size_four() { "0xFFFFFFFF00000000000000000000000000000000000000000000000000000000", )), Operation::Push0, - Operation::Mstore, + Operation::Mstore, // gas_cost = 3 + 3 = 6 // Call the opcode Operation::Push((1, 4.into())), // size Operation::Push0, // offset - Operation::Keccak256, + Operation::Keccak256, // gas_cost = 30 + 6 + 0 = 36 Operation::Stop, ]; @@ -832,11 +888,12 @@ fn keccak256_zero_offset_size_four() { vm.execute(); - assert!( - vm.current_call_frame_mut().stack.pop().unwrap() - == U256::from("0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238") + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from("0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238") ); - assert!(vm.current_call_frame_mut().pc() == 40); + assert_eq!(vm.current_call_frame_mut().pc(), 40); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 52); } #[test] @@ -847,7 +904,7 @@ fn keccak256_zero_offset_size_bigger_than_actual_memory() { "0xFFFFFFFF00000000000000000000000000000000000000000000000000000000", )), Operation::Push0, - Operation::Mstore, + Operation::Mstore, // gas_cost = 3 + 3 = 6 // Call the opcode Operation::Push((1, 33.into())), // size > memory.data.len() (32) Operation::Push0, // offset @@ -863,7 +920,8 @@ fn keccak256_zero_offset_size_bigger_than_actual_memory() { vm.current_call_frame_mut().stack.pop().unwrap() == U256::from("0xae75624a7d0413029c1e0facdd38cc8e177d9225892e2490a69c2f1f89512061") ); - assert!(vm.current_call_frame_mut().pc() == 40); + assert_eq!(vm.current_call_frame_mut().pc(), 40); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 61); } #[test] @@ -879,11 +937,12 @@ fn keccak256_zero_offset_zero_size() { vm.execute(); - assert!( - vm.current_call_frame_mut().stack.pop().unwrap() - == U256::from("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470") ); - assert!(vm.current_call_frame_mut().pc() == 4); + assert_eq!(vm.current_call_frame_mut().pc(), 4); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 34); } #[test] @@ -906,11 +965,12 @@ fn keccak256_offset_four_size_four() { vm.execute(); - assert!( - vm.current_call_frame_mut().stack.pop().unwrap() - == U256::from("0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c") + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from("0xe8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c") ); - assert!(vm.current_call_frame_mut().pc() == 41); + assert_eq!(vm.current_call_frame_mut().pc(), 41); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 53); } #[test] @@ -930,6 +990,7 @@ fn mstore() { U256::from(32) ); assert_eq!(vm.current_call_frame_mut().pc(), 69); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 14); } #[test] @@ -950,6 +1011,7 @@ fn mstore_saves_correct_value() { let memory_size = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(memory_size, U256::from(32)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 14); } #[test] @@ -976,6 +1038,7 @@ fn mstore8() { stored_value.to_big_endian(&mut value_bytes); assert_eq!(value_bytes[0..1], [0xAB]); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 12); } #[test] @@ -1006,6 +1069,7 @@ fn mcopy() { let memory_size = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(memory_size, U256::from(96)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 35); } #[test] @@ -1030,6 +1094,7 @@ fn mload() { let loaded_value = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(loaded_value, U256::from(0x33333)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 18); } #[test] @@ -1047,6 +1112,7 @@ fn msize() { let initial_size = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(initial_size, U256::from(0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); let operations = [ Operation::Push32(U256::from(0x33333)), // value @@ -1067,6 +1133,7 @@ fn msize() { let after_store_size = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(after_store_size, U256::from(32)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 14); let operations = [ Operation::Push32(U256::from(0x55555)), // value @@ -1087,6 +1154,7 @@ fn msize() { let final_size = vm.current_call_frame_mut().stack.pop().unwrap(); assert_eq!(final_size, U256::from(96)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 20); } #[test] @@ -1115,8 +1183,9 @@ fn mstore_mload_offset_not_multiple_of_32() { assert_eq!(loaded_value, U256::from(0xabcdef)); assert_eq!(memory_size, U256::from(64)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 23); - //check with big offset + // check with big offset let operations = [ Operation::Push32(0x123456.into()), // value @@ -1142,6 +1211,7 @@ fn mstore_mload_offset_not_multiple_of_32() { assert_eq!(loaded_value, U256::from(0x123456)); assert_eq!(memory_size, U256::from(2048)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 217); } #[test] @@ -1167,6 +1237,7 @@ fn mload_uninitialized_memory() { assert_eq!(loaded_value, U256::zero()); assert_eq!(memory_size, U256::from(96)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 17); } #[test] @@ -1178,13 +1249,13 @@ fn call_returns_if_bytecode_empty() { let callee_account = Account::new(U256::from(500000), callee_bytecode); let caller_ops = vec![ - Operation::Push32(U256::from(100_000)), // gas - Operation::Push32(callee_address_u256), // address - Operation::Push32(U256::zero()), // value - Operation::Push32(U256::from(0)), // args_offset - Operation::Push32(U256::from(0)), // args_size - Operation::Push32(U256::from(0)), // ret_offset 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, Operation::Stop, ]; @@ -1340,16 +1411,50 @@ fn nested_calls() { } #[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, +fn calldataload() { + let calldata = vec![ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, + ] + .into(); + let ops = vec![ + Operation::Push32(U256::from(0)), // offset + Operation::CallDataLoad, Operation::Stop, ]; + let mut vm = new_vm_with_ops(&ops); + + vm.current_call_frame_mut().calldata = calldata; + vm.execute(); + + let current_call_frame = vm.current_call_frame_mut(); + + let top_of_stack = current_call_frame.stack.pop().unwrap(); + assert_eq!( + top_of_stack, + U256::from_big_endian(&[ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10 + ]) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 6); +} + +#[test] +fn calldataload_being_set_by_parent() { + let ops = vec![ + Operation::Push32(U256::zero()), // offset + Operation::CallDataLoad, + Operation::Push32(U256::from(0)), // offset + Operation::Mstore, + Operation::Push32(U256::from(32)), // size + Operation::Push32(U256::zero()), // offset + Operation::Return, + ]; - let callee_bytecode = callee_ops + let callee_bytecode = ops .iter() .flat_map(Operation::to_bytecode) .collect::(); @@ -1358,15 +1463,25 @@ fn staticcall_changes_callframe_is_static() { let callee_address_u256 = U256::from(2); let callee_account = Account::new(U256::from(500000), callee_bytecode); + let calldata = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, + ]; + let caller_ops = vec![ + Operation::Push32(U256::from_big_endian(&calldata[..32])), // value + Operation::Push32(U256::from(0)), // offset + Operation::Mstore, 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(32)), // 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, + Operation::Call, + Operation::Stop, ]; let mut vm = new_vm_with_ops_addr_bal( @@ -1381,97 +1496,114 @@ fn staticcall_changes_callframe_is_static() { 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); + let calldata = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, + ]; - assert_eq!(U256::from_big_endian(&return_data), U256::from(0xAAAAAAA)); - assert!(current_call_frame.is_static); + let expected_data = U256::from_big_endian(&calldata[..32]); + + assert_eq!(expected_data, current_call_frame.memory.load(0)); } #[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, - ]; +fn calldatasize() { + let calldata = vec![0x11, 0x22, 0x33].into(); + let ops = vec![Operation::CallDataSize, Operation::Stop]; + let mut vm = new_vm_with_ops(&ops); - let callee_bytecode = callee_ops - .iter() - .flat_map(Operation::to_bytecode) - .collect::(); + vm.current_call_frame_mut().calldata = calldata; - 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()); + vm.execute(); - 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 current_call_frame = vm.current_call_frame_mut(); + let top_of_stack = current_call_frame.stack.pop().unwrap(); + assert_eq!(top_of_stack, U256::from(3)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); +} + +#[test] +fn calldatacopy() { + let calldata = vec![0x11, 0x22, 0x33, 0x44, 0x55].into(); + let ops = vec![ + Operation::Push32(U256::from(2)), // size + Operation::Push32(U256::from(1)), // calldata_offset + Operation::Push32(U256::from(0)), // dest_offset + Operation::CallDataCopy, + Operation::Stop, ]; + let mut vm = new_vm_with_ops(&ops); - 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.current_call_frame_mut().calldata = calldata; - vm.db.add_account(callee_address, callee_account); + vm.execute(); 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()); + let memory = current_call_frame.memory.load_range(0, 2); + assert_eq!(memory, vec![0x22, 0x33]); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 18); +} - vm.execute(); +#[test] +fn returndatasize() { + let returndata = vec![0xAA, 0xBB, 0xCC].into(); + let ops = vec![Operation::ReturnDataSize, Operation::Stop]; + let mut vm = new_vm_with_ops(&ops); - 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), - }; + vm.current_call_frame_mut().returndata = returndata; - assert_eq!(storage_slot, Some(slot)); + vm.execute(); - // --- CALL --- changes account 2 storage + let current_call_frame = vm.current_call_frame_mut(); + let top_of_stack = current_call_frame.stack.pop().unwrap(); + assert_eq!(top_of_stack, U256::from(3)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); +} - let callee_return_value = U256::from(0xAAAAAAA); - let callee_ops = [ - Operation::Push32(callee_return_value), // value - Operation::Push32(U256::zero()), // key - Operation::Sstore, +#[test] +fn returndatacopy() { + let returndata = vec![0xAA, 0xBB, 0xCC, 0xDD].into(); + let ops = vec![ + Operation::Push32(U256::from(2)), // size + Operation::Push32(U256::from(1)), // returndata_offset + Operation::Push32(U256::from(0)), // dest_offset + Operation::ReturnDataCopy, Operation::Stop, ]; + let mut vm = new_vm_with_ops(&ops); - let callee_bytecode = callee_ops - .iter() - .flat_map(Operation::to_bytecode) - .collect::(); + vm.current_call_frame_mut().returndata = returndata; + + vm.execute(); + + let current_call_frame = vm.current_call_frame_mut(); + let memory = current_call_frame.memory.load_range(0, 2); + assert_eq!(memory, vec![0xBB, 0xCC]); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 18); +} + +#[test] +fn returndatacopy_being_set_by_parent() { + let callee_bytecode = callee_return_bytecode(U256::from(0xAAAAAAA)); 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(32)), // ret_size 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(2)), // callee address Operation::Push32(U256::from(100_000)), // gas Operation::Call, + Operation::Push32(U256::from(32)), // size + Operation::Push32(U256::from(0)), // returndata offset + Operation::Push32(U256::from(0)), // dest offset + Operation::ReturnDataCopy, + Operation::Stop, ]; let mut vm = new_vm_with_ops_addr_bal( @@ -1482,537 +1614,61 @@ fn delegatecall_changes_own_storage_and_regular_call_doesnt() { 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), - }; + let current_call_frame = vm.current_call_frame_mut(); + + let result = current_call_frame.memory.load(0); - assert_eq!(storage_slot, Some(slot)); + assert_eq!(result, U256::from(0xAAAAAAA)); } #[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()); +fn blockhash_op() { + let block_number = 1_u8; + let block_hash = 12345678; + let current_block_number = 3_u8; + let expected_block_hash = U256::from(block_hash); - 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 operations = [ + Operation::Push((1, U256::from(block_number))), + Operation::BlockHash, + 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()); + let mut vm = new_vm_with_ops(&operations); + vm.block_env.number = U256::from(current_block_number); + vm.db + .block_hashes + .insert(U256::from(block_number), H256::from_low_u64_be(block_hash)); 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()) + vm.current_call_frame_mut().stack.pop().unwrap(), + expected_block_hash ); - assert_eq!(current_call_frame.msg_value, U256::from(0)); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 23); +} - // --- CALLCODE --- +#[test] +fn blockhash_same_block_number() { + let block_number = 1_u8; + let block_hash = 12345678; + let current_block_number = block_number; + let expected_block_hash = U256::zero(); - let callee_return_value = U256::from(0xAAAAAAA); - let callee_ops = [ - Operation::Push32(callee_return_value), // value - Operation::Push32(U256::zero()), // key - Operation::Sstore, + let operations = [ + Operation::Push((1, U256::from(block_number))), + Operation::BlockHash, 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 = [ - Operation::Push32(U256::one()), - Operation::Push32(U256::from(100)), - Operation::Pop, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::one()); -} - -// TODO: when adding error handling this should return an error, not panic -#[test] -#[should_panic] -fn pop_on_empty_stack() { - let operations = [Operation::Pop, Operation::Stop]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::one()); -} - -#[test] -fn pc_op() { - let operations = [Operation::PC, Operation::Stop]; - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::from(0)); -} - -#[test] -fn pc_op_with_push_offset() { - let operations = [ - Operation::Push32(U256::one()), - Operation::PC, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::from(33)); -} - -#[test] -fn jump_op() { - let operations = [ - Operation::Push32(U256::from(35)), - Operation::Jump, - Operation::Stop, // should skip this one - Operation::Jumpdest, - Operation::Push32(U256::from(10)), - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::from(10)); - assert_eq!(vm.current_call_frame_mut().pc(), 70); -} - -#[test] -#[should_panic] -fn jump_not_jumpdest_position() { - let operations = [ - Operation::Push32(U256::from(36)), - Operation::Jump, - Operation::Stop, - Operation::Push32(U256::from(10)), - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - assert_eq!(vm.current_call_frame_mut().pc, 35); -} - -#[test] -#[should_panic] -fn jump_position_bigger_than_program_bytecode_size() { - let operations = [ - Operation::Push32(U256::from(5000)), - Operation::Jump, - Operation::Stop, - Operation::Push32(U256::from(10)), - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - assert_eq!(vm.current_call_frame_mut().pc(), 35); -} - -#[test] -fn jumpi_not_zero() { - let operations = [ - Operation::Push32(U256::one()), - Operation::Push32(U256::from(68)), - Operation::Jumpi, - Operation::Stop, // should skip this one - Operation::Jumpdest, - Operation::Push32(U256::from(10)), - Operation::Stop, - ]; - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::from(10)); -} - -#[test] -fn jumpi_for_zero() { - let operations = [ - Operation::Push32(U256::from(100)), - Operation::Push32(U256::zero()), - Operation::Push32(U256::from(100)), - Operation::Jumpi, - Operation::Stop, - Operation::Jumpdest, - Operation::Push32(U256::from(10)), - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - - vm.execute(); - - assert!(vm.current_call_frame_mut().stack.pop().unwrap() == U256::from(100)); -} - -#[test] -fn calldataload() { - let calldata = vec![ - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, - ] - .into(); - let ops = vec![ - Operation::Push32(U256::from(0)), // offset - Operation::CallDataLoad, - Operation::Stop, - ]; - let mut vm = new_vm_with_ops(&ops); - - vm.current_call_frame_mut().calldata = calldata; - vm.execute(); - - let current_call_frame = vm.current_call_frame_mut(); - - let top_of_stack = current_call_frame.stack.pop().unwrap(); - assert_eq!( - top_of_stack, - U256::from_big_endian(&[ - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, - 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, - 0x0D, 0x0E, 0x0F, 0x10 - ]) - ); -} - -#[test] -fn calldataload_being_set_by_parent() { - let ops = vec![ - Operation::Push32(U256::zero()), // offset - Operation::CallDataLoad, - Operation::Push32(U256::from(0)), // offset - Operation::Mstore, - Operation::Push32(U256::from(32)), // size - Operation::Push32(U256::zero()), // offset - Operation::Return, - ]; - - let callee_bytecode = 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 calldata = [ - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, - ]; - - let caller_ops = vec![ - Operation::Push32(U256::from_big_endian(&calldata[..32])), // value - Operation::Push32(U256::from(0)), // offset - Operation::Mstore, - Operation::Push32(U256::from(32)), // ret_size - Operation::Push32(U256::from(0)), // ret_offset - Operation::Push32(U256::from(32)), // 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, - Operation::Stop, - ]; - - 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 calldata = [ - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, - ]; - - let expected_data = U256::from_big_endian(&calldata[..32]); - - assert_eq!(expected_data, current_call_frame.memory.load(0)); -} - -#[test] -fn calldatasize() { - let calldata = vec![0x11, 0x22, 0x33].into(); - let ops = vec![Operation::CallDataSize, Operation::Stop]; - let mut vm = new_vm_with_ops(&ops); - - vm.current_call_frame_mut().calldata = calldata; - - vm.execute(); - - let current_call_frame = vm.current_call_frame_mut(); - let top_of_stack = current_call_frame.stack.pop().unwrap(); - assert_eq!(top_of_stack, U256::from(3)); -} - -#[test] -fn calldatacopy() { - let calldata = vec![0x11, 0x22, 0x33, 0x44, 0x55].into(); - let ops = vec![ - Operation::Push32(U256::from(2)), // size - Operation::Push32(U256::from(1)), // calldata_offset - Operation::Push32(U256::from(0)), // dest_offset - Operation::CallDataCopy, - Operation::Stop, - ]; - let mut vm = new_vm_with_ops(&ops); - - vm.current_call_frame_mut().calldata = calldata; - - vm.execute(); - - let current_call_frame = vm.current_call_frame_mut(); - let memory = current_call_frame.memory.load_range(0, 2); - println!("{:?}", current_call_frame); - assert_eq!(memory, vec![0x22, 0x33]); -} - -#[test] -fn returndatasize() { - let returndata = vec![0xAA, 0xBB, 0xCC].into(); - let ops = vec![Operation::ReturnDataSize, Operation::Stop]; - let mut vm = new_vm_with_ops(&ops); - - vm.current_call_frame_mut().returndata = returndata; - - vm.execute(); - - let current_call_frame = vm.current_call_frame_mut(); - let top_of_stack = current_call_frame.stack.pop().unwrap(); - assert_eq!(top_of_stack, U256::from(3)); -} - -#[test] -fn returndatacopy() { - let returndata = vec![0xAA, 0xBB, 0xCC, 0xDD].into(); - let ops = vec![ - Operation::Push32(U256::from(2)), // size - Operation::Push32(U256::from(1)), // returndata_offset - Operation::Push32(U256::from(0)), // dest_offset - Operation::ReturnDataCopy, - Operation::Stop, - ]; - let mut vm = new_vm_with_ops(&ops); - - vm.current_call_frame_mut().returndata = returndata; - - vm.execute(); - - let current_call_frame = vm.current_call_frame_mut(); - let memory = current_call_frame.memory.load_range(0, 2); - assert_eq!(memory, vec![0xBB, 0xCC]); -} - -#[test] -fn returndatacopy_being_set_by_parent() { - let callee_bytecode = callee_return_bytecode(U256::from(0xAAAAAAA)); - - let callee_address = Address::from_low_u64_be(U256::from(2).low_u64()); - let callee_account = Account::new(U256::from(500000), callee_bytecode); - - let caller_ops = vec![ - Operation::Push32(U256::from(0)), // ret_offset - Operation::Push32(U256::from(32)), // ret_size - Operation::Push32(U256::from(0)), // args_size - Operation::Push32(U256::from(0)), // args_offset - Operation::Push32(U256::zero()), // value - Operation::Push32(U256::from(2)), // callee address - Operation::Push32(U256::from(100_000)), // gas - Operation::Call, - Operation::Push32(U256::from(32)), // size - Operation::Push32(U256::from(0)), // returndata offset - Operation::Push32(U256::from(0)), // dest offset - Operation::ReturnDataCopy, - Operation::Stop, - ]; - - 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(); - println!("{:?}", current_call_frame); - - let result = current_call_frame.memory.load(0); - - assert_eq!(result, U256::from(0xAAAAAAA)); -} - -#[test] -fn block_hash_op() { - let block_number = 1_u8; - let block_hash = 12345678; - let current_block_number = 3_u8; - let expected_block_hash = U256::from(block_hash); - - let operations = [ - Operation::Push((1, U256::from(block_number))), - Operation::BlockHash, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - vm.block_env.number = U256::from(current_block_number); - vm.db - .block_hashes - .insert(U256::from(block_number), H256::from_low_u64_be(block_hash)); - - vm.execute(); - - assert_eq!( - vm.current_call_frame_mut().stack.pop().unwrap(), - expected_block_hash - ); -} - -#[test] -fn block_hash_same_block_number() { - let block_number = 1_u8; - let block_hash = 12345678; - let current_block_number = block_number; - let expected_block_hash = U256::zero(); - - let operations = [ - Operation::Push((1, U256::from(block_number))), - Operation::BlockHash, - Operation::Stop, - ]; - - let mut vm = new_vm_with_ops(&operations); - vm.block_env.number = U256::from(current_block_number); - vm.db - .block_hashes - .insert(U256::from(block_number), H256::from_low_u64_be(block_hash)); + let mut vm = new_vm_with_ops(&operations); + vm.block_env.number = U256::from(current_block_number); + vm.db + .block_hashes + .insert(U256::from(block_number), H256::from_low_u64_be(block_hash)); vm.execute(); @@ -2020,10 +1676,11 @@ fn block_hash_same_block_number() { vm.current_call_frame_mut().stack.pop().unwrap(), expected_block_hash ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 23); } #[test] -fn block_hash_block_number_not_from_recent_256() { +fn blockhash_block_number_not_from_recent_256() { let block_number = 1_u8; let block_hash = 12345678; let current_block_number = 258; @@ -2047,6 +1704,7 @@ fn block_hash_block_number_not_from_recent_256() { vm.current_call_frame_mut().stack.pop().unwrap(), expected_block_hash ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 23); } #[test] @@ -2064,6 +1722,7 @@ fn coinbase_op() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::from(coinbase_address) ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2078,6 +1737,7 @@ fn timestamp_op() { vm.execute(); assert_eq!(vm.current_call_frame_mut().stack.pop().unwrap(), timestamp); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2095,6 +1755,7 @@ fn number_op() { vm.current_call_frame_mut().stack.pop().unwrap(), block_number ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2112,6 +1773,7 @@ fn prevrandao_op() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::from_big_endian(&prevrandao.0) ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2129,6 +1791,7 @@ fn gaslimit_op() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::from(gas_limit) ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2146,6 +1809,7 @@ fn chain_id_op() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::from(chain_id) ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] @@ -2163,10 +1827,11 @@ fn basefee_op() { vm.current_call_frame_mut().stack.pop().unwrap(), base_fee_per_gas ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] -fn blob_base_fee_op() { +fn blobbasefee_op() { let operations = [Operation::BlobBaseFee, Operation::Stop]; let mut vm = new_vm_with_ops(&operations); @@ -2179,10 +1844,11 @@ fn blob_base_fee_op() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::from(2) ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); } #[test] -fn blob_base_fee_minimun_cost() { +fn blobbasefee_minimum_cost() { let operations = [Operation::BlobBaseFee, Operation::Stop]; let mut vm = new_vm_with_ops(&operations); @@ -2195,6 +1861,179 @@ fn blob_base_fee_minimun_cost() { vm.current_call_frame_mut().stack.pop().unwrap(), U256::one() ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); +} + +#[test] +fn pop_op() { + let operations = [ + Operation::Push32(U256::one()), + Operation::Push32(U256::from(100)), + Operation::Pop, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::one() + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 8); +} + +// TODO: when adding error handling this should return an error, not panic +#[test] +#[should_panic] +fn pop_on_empty_stack() { + let operations = [Operation::Pop, Operation::Stop]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::one() + ); +} + +#[test] +fn pc_op() { + let operations = [Operation::PC, Operation::Stop]; + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from(0) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2); +} + +#[test] +fn pc_op_with_push_offset() { + let operations = [ + Operation::Push32(U256::one()), + Operation::PC, + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from(33) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 5); +} + +#[test] +fn jump_op() { + let operations = [ + Operation::Push32(U256::from(35)), + Operation::Jump, + Operation::Stop, // should skip this one + Operation::Jumpdest, + Operation::Push32(U256::from(10)), + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from(10) + ); + assert_eq!(vm.current_call_frame_mut().pc(), 70); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 15); +} + +#[test] +#[should_panic] +fn jump_not_jumpdest_position() { + let operations = [ + Operation::Push32(U256::from(36)), + Operation::Jump, + Operation::Stop, + Operation::Push32(U256::from(10)), + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + assert_eq!(vm.current_call_frame_mut().pc, 35); +} + +#[test] +#[should_panic] +fn jump_position_bigger_than_program_bytecode_size() { + let operations = [ + Operation::Push32(U256::from(5000)), + Operation::Jump, + Operation::Stop, + Operation::Push32(U256::from(10)), + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + assert_eq!(vm.current_call_frame_mut().pc(), 35); +} + +#[test] +fn jumpi_not_zero() { + let operations = [ + Operation::Push32(U256::one()), + Operation::Push32(U256::from(68)), + Operation::Jumpi, + Operation::Stop, // should skip this one + Operation::Jumpdest, + Operation::Push32(U256::from(10)), + Operation::Stop, + ]; + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from(10) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 20); +} + +#[test] +fn jumpi_for_zero() { + let operations = [ + Operation::Push32(U256::from(100)), + Operation::Push32(U256::zero()), + Operation::Push32(U256::from(100)), + Operation::Jumpi, + Operation::Stop, + Operation::Jumpdest, + Operation::Push32(U256::from(10)), + Operation::Stop, + ]; + + let mut vm = new_vm_with_ops(&operations); + + vm.execute(); + + assert_eq!( + vm.current_call_frame_mut().stack.pop().unwrap(), + U256::from(100) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 19); } #[test] @@ -2317,6 +2156,7 @@ fn log0() { assert_eq!(logs.len(), 1); assert_eq!(logs[0].data, data.to_vec()); assert_eq!(logs[0].topics.len(), 0); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 649); } #[test] @@ -2345,6 +2185,7 @@ fn log1() { assert_eq!(logs.len(), 1); assert_eq!(logs[0].data, data.to_vec()); assert_eq!(logs[0].topics, vec![H32::from_slice(&topic1)]); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 1027); } #[test] @@ -2379,6 +2220,7 @@ fn log2() { logs[0].topics, vec![H32::from_slice(&topic1), H32::from_slice(&topic2)] ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 1405); } #[test] @@ -2420,6 +2262,7 @@ fn log3() { H32::from_slice(&topic3) ] ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 1783); } #[test] @@ -2465,6 +2308,7 @@ fn log4() { H32::from_slice(&topic4) ] ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 2161); } #[test] @@ -2488,6 +2332,7 @@ fn log_with_0_data_size() { assert_eq!(logs.len(), 1); assert_eq!(logs[0].data, Vec::new()); assert_eq!(logs[0].topics.len(), 0); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 393); } #[test] @@ -2534,6 +2379,7 @@ fn log_with_data_in_memory_smaller_than_size() { assert_eq!(logs.len(), 1); assert_eq!(logs[0].data, data); assert_eq!(logs[0].topics.len(), 0); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 649); } #[test] @@ -2567,4 +2413,5 @@ fn multiple_logs_of_different_types() { assert_eq!(logs[1].data, data.to_vec()); assert_eq!(logs[0].topics, vec![H32::from_slice(&topic1)]); assert_eq!(logs[1].topics.len(), 0); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 1664); } diff --git a/crates/levm/tests/vm_tests.rs b/crates/levm/tests/vm_tests.rs index 5d12e31a58..a1db234923 100644 --- a/crates/levm/tests/vm_tests.rs +++ b/crates/levm/tests/vm_tests.rs @@ -1,4 +1,5 @@ use levm::{ + constants::TX_BASE_COST, operations::Operation, primitives::{Address, Bytes, U256}, vm::VM, @@ -203,7 +204,8 @@ fn transient_store() { .get(&(current_call_frame.msg_sender, key)) .unwrap(), value - ) + ); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 106) } #[test] @@ -234,5 +236,6 @@ fn transient_load() { vm.execute(); - assert_eq!(*vm.current_call_frame_mut().stack.last().unwrap(), value) + assert_eq!(*vm.current_call_frame_mut().stack.last().unwrap(), value); + assert_eq!(vm.consumed_gas, TX_BASE_COST + 103) }