diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs
index 57afa405aa..257b93c80b 100644
--- a/crates/interpreter/src/host.rs
+++ b/crates/interpreter/src/host.rs
@@ -2,6 +2,7 @@ use crate::primitives::Bytecode;
use crate::{
primitives::{Address, Bytes, Env, B256, U256},
CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, SelfDestructResult,
+ SharedMemory,
};
use alloc::vec::Vec;
pub use dummy::DummyHost;
@@ -51,7 +52,12 @@ pub trait Host {
fn create(
&mut self,
inputs: &mut CreateInputs,
+ shared_memory: &mut SharedMemory,
) -> (InstructionResult, Option
, Gas, Bytes);
/// Invoke a call operation.
- fn call(&mut self, input: &mut CallInputs) -> (InstructionResult, Gas, Bytes);
+ fn call(
+ &mut self,
+ input: &mut CallInputs,
+ shared_memory: &mut SharedMemory,
+ ) -> (InstructionResult, Gas, Bytes);
}
diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs
index 72b7699da2..ed6b768286 100644
--- a/crates/interpreter/src/host/dummy.rs
+++ b/crates/interpreter/src/host/dummy.rs
@@ -2,6 +2,7 @@ use crate::primitives::{hash_map::Entry, Bytecode, Bytes, HashMap, U256};
use crate::{
primitives::{Address, Env, Log, B256, KECCAK_EMPTY},
CallInputs, CreateInputs, Gas, Host, InstructionResult, Interpreter, SelfDestructResult,
+ SharedMemory,
};
use alloc::vec::Vec;
@@ -137,12 +138,17 @@ impl Host for DummyHost {
fn create(
&mut self,
_inputs: &mut CreateInputs,
+ _shared_memory: &mut SharedMemory,
) -> (InstructionResult, Option, Gas, Bytes) {
panic!("Create is not supported for this host")
}
#[inline]
- fn call(&mut self, _input: &mut CallInputs) -> (InstructionResult, Gas, Bytes) {
+ fn call(
+ &mut self,
+ _input: &mut CallInputs,
+ _shared_memory: &mut SharedMemory,
+ ) -> (InstructionResult, Gas, Bytes) {
panic!("Call is not supported for this host")
}
}
diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs
index 9c28a1af97..ed5a3086cb 100644
--- a/crates/interpreter/src/instructions/control.rs
+++ b/crates/interpreter/src/instructions/control.rs
@@ -53,7 +53,7 @@ fn return_inner(interpreter: &mut Interpreter, result: InstructionResult) {
// important: offset must be ignored if len is zero
if len != 0 {
let offset = as_usize_or_fail!(interpreter, offset);
- memory_resize!(interpreter, offset, len);
+ shared_memory_resize!(interpreter, offset, len);
interpreter.return_offset = offset;
}
interpreter.return_len = len;
diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs
index babeba926c..928c6b5000 100644
--- a/crates/interpreter/src/instructions/host.rs
+++ b/crates/interpreter/src/instructions/host.rs
@@ -109,11 +109,11 @@ pub fn extcodecopy(interpreter: &mut Interpreter, host: &mu
}
let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
let code_offset = min(as_usize_saturated!(code_offset), code.len());
- memory_resize!(interpreter, memory_offset, len);
+ shared_memory_resize!(interpreter, memory_offset, len);
// Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it
interpreter
- .memory
+ .shared_memory
.set_data(memory_offset, code_offset, len, code.bytes());
}
@@ -197,8 +197,8 @@ pub fn log(interpreter: &mut Interpreter, host: &mut H)
Bytes::new()
} else {
let offset = as_usize_or_fail!(interpreter, offset);
- memory_resize!(interpreter, offset, len);
- Bytes::copy_from_slice(interpreter.memory.slice(offset, len))
+ shared_memory_resize!(interpreter, offset, len);
+ Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len))
};
if interpreter.stack.len() < N {
@@ -273,8 +273,8 @@ pub fn prepare_create_inputs(
}
let code_offset = as_usize_or_fail!(interpreter, code_offset);
- memory_resize!(interpreter, code_offset, len);
- Bytes::copy_from_slice(interpreter.memory.slice(code_offset, len))
+ shared_memory_resize!(interpreter, code_offset, len);
+ Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len))
};
let scheme = if IS_CREATE2 {
@@ -315,7 +315,8 @@ pub fn create(
return;
};
- let (return_reason, address, gas, return_data) = host.create(&mut create_input);
+ let (return_reason, address, gas, return_data) =
+ host.create(&mut create_input, interpreter.shared_memory);
interpreter.return_data_buffer = match return_reason {
// Save data to return data buffer if the create reverted
@@ -399,8 +400,8 @@ fn prepare_call_inputs(
let in_len = as_usize_or_fail!(interpreter, in_len);
let input = if in_len != 0 {
let in_offset = as_usize_or_fail!(interpreter, in_offset);
- memory_resize!(interpreter, in_offset, in_len);
- Bytes::copy_from_slice(interpreter.memory.slice(in_offset, in_len))
+ shared_memory_resize!(interpreter, in_offset, in_len);
+ Bytes::copy_from_slice(interpreter.shared_memory.slice(in_offset, in_len))
} else {
Bytes::new()
};
@@ -408,7 +409,7 @@ fn prepare_call_inputs(
*result_len = as_usize_or_fail!(interpreter, out_len);
*result_offset = if *result_len != 0 {
let out_offset = as_usize_or_fail!(interpreter, out_offset);
- memory_resize!(interpreter, out_offset, *result_len);
+ shared_memory_resize!(interpreter, out_offset, *result_len);
out_offset
} else {
usize::MAX //unrealistic value so we are sure it is not used
@@ -535,10 +536,9 @@ pub fn call_inner(
};
// Call host to interact with target contract
- let (reason, gas, return_data) = host.call(&mut call_input);
+ let (reason, gas, return_data) = host.call(&mut call_input, interpreter.shared_memory);
interpreter.return_data_buffer = return_data;
-
let target_len = min(out_len, interpreter.return_data_buffer.len());
match reason {
@@ -549,7 +549,7 @@ pub fn call_inner(
interpreter.gas.record_refund(gas.refunded());
}
interpreter
- .memory
+ .shared_memory
.set(out_offset, &interpreter.return_data_buffer[..target_len]);
push!(interpreter, U256::from(1));
}
@@ -558,7 +558,7 @@ pub fn call_inner(
interpreter.gas.erase_cost(gas.remaining());
}
interpreter
- .memory
+ .shared_memory
.set(out_offset, &interpreter.return_data_buffer[..target_len]);
push!(interpreter, U256::ZERO);
}
diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs
index 714a83368b..41063fca4a 100644
--- a/crates/interpreter/src/instructions/macros.rs
+++ b/crates/interpreter/src/instructions/macros.rs
@@ -50,18 +50,12 @@ macro_rules! gas_or_fail {
};
}
-macro_rules! memory_resize {
+macro_rules! shared_memory_resize {
($interp:expr, $offset:expr, $len:expr) => {
if let Some(new_size) =
- crate::interpreter::memory::next_multiple_of_32($offset.saturating_add($len))
+ crate::interpreter::shared_memory::next_multiple_of_32($offset.saturating_add($len))
{
- #[cfg(feature = "memory_limit")]
- if new_size > ($interp.memory_limit as usize) {
- $interp.instruction_result = InstructionResult::MemoryLimitOOG;
- return;
- }
-
- if new_size > $interp.memory.len() {
+ if new_size > $interp.shared_memory.len() {
if crate::USE_GAS {
let num_bytes = new_size / 32;
if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) {
@@ -69,7 +63,7 @@ macro_rules! memory_resize {
return;
}
}
- $interp.memory.resize(new_size);
+ $interp.shared_memory.resize(new_size);
}
} else {
$interp.instruction_result = InstructionResult::MemoryOOG;
diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs
index ec3a9bbabb..b3ae69cacb 100644
--- a/crates/interpreter/src/instructions/memory.rs
+++ b/crates/interpreter/src/instructions/memory.rs
@@ -9,10 +9,16 @@ pub fn mload(interpreter: &mut Interpreter, _host: &mut H) {
gas!(interpreter, gas::VERYLOW);
pop!(interpreter, index);
let index = as_usize_or_fail!(interpreter, index);
- memory_resize!(interpreter, index, 32);
+ shared_memory_resize!(interpreter, index, 32);
push!(
interpreter,
- U256::from_be_bytes::<32>(interpreter.memory.slice(index, 32).try_into().unwrap())
+ U256::from_be_bytes::<32>(
+ interpreter
+ .shared_memory
+ .slice(index, 32)
+ .try_into()
+ .unwrap()
+ )
);
}
@@ -20,21 +26,21 @@ pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) {
gas!(interpreter, gas::VERYLOW);
pop!(interpreter, index, value);
let index = as_usize_or_fail!(interpreter, index);
- memory_resize!(interpreter, index, 32);
- interpreter.memory.set_u256(index, value);
+ shared_memory_resize!(interpreter, index, 32);
+ interpreter.shared_memory.set_u256(index, value);
}
pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) {
gas!(interpreter, gas::VERYLOW);
pop!(interpreter, index, value);
let index = as_usize_or_fail!(interpreter, index);
- memory_resize!(interpreter, index, 1);
- interpreter.memory.set_byte(index, value.byte(0))
+ shared_memory_resize!(interpreter, index, 1);
+ interpreter.shared_memory.set_byte(index, value.byte(0))
}
pub fn msize(interpreter: &mut Interpreter, _host: &mut H) {
gas!(interpreter, gas::BASE);
- push!(interpreter, U256::from(interpreter.memory.len()));
+ push!(interpreter, U256::from(interpreter.shared_memory.len()));
}
// EIP-5656: MCOPY - Memory copying instruction
@@ -53,7 +59,7 @@ pub fn mcopy(interpreter: &mut Interpreter, _host: &mut H)
let dst = as_usize_or_fail!(interpreter, dst);
let src = as_usize_or_fail!(interpreter, src);
// resize memory
- memory_resize!(interpreter, max(dst, src), len);
+ shared_memory_resize!(interpreter, max(dst, src), len);
// copy memory in place
- interpreter.memory.copy(dst, src, len);
+ interpreter.shared_memory.copy(dst, src, len);
}
diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs
index e4976058ed..9ae285bb1e 100644
--- a/crates/interpreter/src/instructions/system.rs
+++ b/crates/interpreter/src/instructions/system.rs
@@ -12,8 +12,8 @@ pub fn keccak256(interpreter: &mut Interpreter, _host: &mut H) {
KECCAK_EMPTY
} else {
let from = as_usize_or_fail!(interpreter, from);
- memory_resize!(interpreter, from, len);
- crate::primitives::keccak256(interpreter.memory.slice(from, len))
+ shared_memory_resize!(interpreter, from, len);
+ crate::primitives::keccak256(interpreter.shared_memory.slice(from, len))
};
push_b256!(interpreter, hash);
@@ -43,10 +43,10 @@ pub fn codecopy(interpreter: &mut Interpreter, _host: &mut H) {
}
let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
let code_offset = as_usize_saturated!(code_offset);
- memory_resize!(interpreter, memory_offset, len);
+ shared_memory_resize!(interpreter, memory_offset, len);
// Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it
- interpreter.memory.set_data(
+ interpreter.shared_memory.set_data(
memory_offset,
code_offset,
len,
@@ -89,12 +89,15 @@ pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut H) {
}
let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
let data_offset = as_usize_saturated!(data_offset);
- memory_resize!(interpreter, memory_offset, len);
+ shared_memory_resize!(interpreter, memory_offset, len);
// Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it
- interpreter
- .memory
- .set_data(memory_offset, data_offset, len, &interpreter.contract.input);
+ interpreter.shared_memory.set_data(
+ memory_offset,
+ data_offset,
+ len,
+ &interpreter.contract.input,
+ );
}
/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
@@ -121,8 +124,8 @@ pub fn returndatacopy(interpreter: &mut Interpreter, _host:
}
if len != 0 {
let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
- memory_resize!(interpreter, memory_offset, len);
- interpreter.memory.set(
+ shared_memory_resize!(interpreter, memory_offset, len);
+ interpreter.shared_memory.set(
memory_offset,
&interpreter.return_data_buffer[data_offset..data_end],
);
diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs
index d13bd1998a..59d9ae1ba8 100644
--- a/crates/interpreter/src/interpreter.rs
+++ b/crates/interpreter/src/interpreter.rs
@@ -1,6 +1,6 @@
pub mod analysis;
mod contract;
-pub mod memory;
+pub(crate) mod shared_memory;
mod stack;
use crate::primitives::{Bytes, Spec};
@@ -8,7 +8,7 @@ use crate::{alloc::boxed::Box, opcode::eval, Gas, Host, InstructionResult};
pub use analysis::BytecodeLocked;
pub use contract::Contract;
-pub use memory::Memory;
+pub use shared_memory::SharedMemory;
pub use stack::{Stack, STACK_LIMIT};
pub const CALL_STACK_LIMIT: u64 = 1024;
@@ -22,8 +22,8 @@ pub const MAX_CODE_SIZE: usize = 0x6000;
pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE;
#[derive(Debug)]
-pub struct Interpreter {
- /// Contract information and invoking data.
+pub struct Interpreter<'a> {
+ /// Contract information and invoking data
pub contract: Box,
/// The current instruction pointer.
pub instruction_pointer: *const u8,
@@ -32,9 +32,9 @@ pub struct Interpreter {
pub instruction_result: InstructionResult,
/// The gas state.
pub gas: Gas,
- /// The memory.
- pub memory: Memory,
- /// The stack.
+ /// Shared memory.
+ pub shared_memory: &'a mut SharedMemory,
+ /// Stack.
pub stack: Stack,
/// The return data buffer for internal calls.
pub return_data_buffer: Bytes,
@@ -46,43 +46,27 @@ pub struct Interpreter {
pub return_len: usize,
/// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen.
pub is_static: bool,
- /// Memory limit. See [`crate::CfgEnv`].
- #[cfg(feature = "memory_limit")]
- pub memory_limit: u64,
}
-impl Interpreter {
- /// Instantiates a new interpreter.
- #[inline]
- pub fn new(contract: Box, gas_limit: u64, is_static: bool) -> Self {
+impl<'a> Interpreter<'a> {
+ /// Create new interpreter
+ pub fn new(
+ contract: Box,
+ gas_limit: u64,
+ is_static: bool,
+ shared_memory: &'a mut SharedMemory,
+ ) -> Self {
Self {
instruction_pointer: contract.bytecode.as_ptr(),
contract,
- instruction_result: InstructionResult::Continue,
gas: Gas::new(gas_limit),
- memory: Memory::new(),
- stack: Stack::new(),
+ instruction_result: InstructionResult::Continue,
+ is_static,
return_data_buffer: Bytes::new(),
- return_offset: 0,
return_len: 0,
- is_static,
- #[cfg(feature = "memory_limit")]
- memory_limit: u64::MAX,
- }
- }
-
- /// Instantiates a new interpreter with the given memory limit.
- #[cfg(feature = "memory_limit")]
- #[inline]
- pub fn new_with_memory_limit(
- contract: Box,
- gas_limit: u64,
- is_static: bool,
- memory_limit: u64,
- ) -> Self {
- Self {
- memory_limit,
- ..Self::new(contract, gas_limit, is_static)
+ return_offset: 0,
+ shared_memory,
+ stack: Stack::new(),
}
}
@@ -104,12 +88,6 @@ impl Interpreter {
&self.gas
}
- /// Returns a reference to the interpreter's memory.
- #[inline]
- pub fn memory(&self) -> &Memory {
- &self.memory
- }
-
/// Returns a reference to the interpreter's stack.
#[inline]
pub fn stack(&self) -> &Stack {
@@ -179,7 +157,8 @@ impl Interpreter {
if self.return_len == 0 {
&[]
} else {
- self.memory.slice(self.return_offset, self.return_len)
+ self.shared_memory
+ .slice(self.return_offset, self.return_len)
}
}
}
diff --git a/crates/interpreter/src/interpreter/memory.rs b/crates/interpreter/src/interpreter/memory.rs
deleted file mode 100644
index aa3a43fdb0..0000000000
--- a/crates/interpreter/src/interpreter/memory.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-use crate::primitives::U256;
-use alloc::vec::Vec;
-use core::{
- cmp::min,
- fmt,
- ops::{BitAnd, Not},
-};
-
-/// A sequential memory. It uses Rust's `Vec` for internal
-/// representation.
-#[derive(Clone, Eq, PartialEq)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-pub struct Memory {
- data: Vec,
-}
-
-impl fmt::Debug for Memory {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Memory")
- .field("data", &crate::primitives::hex::encode(&self.data))
- .finish()
- }
-}
-
-impl Default for Memory {
- #[inline]
- fn default() -> Self {
- Self {
- data: Vec::with_capacity(4 * 1024), // took it from evmone
- }
- }
-}
-
-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.len()
- }
-
- /// Returns the length of the current memory range.
- #[inline]
- pub fn len(&self) -> usize {
- self.data.len()
- }
-
- /// Returns true if current memory range length is zero.
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// 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()
- }
-
- /// 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);
- }
-
- /// Returns a byte slice of the memory region at the given offset.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn slice(&self, offset: usize, size: usize) -> &[u8] {
- match self.data.get(offset..offset + size) {
- Some(slice) => slice,
- None => debug_unreachable!("slice OOB: {offset}..{size}; len: {}", self.len()),
- }
- }
-
- #[deprecated = "use `slice` instead"]
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn get_slice(&self, offset: usize, size: usize) -> &[u8] {
- self.slice(offset, size)
- }
-
- /// Returns a mutable byte slice of the memory region at the given offset.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn slice_mut(&mut self, offset: usize, size: usize) -> &mut [u8] {
- let _len = self.len();
- match self.data.get_mut(offset..offset + size) {
- Some(slice) => slice,
- None => debug_unreachable!("slice_mut OOB: {offset}..{size}; len: {_len}"),
- }
- }
-
- /// Sets the `byte` at the given `index`.
- ///
- /// Panics when `index` is out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn set_byte(&mut self, index: usize, byte: u8) {
- match self.data.get_mut(index) {
- Some(b) => *b = byte,
- None => debug_unreachable!("set_byte OOB: {index}; len: {}", self.len()),
- }
- }
-
- /// Sets the given `value` to the memory region at the given `offset`.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn set_u256(&mut self, offset: usize, value: U256) {
- self.set(offset, &value.to_be_bytes::<32>());
- }
-
- /// Set memory region at given `offset`.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn set(&mut self, offset: usize, value: &[u8]) {
- if !value.is_empty() {
- self.slice_mut(offset, value.len()).copy_from_slice(value);
- }
- }
-
- /// 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.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) {
- if data_offset >= data.len() {
- // nullify all memory slots
- self.slice_mut(memory_offset, len).fill(0);
- return;
- }
- let data_end = min(data_offset + len, data.len());
- let data_len = data_end - data_offset;
- debug_assert!(data_offset < data.len() && data_end <= data.len());
- let data = unsafe { data.get_unchecked(data_offset..data_end) };
- self.slice_mut(memory_offset, data_len)
- .copy_from_slice(data);
-
- // nullify rest of memory slots
- // Safety: Memory is assumed to be valid. And it is commented where that assumption is made
- self.slice_mut(memory_offset + data_len, len - data_len)
- .fill(0);
- }
-
- /// Copies elements from one part of the memory to another part of itself.
- ///
- /// Panics on out of bounds.
- #[inline(always)]
- #[cfg_attr(debug_assertions, track_caller)]
- pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
- self.data.copy_within(src..src + len, dst);
- }
-}
-
-/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
-#[inline]
-pub(crate) fn next_multiple_of_32(x: usize) -> Option {
- let r = x.bitand(31).not().wrapping_add(1).bitand(31);
- x.checked_add(r)
-}
-
-#[cfg(test)]
-mod tests {
- use super::next_multiple_of_32;
- use crate::Memory;
-
- #[test]
- fn test_copy() {
- // Create a sample memory instance
- let mut memory = Memory::new();
-
- // Set up initial memory data
- let data: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- memory.resize(data.len());
- memory.set_data(0, 0, data.len(), &data);
-
- // Perform a copy operation
- memory.copy(5, 0, 4);
-
- // Verify the copied data
- let copied_data = memory.slice(5, 4);
- assert_eq!(copied_data, &[1, 2, 3, 4]);
- }
-
- #[test]
- fn test_next_multiple_of_32() {
- // next_multiple_of_32 returns x when it is a multiple of 32
- for i in 0..32 {
- let x = i * 32;
- assert_eq!(Some(x), next_multiple_of_32(x));
- }
-
- // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0`
- for x in 0..1024 {
- if x % 32 == 0 {
- continue;
- }
- let next_multiple = x + 32 - (x % 32);
- assert_eq!(Some(next_multiple), next_multiple_of_32(x));
- }
- }
-}
diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs
new file mode 100644
index 0000000000..d076993e9e
--- /dev/null
+++ b/crates/interpreter/src/interpreter/shared_memory.rs
@@ -0,0 +1,258 @@
+use revm_primitives::U256;
+
+use crate::alloc::vec::Vec;
+use core::{
+ cmp::min,
+ fmt,
+ ops::{BitAnd, Not},
+};
+
+/// A sequential memory shared between calls, which uses
+/// a `Vec` for internal representation.
+/// A [SharedMemory] instance should always be obtained using
+/// the `new` static method to ensure memory safety.
+pub struct SharedMemory {
+ /// Shared buffer
+ data: Vec,
+ /// Memory checkpoints for each depth
+ checkpoints: Vec,
+ /// How much memory has been used in the current context
+ current_len: usize,
+}
+
+impl fmt::Debug for SharedMemory {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SharedMemory")
+ .field(
+ "current_slice",
+ &crate::primitives::hex::encode(self.context_memory()),
+ )
+ .finish()
+ }
+}
+
+impl Default for SharedMemory {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl SharedMemory {
+ /// Allocate memory to be shared between calls.
+ /// Memory size is estimated using https://2π.com/22/eth-max-mem
+ /// which depends on transaction [gas_limit].
+ /// Maximum allocation size is 2^32 - 1 bytes;
+ pub fn new() -> Self {
+ Self {
+ data: Vec::with_capacity(4 * 1024), // from evmone
+ checkpoints: Vec::with_capacity(32),
+ current_len: 0,
+ }
+ }
+
+ /// Prepares the shared memory for a new context
+ pub fn new_context_memory(&mut self) {
+ let base_offset = self.last_checkpoint();
+ let new_checkpoint = base_offset + self.current_len;
+
+ self.checkpoints.push(new_checkpoint);
+ self.current_len = 0;
+ }
+
+ /// Prepares the shared memory for returning to the previous context
+ pub fn free_context_memory(&mut self) {
+ if let Some(old_checkpoint) = self.checkpoints.pop() {
+ let last_checkpoint = self.last_checkpoint();
+ self.current_len = old_checkpoint - last_checkpoint;
+ }
+ }
+
+ /// Get the length of the current memory range.
+ #[inline(always)]
+ pub fn len(&self) -> usize {
+ self.current_len
+ }
+
+ /// Returns true if the current memory range is empty.
+ #[inline(always)]
+ pub fn is_empty(&self) -> bool {
+ self.current_len == 0
+ }
+
+ /// Resize the memory. assume that we already checked if:
+ /// - we have enough gas to resize this vector
+ /// - we made new_size as multiply of 32
+ /// - [new_size] is greater than `self.len()`
+ #[inline(always)]
+ pub fn resize(&mut self, new_size: usize) {
+ let last_checkpoint = self.last_checkpoint();
+ let range = last_checkpoint + self.current_len..last_checkpoint + new_size;
+
+ if let Some(available_memory) = self.data.get_mut(range) {
+ available_memory.fill(0);
+ } else {
+ self.data
+ .resize(last_checkpoint + usize::max(new_size, 4 * 1024), 0);
+ }
+
+ self.current_len = new_size;
+ }
+
+ /// Returns a byte slice of the memory region at the given offset.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn slice(&self, offset: usize, size: usize) -> &[u8] {
+ let end = offset + size;
+ let last_checkpoint = self.last_checkpoint();
+
+ match self
+ .data
+ .get(last_checkpoint + offset..last_checkpoint + offset + size)
+ {
+ Some(slice) => slice,
+ None => debug_unreachable!("slice OOB: {offset}..{end}; len: {}", self.len()),
+ }
+ }
+
+ /// Returns a byte slice of the memory region at the given offset.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn slice_mut(&mut self, offset: usize, size: usize) -> &mut [u8] {
+ let len = self.len();
+ let end = offset + size;
+ let last_checkpoint = self.last_checkpoint();
+
+ match self
+ .data
+ .get_mut(last_checkpoint + offset..last_checkpoint + offset + size)
+ {
+ Some(slice) => slice,
+ None => debug_unreachable!("slice OOB: {offset}..{end}; len: {}", len),
+ }
+ }
+
+ /// Sets the `byte` at the given `index`.
+ ///
+ /// Panics when `index` is out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn set_byte(&mut self, index: usize, byte: u8) {
+ let last_checkpoint = self.last_checkpoint();
+ match self.data.get_mut(last_checkpoint + index) {
+ Some(b) => *b = byte,
+ None => debug_unreachable!("set_byte OOB: {index}; len: {}", self.len()),
+ }
+ }
+
+ /// Sets the given `value` to the memory region at the given `offset`.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn set_u256(&mut self, offset: usize, value: U256) {
+ self.set(offset, &value.to_be_bytes::<32>());
+ }
+
+ /// Set memory region at given `offset`.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn set(&mut self, offset: usize, value: &[u8]) {
+ if !value.is_empty() {
+ self.slice_mut(offset, value.len()).copy_from_slice(value);
+ }
+ }
+
+ /// 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.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) {
+ if data_offset >= data.len() {
+ // nullify all memory slots
+ self.slice_mut(memory_offset, len).fill(0);
+ return;
+ }
+ let data_end = min(data_offset + len, data.len());
+ let data_len = data_end - data_offset;
+ debug_assert!(data_offset < data.len() && data_end <= data.len());
+ let data = unsafe { data.get_unchecked(data_offset..data_end) };
+ self.slice_mut(memory_offset, data_len)
+ .copy_from_slice(data);
+
+ // nullify rest of memory slots
+ // Safety: Memory is assumed to be valid. And it is commented where that assumption is made
+ self.slice_mut(memory_offset + data_len, len - data_len)
+ .fill(0);
+ }
+
+ /// Copies elements from one part of the memory to another part of itself.
+ ///
+ /// Panics on out of bounds.
+ #[inline(always)]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
+ self.context_memory_mut().copy_within(src..src + len, dst);
+ }
+
+ /// Get a reference to the memory of the current context
+ #[inline(always)]
+ fn context_memory(&self) -> &[u8] {
+ let last_checkpoint = self.last_checkpoint();
+ let current_len = self.current_len;
+ // Safety: it is a valid pointer to a slice of `self.data`
+ &self.data[last_checkpoint..last_checkpoint + current_len]
+ }
+
+ /// Get a mutable reference to the memory of the current context
+ #[inline(always)]
+ fn context_memory_mut(&mut self) -> &mut [u8] {
+ let last_checkpoint = self.last_checkpoint();
+ let current_len = self.current_len;
+ // Safety: it is a valid pointer to a slice of `self.data`
+ &mut self.data[last_checkpoint..last_checkpoint + current_len]
+ }
+
+ /// Get the last memory checkpoint
+ #[inline(always)]
+ fn last_checkpoint(&self) -> usize {
+ *self.checkpoints.last().unwrap_or(&0)
+ }
+}
+
+/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
+#[inline]
+pub(crate) fn next_multiple_of_32(x: usize) -> Option {
+ let r = x.bitand(31).not().wrapping_add(1).bitand(31);
+ x.checked_add(r)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::next_multiple_of_32;
+
+ #[test]
+ fn test_next_multiple_of_32() {
+ // next_multiple_of_32 returns x when it is a multiple of 32
+ for i in 0..32 {
+ let x = i * 32;
+ assert_eq!(Some(x), next_multiple_of_32(x));
+ }
+
+ // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0`
+ for x in 0..1024 {
+ if x % 32 == 0 {
+ continue;
+ }
+ let next_multiple = x + 32 - (x % 32);
+ assert_eq!(Some(next_multiple), next_multiple_of_32(x));
+ }
+ }
+}
diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs
index a55265d791..0fcd745e73 100644
--- a/crates/interpreter/src/lib.rs
+++ b/crates/interpreter/src/lib.rs
@@ -22,7 +22,7 @@ pub use inner_models::*;
pub use instruction_result::InstructionResult;
pub use instructions::{opcode, Instruction, OpCode, OPCODE_JUMPMAP};
pub use interpreter::{
- analysis, BytecodeLocked, Contract, Interpreter, Memory, Stack, CALL_STACK_LIMIT,
+ analysis, BytecodeLocked, Contract, Interpreter, SharedMemory, Stack, CALL_STACK_LIMIT,
MAX_CODE_SIZE, MAX_INITCODE_SIZE,
};
diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs
index 37056277f9..4dadb40c67 100644
--- a/crates/revm/benches/bench.rs
+++ b/crates/revm/benches/bench.rs
@@ -8,6 +8,7 @@ use revm::{
address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256,
},
};
+use revm_interpreter::SharedMemory;
use std::time::Duration;
type Evm = revm::EVM;
@@ -57,7 +58,7 @@ fn snailtracer(c: &mut Criterion) {
.measurement_time(Duration::from_secs(10))
.sample_size(10);
bench_transact(&mut g, &mut evm);
- bench_eval(&mut g, &evm);
+ bench_eval(&mut g, &mut evm);
g.finish();
}
@@ -85,7 +86,9 @@ fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) {
g.bench_function(id, |b| b.iter(|| evm.transact().unwrap()));
}
-fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &Evm) {
+fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) {
+ let mut shared_memory = SharedMemory::new();
+
g.bench_function("eval", |b| {
let contract = Contract {
input: evm.env.tx.data.clone(),
@@ -94,7 +97,12 @@ fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &Evm) {
};
let mut host = DummyHost::new(evm.env.clone());
b.iter(|| {
- let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false);
+ let mut interpreter = Interpreter::new(
+ Box::new(contract.clone()),
+ u64::MAX,
+ false,
+ &mut shared_memory,
+ );
let res = interpreter.run::<_, BerlinSpec>(&mut host);
host.clear();
res
diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs
index 577b219e3f..792a06c5fc 100644
--- a/crates/revm/src/evm_impl.rs
+++ b/crates/revm/src/evm_impl.rs
@@ -15,7 +15,7 @@ use alloc::boxed::Box;
use alloc::vec::Vec;
use core::marker::PhantomData;
use revm_interpreter::gas::initial_tx_gas;
-use revm_interpreter::MAX_CODE_SIZE;
+use revm_interpreter::{SharedMemory, MAX_CODE_SIZE};
use revm_precompile::{Precompile, Precompiles};
#[cfg(feature = "optimism")]
@@ -285,40 +285,48 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact
let transact_gas_limit = tx_gas_limit - initial_gas_spend;
+ let mut shared_memory = SharedMemory::new();
+
// call inner handling of call/create
let (call_result, ret_gas, output) = match self.data.env.tx.transact_to {
TransactTo::Call(address) => {
// Nonce is already checked
caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
- let (exit, gas, bytes) = self.call(&mut CallInputs {
- contract: address,
- transfer: Transfer {
- source: tx_caller,
- target: address,
- value: tx_value,
- },
- input: tx_data,
- gas_limit: transact_gas_limit,
- context: CallContext {
- caller: tx_caller,
- address,
- code_address: address,
- apparent_value: tx_value,
- scheme: CallScheme::Call,
+ let (exit, gas, bytes) = self.call(
+ &mut CallInputs {
+ contract: address,
+ transfer: Transfer {
+ source: tx_caller,
+ target: address,
+ value: tx_value,
+ },
+ input: tx_data,
+ gas_limit: transact_gas_limit,
+ context: CallContext {
+ caller: tx_caller,
+ address,
+ code_address: address,
+ apparent_value: tx_value,
+ scheme: CallScheme::Call,
+ },
+ is_static: false,
},
- is_static: false,
- });
+ &mut shared_memory,
+ );
(exit, gas, Output::Call(bytes))
}
TransactTo::Create(scheme) => {
- let (exit, address, ret_gas, bytes) = self.create(&mut CreateInputs {
- caller: tx_caller,
- scheme,
- value: tx_value,
- init_code: tx_data,
- gas_limit: transact_gas_limit,
- });
+ let (exit, address, ret_gas, bytes) = self.create(
+ &mut CreateInputs {
+ caller: tx_caller,
+ scheme,
+ value: tx_value,
+ init_code: tx_data,
+ gas_limit: transact_gas_limit,
+ },
+ &mut shared_memory,
+ );
(exit, ret_gas, Output::Create(bytes, address))
}
};
@@ -525,7 +533,11 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
}
/// EVM create opcode for both initial crate and CREATE and CREATE2 opcodes.
- fn create_inner(&mut self, inputs: &CreateInputs) -> CreateResult {
+ fn create_inner(
+ &mut self,
+ inputs: &CreateInputs,
+ shared_memory: &mut SharedMemory,
+ ) -> CreateResult {
// Prepare crate.
let prepared_create = match self.prepare_create(inputs) {
Ok(o) => o,
@@ -533,15 +545,18 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
};
// Create new interpreter and execute initcode
- let (exit_reason, mut interpreter) =
- self.run_interpreter(prepared_create.contract, prepared_create.gas.limit(), false);
+ let (exit_reason, mut bytes, mut gas) = self.run_interpreter(
+ prepared_create.contract,
+ prepared_create.gas.limit(),
+ false,
+ shared_memory,
+ );
// Host error if present on execution
match exit_reason {
return_ok!() => {
// if ok, check contract creation limit and calculate gas deduction on output len.
- let mut bytes = interpreter.return_value();
-
+ //
// EIP-3541: Reject new contract code starting with the 0xEF byte
if GSPEC::enabled(LONDON) && !bytes.is_empty() && bytes.first() == Some(&0xEF) {
self.data
@@ -550,7 +565,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
return CreateResult {
result: InstructionResult::CreateContractStartingWithEF,
created_address: Some(prepared_create.created_address),
- gas: interpreter.gas,
+ gas,
return_value: bytes,
};
}
@@ -572,13 +587,13 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
return CreateResult {
result: InstructionResult::CreateContractSizeLimit,
created_address: Some(prepared_create.created_address),
- gas: interpreter.gas,
+ gas,
return_value: bytes,
};
}
if crate::USE_GAS {
let gas_for_code = bytes.len() as u64 * gas::CODEDEPOSIT;
- if !interpreter.gas.record_cost(gas_for_code) {
+ if !gas.record_cost(gas_for_code) {
// record code deposit gas cost and check if we are out of gas.
// EIP-2 point 3: If contract creation does not have enough gas to pay for the
// final gas fee for adding the contract code to the state, the contract
@@ -590,7 +605,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
return CreateResult {
result: InstructionResult::OutOfGas,
created_address: Some(prepared_create.created_address),
- gas: interpreter.gas,
+ gas,
return_value: bytes,
};
} else {
@@ -612,7 +627,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
CreateResult {
result: InstructionResult::Return,
created_address: Some(prepared_create.created_address),
- gas: interpreter.gas,
+ gas,
return_value: bytes,
}
}
@@ -623,32 +638,30 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
CreateResult {
result: exit_reason,
created_address: Some(prepared_create.created_address),
- gas: interpreter.gas,
- return_value: interpreter.return_value(),
+ gas,
+ return_value: bytes,
}
}
}
}
/// Create a Interpreter and run it.
- /// Returns the exit reason and created interpreter as it contains return values and gas spend.
+ /// Returns the exit reason, return value and gas from interpreter
pub fn run_interpreter(
&mut self,
contract: Box,
gas_limit: u64,
is_static: bool,
- ) -> (InstructionResult, Box) {
- // Create inspector
- #[cfg(feature = "memory_limit")]
- let mut interpreter = Box::new(Interpreter::new_with_memory_limit(
+ shared_memory: &mut SharedMemory,
+ ) -> (InstructionResult, Bytes, Gas) {
+ let mut interpreter = Box::new(Interpreter::new(
contract,
gas_limit,
is_static,
- self.data.env.cfg.memory_limit,
+ shared_memory,
));
- #[cfg(not(feature = "memory_limit"))]
- let mut interpreter = Box::new(Interpreter::new(contract, gas_limit, is_static));
+ interpreter.shared_memory.new_context_memory();
if INSPECT {
self.inspector
@@ -660,7 +673,11 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
interpreter.run::(self)
};
- (exit_reason, interpreter)
+ let (return_value, gas) = (interpreter.return_value(), *interpreter.gas());
+
+ interpreter.shared_memory.free_context_memory();
+
+ (exit_reason, return_value, gas)
}
/// Call precompile contract
@@ -777,7 +794,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
}
/// Main contract call of the EVM.
- fn call_inner(&mut self, inputs: &CallInputs) -> CallResult {
+ fn call_inner(&mut self, inputs: &CallInputs, shared_memory: &mut SharedMemory) -> CallResult {
// Prepare call
let prepared_call = match self.prepare_call(inputs) {
Ok(o) => o,
@@ -788,15 +805,16 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB,
self.call_precompile(inputs, prepared_call.gas)
} else if !prepared_call.contract.bytecode.is_empty() {
// Create interpreter and execute subcall
- let (exit_reason, interpreter) = self.run_interpreter(
+ let (exit_reason, bytes, gas) = self.run_interpreter(
prepared_call.contract,
prepared_call.gas.limit(),
inputs.is_static,
+ shared_memory,
);
CallResult {
result: exit_reason,
- gas: interpreter.gas,
- return_value: interpreter.return_value(),
+ gas,
+ return_value: bytes,
}
} else {
CallResult {
@@ -948,6 +966,7 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host
fn create(
&mut self,
inputs: &mut CreateInputs,
+ shared_memory: &mut SharedMemory,
) -> (InstructionResult, Option, Gas, Bytes) {
// Call inspector
if INSPECT {
@@ -958,7 +977,7 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host
.create_end(&mut self.data, inputs, ret, address, gas, out);
}
}
- let ret = self.create_inner(inputs);
+ let ret = self.create_inner(inputs, shared_memory);
if INSPECT {
self.inspector.create_end(
&mut self.data,
@@ -973,7 +992,11 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host
}
}
- fn call(&mut self, inputs: &mut CallInputs) -> (InstructionResult, Gas, Bytes) {
+ fn call(
+ &mut self,
+ inputs: &mut CallInputs,
+ shared_memory: &mut SharedMemory,
+ ) -> (InstructionResult, Gas, Bytes) {
if INSPECT {
let (ret, gas, out) = self.inspector.call(&mut self.data, inputs);
if ret != InstructionResult::Continue {
@@ -982,7 +1005,7 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host
.call_end(&mut self.data, inputs, gas, ret, out);
}
}
- let ret = self.call_inner(inputs);
+ let ret = self.call_inner(inputs, shared_memory);
if INSPECT {
self.inspector.call_end(
&mut self.data,
diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs
index f0b8a4ae88..31c18e92f9 100644
--- a/crates/revm/src/inspector/customprinter.rs
+++ b/crates/revm/src/inspector/customprinter.rs
@@ -38,7 +38,7 @@ impl Inspector for CustomPrintTracer {
interp.gas.refunded(),
interp.gas.refunded(),
interp.stack.data(),
- interp.memory.data().len(),
+ interp.shared_memory.len(),
);
self.gas_inspector.step(interp, data);
diff --git a/crates/revm/src/inspector/tracer_eip3155.rs b/crates/revm/src/inspector/tracer_eip3155.rs
index 5625225f40..926e70bdf2 100644
--- a/crates/revm/src/inspector/tracer_eip3155.rs
+++ b/crates/revm/src/inspector/tracer_eip3155.rs
@@ -5,7 +5,7 @@ use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult};
use crate::primitives::{db::Database, hex, Address, Bytes};
use crate::{evm_impl::EVMData, Inspector};
use revm_interpreter::primitives::U256;
-use revm_interpreter::{opcode, Interpreter, Memory, Stack};
+use revm_interpreter::{opcode, Interpreter, SharedMemory, Stack};
use serde_json::json;
use std::io::Write;
@@ -24,7 +24,7 @@ pub struct TracerEip3155 {
gas: u64,
mem_size: usize,
#[allow(dead_code)]
- memory: Option,
+ memory: Option,
skip: bool,
}
@@ -63,7 +63,7 @@ impl Inspector for TracerEip3155 {
self.stack = interp.stack.clone();
self.pc = interp.program_counter();
self.opcode = interp.current_opcode();
- self.mem_size = interp.memory.len();
+ self.mem_size = interp.shared_memory.len();
self.gas = self.gas_inspector.gas_remaining();
InstructionResult::Continue
}