diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index f30dfab741..a92363df23 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -53,6 +53,7 @@ pub fn sstore_refund(original: U256, current: U256, new: U256, spec: SpecId) -> } } +#[inline] pub fn create2_cost(len: usize) -> Option { let base = CREATE; // ceil(len / 32.0) @@ -64,6 +65,7 @@ pub fn create2_cost(len: usize) -> Option { Some(gas) } +#[inline] fn log2floor(value: U256) -> u64 { assert!(value != U256::ZERO); let mut l: u64 = 256; @@ -83,6 +85,7 @@ fn log2floor(value: U256) -> u64 { l } +#[inline] pub fn exp_cost(power: U256, spec: SpecId) -> Option { if power == U256::ZERO { Some(EXP) @@ -100,12 +103,14 @@ pub fn exp_cost(power: U256, spec: SpecId) -> Option { } } +#[inline] pub fn verylowcopy_cost(len: u64) -> Option { let wordd = len / 32; let wordr = len % 32; VERYLOW.checked_add(COPY.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?) } +#[inline] pub fn extcodecopy_cost(len: u64, is_cold: bool, spec: SpecId) -> Option { let wordd = len / 32; let wordr = len % 32; @@ -154,12 +159,14 @@ pub fn keccak256_cost(len: u64) -> Option { /// Apply extra gas cost of 2 for every 32-byte chunk of initcode. /// /// This cannot overflow as the initcode length is assumed to be checked. +#[inline] pub fn initcode_cost(len: u64) -> u64 { let wordd = len / 32; let wordr = len % 32; INITCODE_WORD_COST * if wordr == 0 { wordd } else { wordd + 1 } } +#[inline] pub fn sload_cost(is_cold: bool, spec: SpecId) -> u64 { if SpecId::enabled(spec, BERLIN) { if is_cold { @@ -288,6 +295,7 @@ pub fn call_cost( + new_cost(is_call_or_staticcall, is_new, transfers_value, spec) } +#[inline] pub fn hot_cold_cost(is_cold: bool, regular_value: u64, spec: SpecId) -> u64 { if SpecId::enabled(spec, BERLIN) { if is_cold { @@ -300,6 +308,7 @@ pub fn hot_cold_cost(is_cold: bool, regular_value: u64, spec: SpecId) -> u64 { } } +#[inline] fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { if is_call_or_callcode && transfers_value { CALLVALUE @@ -308,6 +317,7 @@ fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { } } +#[inline] fn new_cost(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool, spec: SpecId) -> u64 { if is_call_or_staticcall { // EIP-161: State trie clearing (invariant-preserving alternative) @@ -327,6 +337,7 @@ fn new_cost(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool, sp } } +#[inline] pub fn memory_gas(a: usize) -> u64 { let a = a as u64; MEMORY diff --git a/crates/interpreter/src/gas/mod.rs b/crates/interpreter/src/gas/mod.rs index ad1d8c3721..594de66029 100644 --- a/crates/interpreter/src/gas/mod.rs +++ b/crates/interpreter/src/gas/mod.rs @@ -17,7 +17,9 @@ pub struct Gas { /// Refunded gas. This gas is used only at the end of execution. refunded: i64, } + impl Gas { + #[inline] pub fn new(limit: u64) -> Self { Self { limit, @@ -28,37 +30,44 @@ impl Gas { } } + #[inline] pub fn limit(&self) -> u64 { self.limit } + #[inline] pub fn memory(&self) -> u64 { self.memory } + #[inline] pub fn refunded(&self) -> i64 { self.refunded } + #[inline] pub fn spend(&self) -> u64 { self.all_used_gas } + #[inline] pub fn remaining(&self) -> u64 { self.limit - self.all_used_gas } + #[inline] pub fn erase_cost(&mut self, returned: u64) { self.used -= returned; self.all_used_gas -= returned; } + #[inline] pub fn record_refund(&mut self, refund: i64) { self.refunded += refund; } /// Record an explicit cost. - #[inline(always)] + #[inline] pub fn record_cost(&mut self, cost: u64) -> bool { let (all_used_gas, overflow) = self.all_used_gas.overflowing_add(cost); if overflow || self.limit < all_used_gas { @@ -71,6 +80,7 @@ impl Gas { } /// used in memory_resize! macro to record gas used for memory expansion. + #[inline] pub fn record_memory(&mut self, gas_memory: u64) -> bool { if gas_memory > self.memory { let (all_used_gas, overflow) = self.used.overflowing_add(gas_memory); @@ -83,8 +93,10 @@ impl Gas { true } - /// used in gas_refund! macro to record refund value. + /// Used in the `gas_refund!` macro to record refund value. + /// /// Refund can be negative but self.refunded is always positive. + #[inline] pub fn gas_refund(&mut self, refund: i64) { self.refunded += refund; } diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index 82a6ff7ded..ac30a34408 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -12,6 +12,8 @@ pub struct DummyHost { } impl DummyHost { + /// Create a new dummy host with the given [`Env`]. + #[inline] pub fn new(env: Env) -> Self { Self { env, @@ -19,6 +21,9 @@ impl DummyHost { log: Vec::new(), } } + + /// Clears the storage and logs of the dummy host. + #[inline] pub fn clear(&mut self) { self.storage.clear(); self.log.clear(); @@ -26,10 +31,12 @@ impl DummyHost { } impl Host for DummyHost { + #[inline] fn step(&mut self, _interp: &mut Interpreter) -> InstructionResult { InstructionResult::Continue } + #[inline] fn step_end( &mut self, _interp: &mut Interpreter, @@ -38,30 +45,37 @@ impl Host for DummyHost { InstructionResult::Continue } + #[inline] fn env(&mut self) -> &mut Env { &mut self.env } + #[inline] fn load_account(&mut self, _address: B160) -> Option<(bool, bool)> { Some((true, true)) } + #[inline] fn block_hash(&mut self, _number: U256) -> Option { Some(B256::zero()) } + #[inline] fn balance(&mut self, _address: B160) -> Option<(U256, bool)> { Some((U256::ZERO, false)) } + #[inline] fn code(&mut self, _address: B160) -> Option<(Bytecode, bool)> { Some((Bytecode::default(), false)) } + #[inline] fn code_hash(&mut self, __address: B160) -> Option<(B256, bool)> { Some((KECCAK_EMPTY, false)) } + #[inline] fn sload(&mut self, __address: B160, index: U256) -> Option<(U256, bool)> { match self.storage.entry(index) { Entry::Occupied(entry) => Some((*entry.get(), false)), @@ -72,6 +86,7 @@ impl Host for DummyHost { } } + #[inline] fn sstore( &mut self, _address: B160, @@ -89,6 +104,7 @@ impl Host for DummyHost { Some((U256::ZERO, present, value, is_cold)) } + #[inline] fn log(&mut self, address: B160, topics: Vec, data: Bytes) { self.log.push(Log { address, @@ -97,10 +113,12 @@ impl Host for DummyHost { }) } + #[inline] fn selfdestruct(&mut self, _address: B160, _target: B160) -> Option { panic!("Selfdestruct is not supported for this host") } + #[inline] fn create( &mut self, _inputs: &mut CreateInputs, @@ -108,6 +126,7 @@ impl Host for DummyHost { panic!("Create is not supported for this host") } + #[inline] fn call(&mut self, _input: &mut CallInputs) -> (InstructionResult, Gas, Bytes) { panic!("Call is not supported for this host") } diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index b988c645fb..fe7da8fa33 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -46,7 +46,21 @@ pub enum InstructionResult { } impl InstructionResult { - pub fn is_error(&self) -> bool { + /// Returns whether the result is a success. + #[inline] + pub fn is_ok(self) -> bool { + matches!(self, crate::return_ok!()) + } + + /// Returns whether the result is a revert. + #[inline] + pub fn is_revert(self) -> bool { + matches!(self, crate::return_revert!()) + } + + /// Returns whether the result is an error. + #[inline] + pub fn is_error(self) -> bool { matches!( self, Self::OutOfGas @@ -87,11 +101,13 @@ pub enum SuccessOrHalt { impl SuccessOrHalt { /// Returns true if the transaction returned successfully without halts. - pub fn is_success(&self) -> bool { + #[inline] + pub fn is_success(self) -> bool { matches!(self, SuccessOrHalt::Success(_)) } /// Returns the [Eval] value if this a successful result + #[inline] pub fn to_success(self) -> Option { match self { SuccessOrHalt::Success(eval) => Some(eval), @@ -100,16 +116,19 @@ impl SuccessOrHalt { } /// Returns true if the transaction reverted. - pub fn is_revert(&self) -> bool { + #[inline] + pub fn is_revert(self) -> bool { matches!(self, SuccessOrHalt::Revert) } /// Returns true if the EVM has experienced an exceptional halt - pub fn is_halt(&self) -> bool { + #[inline] + pub fn is_halt(self) -> bool { matches!(self, SuccessOrHalt::Halt(_)) } /// Returns the [Halt] value the EVM has experienced an exceptional halt + #[inline] pub fn to_halt(self) -> Option { match self { SuccessOrHalt::Halt(halt) => Some(halt), diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 88d453d8f5..abf1954b69 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -24,14 +24,12 @@ pub(super) fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec pop!(interpreter, index, value); let index = as_usize_or_fail!(interpreter, index); memory_resize!(interpreter, index, 1); - let value = value.as_le_bytes()[0]; - // Safety: we resized our memory two lines above. - unsafe { interpreter.memory.set_byte(index, value) } + interpreter.memory.set_byte(index, value.byte(0)) } pub(super) fn msize(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.memory.effective_len())); + push!(interpreter, U256::from(interpreter.memory.len())); } // EIP-5656: MCOPY - Memory copying instruction diff --git a/crates/interpreter/src/interpreter/analysis.rs b/crates/interpreter/src/interpreter/analysis.rs index 5828e31dd1..9fec465da8 100644 --- a/crates/interpreter/src/interpreter/analysis.rs +++ b/crates/interpreter/src/interpreter/analysis.rs @@ -71,6 +71,7 @@ pub struct BytecodeLocked { } impl Default for BytecodeLocked { + #[inline] fn default() -> Self { Bytecode::default() .try_into() @@ -81,6 +82,7 @@ impl Default for BytecodeLocked { impl TryFrom for BytecodeLocked { type Error = (); + #[inline] fn try_from(bytecode: Bytecode) -> Result { if let BytecodeState::Analysed { len, jump_map } = bytecode.state { Ok(BytecodeLocked { @@ -96,21 +98,31 @@ impl TryFrom for BytecodeLocked { } impl BytecodeLocked { + /// Returns a raw pointer to the underlying byte slice. + #[inline] pub fn as_ptr(&self) -> *const u8 { self.bytecode.as_ptr() } + + /// Returns the length of the bytecode. + #[inline] pub fn len(&self) -> usize { self.len } - pub fn hash(&self) -> B256 { - self.hash - } - + /// Returns whether the bytecode is empty. + #[inline] pub fn is_empty(&self) -> bool { self.len == 0 } + /// Returns the hash of the bytecode. + #[inline] + pub fn hash(&self) -> B256 { + self.hash + } + + #[inline] pub fn unlock(self) -> Bytecode { Bytecode { bytecode: self.bytecode, @@ -121,14 +133,21 @@ impl BytecodeLocked { }, } } + + /// Returns the bytecode as a byte slice. + #[inline] pub fn bytecode(&self) -> &[u8] { - self.bytecode.as_ref() + &self.bytecode } + /// Returns the original bytecode as a byte slice. + #[inline] pub fn original_bytecode_slice(&self) -> &[u8] { - &self.bytecode.as_ref()[..self.len] + &self.bytecode[..self.len] } + /// Returns a reference to the jump map. + #[inline] pub fn jump_map(&self) -> &JumpMap { &self.jump_map } diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index 39dd48fb47..f82ea0a76b 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -18,6 +18,8 @@ pub struct Contract { } impl Contract { + /// Instantiates a new contract by analyzing the given bytecode. + #[inline] pub fn new(input: Bytes, bytecode: Bytecode, address: B160, caller: B160, value: U256) -> Self { let bytecode = to_analysed(bytecode).try_into().expect("it is analyzed"); @@ -30,7 +32,8 @@ impl Contract { } } - /// Create new contract from environment + /// Creates a new contract from the given [`Env`]. + #[inline] pub fn new_env(env: &Env, bytecode: Bytecode) -> Self { let contract_address = match env.tx.transact_to { TransactTo::Call(caller) => caller, @@ -45,10 +48,8 @@ impl Contract { ) } - pub fn is_valid_jump(&self, pos: usize) -> bool { - self.bytecode.jump_map().is_valid(pos) - } - + /// Creates a new contract from the given [`CallContext`]. + #[inline] pub fn new_with_context(input: Bytes, bytecode: Bytecode, call_context: &CallContext) -> Self { Self::new( input, @@ -58,4 +59,10 @@ impl Contract { call_context.apparent_value, ) } + + /// Returns whether the given position is a valid jump destination. + #[inline] + pub fn is_valid_jump(&self, pos: usize) -> bool { + self.bytecode.jump_map().is_valid(pos) + } } diff --git a/crates/interpreter/src/interpreter/memory.rs b/crates/interpreter/src/interpreter/memory.rs index 5502fbbfd7..73fedf0e16 100644 --- a/crates/interpreter/src/interpreter/memory.rs +++ b/crates/interpreter/src/interpreter/memory.rs @@ -13,6 +13,7 @@ pub struct Memory { } impl Default for Memory { + #[inline] fn default() -> Self { Memory::new() } @@ -20,69 +21,78 @@ impl Default for Memory { impl Memory { /// Create a new memory with the given limit. + #[inline] pub fn new() -> Self { Self { data: Vec::with_capacity(4 * 1024), // took it from evmone } } + #[deprecated = "Use `len` instead"] + #[doc(hidden)] + #[inline] pub fn effective_len(&self) -> usize { - self.data.len() + self.len() } - /// Get the length of the current memory range. + /// Returns the length of the current memory range. + #[inline] pub fn len(&self) -> usize { self.data.len() } - /// Return true if current effective memory range is zero. + /// Returns true if current memory range length is zero. + #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } - /// Return the full memory. + /// Return a reference to the full memory. + #[inline] pub fn data(&self) -> &Vec { &self.data } /// Consumes the type and returns the full memory. + #[inline] pub fn into_data(self) -> Vec { self.data } /// Shrinks the capacity of the data buffer as much as possible. + #[inline] pub fn shrink_to_fit(&mut self) { self.data.shrink_to_fit() } - /// Resize the memory. asume that we already checked if - /// we have enought gas to resize this vector and that we made new_size as multiply of 32 + /// Resizes the stack in-place so that then length is equal to `new_size`. + /// + /// `new_size` should be a multiple of 32. + #[inline] pub fn resize(&mut self, new_size: usize) { self.data.resize(new_size, 0); } - /// Get memory region at given offset. Dont check offset and size - #[inline(always)] + /// Returns a byte slice of the memory region at the given offset. + #[inline] pub fn get_slice(&self, offset: usize, size: usize) -> &[u8] { &self.data[offset..offset + size] } - /// Set memory region at given offset - /// - /// # Safety - /// The caller is responsible for checking the offset and value - #[inline(always)] - pub unsafe fn set_byte(&mut self, index: usize, byte: u8) { - *self.data.get_mut(index).unwrap() = byte; + /// Sets the `byte` at the given `index`. + #[inline] + pub fn set_byte(&mut self, index: usize, byte: u8) { + self.data[index] = byte; } - #[inline(always)] + /// Sets the given `value` to the memory region at the given `offset`. + #[inline] pub fn set_u256(&mut self, index: usize, value: U256) { self.data[index..index + 32].copy_from_slice(&value.to_be_bytes::<{ U256::BYTES }>()); } - /// Set memory region at given offset. The offset and value are already checked - #[inline(always)] + /// Set memory region at given `offset`. + #[inline] pub fn set(&mut self, offset: usize, value: &[u8]) { if !value.is_empty() { self.data[offset..(value.len() + offset)].copy_from_slice(value); @@ -91,7 +101,7 @@ impl Memory { /// Set memory from data. Our memory offset+len is expected to be correct but we /// are doing bound checks on data/data_offeset/len and zeroing parts that is not copied. - #[inline(always)] + #[inline] pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) { if data_offset >= data.len() { // nulify all memory slots @@ -112,9 +122,6 @@ impl Memory { } /// In memory copy given a src, dst, and length - /// - /// # Safety - /// The caller is responsible to check that we resized memory properly. #[inline(always)] pub fn copy(&mut self, dst: usize, src: usize, len: usize) { self.data.copy_within(src..src + len, dst); @@ -130,9 +137,8 @@ pub(crate) fn next_multiple_of_32(x: usize) -> Option { #[cfg(test)] mod tests { - use crate::Memory; - use super::next_multiple_of_32; + use crate::Memory; #[test] fn test_copy() { diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index e27b33e144..e0a5f58265 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -2,6 +2,7 @@ use crate::primitives::{B256, U256}; use crate::{alloc::vec::Vec, InstructionResult}; use core::fmt; +/// The EVM stack limit, in number of words. pub const STACK_LIMIT: usize = 1024; /// EVM stack. @@ -32,93 +33,86 @@ impl Default for Stack { } impl Stack { - /// Create a new stack with given limit. + /// Instantiate a new stack with the [default stack limit][STACK_LIMIT]. #[inline] pub fn new() -> Self { Self { - // Safety: A lot of functions assumes that capacity is STACK_LIMIT + // Safety: A lot of functions assume that capacity is STACK_LIMIT data: Vec::with_capacity(STACK_LIMIT), } } + /// Returns the length of the stack in words. #[inline] - /// Stack length. pub fn len(&self) -> usize { self.data.len() } + /// Returns whether the stack is empty. #[inline] - /// Whether the stack is empty. pub fn is_empty(&self) -> bool { self.data.is_empty() } + /// Returns the underlying data of the stack. #[inline] - /// Stack data. pub fn data(&self) -> &Vec { &self.data } - #[inline(always)] + /// Remove the topmost value from the stack. If the stack is empty, returns a `StackUnderflow` + /// error. + #[inline] pub fn reduce_one(&mut self) -> Option { - let len = self.data.len(); - if len < 1 { - return Some(InstructionResult::StackUnderflow); - } - unsafe { - self.data.set_len(len - 1); - } - None + self.pop().err() } + /// Removes the topmost element from the stack and returns it, or `StackUnderflow` if it is + /// empty. #[inline] - /// Pop a value from the stack. If the stack is already empty, returns the - /// `StackUnderflow` error. pub fn pop(&mut self) -> Result { self.data.pop().ok_or(InstructionResult::StackUnderflow) } - #[inline(always)] - /// Pops a value from the stack, returning it. + /// Removes the topmost element from the stack and returns it. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn pop_unsafe(&mut self) -> U256 { - let mut len = self.data.len(); - len -= 1; - self.data.set_len(len); - *self.data.get_unchecked(len) + self.data.pop().unwrap_unchecked() } - #[inline(always)] /// Peeks the top of the stack. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn top_unsafe(&mut self) -> &mut U256 { let len = self.data.len(); self.data.get_unchecked_mut(len - 1) } - #[inline(always)] /// Pop the topmost value, returning the value and the new topmost value. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn pop_top_unsafe(&mut self) -> (U256, &mut U256) { - let mut len = self.data.len(); - let pop = *self.data.get_unchecked(len - 1); - len -= 1; - self.data.set_len(len); - - (pop, self.data.get_unchecked_mut(len - 1)) + let pop = self.pop_unsafe(); + let top = self.top_unsafe(); + (pop, top) } - #[inline(always)] /// Pops 2 values from the stack and returns them, in addition to the new topmost value. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn pop2_top_unsafe(&mut self) -> (U256, U256, &mut U256) { let mut len = self.data.len(); let pop1 = *self.data.get_unchecked(len - 1); @@ -129,11 +123,12 @@ impl Stack { (pop1, pop2, self.data.get_unchecked_mut(len - 1)) } - #[inline(always)] /// Pops 2 values from the stack. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn pop2_unsafe(&mut self) -> (U256, U256) { let mut len = self.data.len(); len -= 2; @@ -144,11 +139,12 @@ impl Stack { ) } - #[inline(always)] /// Pops 3 values from the stack. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. + #[inline(always)] pub unsafe fn pop3_unsafe(&mut self) -> (U256, U256, U256) { let mut len = self.data.len(); len -= 3; @@ -164,7 +160,8 @@ impl Stack { /// Pops 4 values from the stack. /// /// # Safety - /// The caller is responsible to check length of array + /// + /// The caller is responsible for checking the length of the stack. pub unsafe fn pop4_unsafe(&mut self) -> (U256, U256, U256, U256) { let mut len = self.data.len(); len -= 4; @@ -177,9 +174,9 @@ impl Stack { ) } - #[inline] /// Push a new value into the stack. If it will exceed the stack limit, /// returns `StackOverflow` error and leaves the stack unchanged. + #[inline] pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { if self.data.len() + 1 > STACK_LIMIT { return Err(InstructionResult::StackOverflow); @@ -188,9 +185,11 @@ impl Stack { Ok(()) } + /// Push a new value onto the stack. + /// + /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack + /// unchanged. #[inline] - /// Push a new value into the stack. If it will exceed the stack limit, - /// returns `StackOverflow` error and leaves the stack unchanged. pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { if self.data.len() + 1 > STACK_LIMIT { return Err(InstructionResult::StackOverflow); @@ -199,10 +198,10 @@ impl Stack { Ok(()) } - #[inline] /// Peek a value at given index for the stack, where the top of /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. + #[inline] pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { Ok(self.data[self.data.len() - no_from_top - 1]) @@ -211,6 +210,7 @@ impl Stack { } } + /// Duplicates the `N`th value from the top of the stack. #[inline(always)] pub fn dup(&mut self) -> Option { let len = self.data.len(); @@ -228,6 +228,7 @@ impl Stack { } } + /// Swaps the topmost value with the `N`th value from the top. #[inline(always)] pub fn swap(&mut self) -> Option { let len = self.data.len(); @@ -239,7 +240,10 @@ impl Stack { None } - /// push slice onto memory it is expected to be max 32 bytes and be contains inside B256 + /// Push a slice of bytes of `N` length onto the stack. + /// + /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack + /// unchanged. #[inline(always)] pub fn push_slice(&mut self, slice: &[u8]) -> Option { let new_len = self.data.len() + 1; @@ -296,10 +300,10 @@ impl Stack { None } - #[inline] /// Set a value at given index for the stack, where the top of the /// stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. + #[inline] pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), InstructionResult> { if self.data.len() > no_from_top { let len = self.data.len(); diff --git a/crates/primitives/src/bytecode.rs b/crates/primitives/src/bytecode.rs index 1cd8aa8f9b..4890726e8e 100644 --- a/crates/primitives/src/bytecode.rs +++ b/crates/primitives/src/bytecode.rs @@ -1,5 +1,5 @@ use crate::{keccak256, B256, KECCAK_EMPTY}; -use alloc::{sync::Arc, vec, vec::Vec}; +use alloc::{sync::Arc, vec::Vec}; use bitvec::prelude::{bitvec, Lsb0}; use bitvec::vec::BitVec; use bytes::Bytes; @@ -11,16 +11,19 @@ pub struct JumpMap(pub Arc>); impl JumpMap { /// Get the raw bytes of the jump map + #[inline] pub fn as_slice(&self) -> &[u8] { self.0.as_raw_slice() } /// Construct a jump map from raw bytes + #[inline] pub fn from_slice(slice: &[u8]) -> Self { Self(Arc::new(BitVec::from_slice(slice))) } /// Check if `pc` is a valid jump destination. + #[inline] pub fn is_valid(&self, pc: usize) -> bool { pc < self.0.len() && self.0[pc] } @@ -44,16 +47,18 @@ pub struct Bytecode { } impl Default for Bytecode { + #[inline] fn default() -> Self { Bytecode::new() } } impl Bytecode { - /// Create [`Bytecode`] with one STOP opcode. + /// Creates a new [`Bytecode`] with exactly one STOP opcode. + #[inline] pub fn new() -> Self { Bytecode { - bytecode: vec![0].into(), + bytecode: Bytes::from_static(&[0]), hash: KECCAK_EMPTY, state: BytecodeState::Analysed { len: 0, @@ -62,6 +67,8 @@ impl Bytecode { } } + /// Creates a new raw [`Bytecode`]. + #[inline] pub fn new_raw(bytecode: Bytes) -> Self { let hash = if bytecode.is_empty() { KECCAK_EMPTY @@ -75,11 +82,15 @@ impl Bytecode { } } - /// Create new raw Bytecode with hash + /// Creates a new raw Bytecode with the given hash. /// /// # Safety - /// Hash need to be appropriate keccak256 over bytecode. + /// + /// The given `hash` has to be equal to the [`keccak256`] hash of the given `bytecode`. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] pub unsafe fn new_raw_with_hash(bytecode: Bytes, hash: B256) -> Self { + debug_assert_eq!(keccak256(&bytecode), hash, "Invalid bytecode hash"); Self { bytecode, hash, @@ -90,8 +101,10 @@ impl Bytecode { /// Create new checked bytecode /// /// # Safety + /// /// Bytecode need to end with STOP (0x00) opcode as checked bytecode assumes /// that it is safe to iterate over bytecode without checking lengths + #[inline] pub unsafe fn new_checked(bytecode: Bytes, len: usize, hash: Option) -> Self { let hash = match hash { None if len == 0 => KECCAK_EMPTY, @@ -105,10 +118,14 @@ impl Bytecode { } } + /// Returns a reference to the bytecode. + #[inline] pub fn bytes(&self) -> &Bytes { &self.bytecode } + /// Returns a reference to the original bytecode. + #[inline] pub fn original_bytes(&self) -> Bytes { match self.state { BytecodeState::Raw => self.bytecode.clone(), @@ -118,28 +135,31 @@ impl Bytecode { } } - pub fn hash(&self) -> B256 { - self.hash + /// Returns the length of the bytecode. + #[inline] + pub fn len(&self) -> usize { + match self.state { + BytecodeState::Raw => self.bytecode.len(), + BytecodeState::Checked { len, .. } | BytecodeState::Analysed { len, .. } => len, + } } - pub fn state(&self) -> &BytecodeState { - &self.state + /// Returns whether the bytecode is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 } - pub fn is_empty(&self) -> bool { - match self.state { - BytecodeState::Raw => self.bytecode.is_empty(), - BytecodeState::Checked { len } => len == 0, - BytecodeState::Analysed { len, .. } => len == 0, - } + /// Returns the hash of the bytecode. + #[inline] + pub fn hash(&self) -> B256 { + self.hash } - pub fn len(&self) -> usize { - match self.state { - BytecodeState::Raw => self.bytecode.len(), - BytecodeState::Checked { len, .. } => len, - BytecodeState::Analysed { len, .. } => len, - } + /// Returns the [`BytecodeState`]. + #[inline] + pub fn state(&self) -> &BytecodeState { + &self.state } pub fn to_checked(self) -> Self {