diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 9e0f6e1264..121e12ead7 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -61,6 +61,7 @@ impl From for InstructionResult { SuccessReason::Return => InstructionResult::Return, SuccessReason::Stop => InstructionResult::Stop, SuccessReason::SelfDestruct => InstructionResult::SelfDestruct, + SuccessReason::EofReturnContract => InstructionResult::ReturnContract, } } } @@ -269,9 +270,7 @@ impl From for SuccessOrHalt { InstructionResult::FatalExternalError => Self::FatalExternalError, InstructionResult::EOFOpcodeDisabledInLegacy => Self::Halt(HaltReason::OpcodeNotFound), InstructionResult::EOFFunctionStackOverflow => Self::FatalExternalError, - InstructionResult::ReturnContract => { - panic!("Unexpected EOF internal Return Contract") - } + InstructionResult::ReturnContract => Self::Success(SuccessReason::EofReturnContract), } } } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 555b5e2876..206891e962 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -1,8 +1,6 @@ mod call_helpers; -pub use call_helpers::{ - calc_call_gas, get_memory_input_and_out_ranges, resize_memory_and_return_range, -}; +pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_memory}; use revm_primitives::{keccak256, BerlinSpec}; use crate::{ @@ -12,29 +10,9 @@ use crate::{ CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInput, Host, InstructionResult, InterpreterAction, InterpreterResult, LoadAccountResult, MAX_INITCODE_SIZE, }; -use core::{cmp::max, ops::Range}; +use core::cmp::max; use std::boxed::Box; -/// Resize memory and return memory range if successful. -/// Return `None` if there is not enough gas. And if `len` -/// is zero return `Some(usize::MAX..usize::MAX)`. -pub fn resize_memory( - interpreter: &mut Interpreter, - offset: U256, - len: U256, -) -> Option> { - let len = as_usize_or_fail_ret!(interpreter, len, None); - if len != 0 { - let offset = as_usize_or_fail_ret!(interpreter, offset, None); - resize_memory!(interpreter, offset, len, None); - // range is checked in resize_memory! macro and it is bounded by usize. - Some(offset..offset + len) - } else { - //unrealistic value so we are sure it is not used - Some(usize::MAX..usize::MAX) - } -} - /// EOF Create instruction pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) { require_eof!(interpreter); @@ -52,10 +30,20 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) .expect("EOF is checked"); // resize memory and get return range. - let Some(return_range) = resize_memory(interpreter, data_offset, data_size) else { + let Some(input_range) = resize_memory(interpreter, data_offset, data_size) else { return; }; + let input = if !input_range.is_empty() { + interpreter + .shared_memory + .slice_range(input_range) + .to_vec() + .into() + } else { + Bytes::new() + }; + let eof = Eof::decode(sub_container.clone()).expect("Subcontainer is verified"); if !eof.body.is_data_filled { @@ -86,7 +74,7 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) value, eof, gas_limit, - return_range, + input, )), }; @@ -150,8 +138,7 @@ pub fn return_contract(interpreter: &mut Interpreter, _host: & pub fn extcall_input(interpreter: &mut Interpreter) -> Option { pop_ret!(interpreter, input_offset, input_size, None); - let return_memory_offset = - resize_memory_and_return_range(interpreter, input_offset, input_size)?; + let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?; Some(Bytes::copy_from_slice( interpreter diff --git a/crates/interpreter/src/instructions/contract/call_helpers.rs b/crates/interpreter/src/instructions/contract/call_helpers.rs index 5570efe3ac..f9acc74901 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -11,21 +11,21 @@ pub fn get_memory_input_and_out_ranges( ) -> Option<(Bytes, Range)> { pop_ret!(interpreter, in_offset, in_len, out_offset, out_len, None); - let in_range = resize_memory_and_return_range(interpreter, in_offset, in_len)?; + let in_range = resize_memory(interpreter, in_offset, in_len)?; let mut input = Bytes::new(); if !in_range.is_empty() { input = Bytes::copy_from_slice(interpreter.shared_memory.slice_range(in_range)); } - let ret_range = resize_memory_and_return_range(interpreter, out_offset, out_len)?; + let ret_range = resize_memory(interpreter, out_offset, out_len)?; Some((input, ret_range)) } /// Resize memory and return range of memory. /// If `len` is 0 dont touch memory and return `usize::MAX` as offset and 0 as length. #[inline] -pub fn resize_memory_and_return_range( +pub fn resize_memory( interpreter: &mut Interpreter, offset: U256, len: U256, diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index a27752b2e8..d2f0d51730 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -68,18 +68,6 @@ impl Default for Interpreter { } } -/// The result of an interpreter operation. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] -pub struct InterpreterResult { - /// The result of the instruction execution. - pub result: InstructionResult, - /// The output of the instruction execution. - pub output: Bytes, - /// The gas usage information. - pub gas: Gas, -} - impl Interpreter { /// Create new interpreter pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self { @@ -388,7 +376,28 @@ impl Interpreter { } } +/// The result of an interpreter operation. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] +pub struct InterpreterResult { + /// The result of the instruction execution. + pub result: InstructionResult, + /// The output of the instruction execution. + pub output: Bytes, + /// The gas usage information. + pub gas: Gas, +} + impl InterpreterResult { + /// Returns a new `InterpreterResult` with the given values. + pub fn new(result: InstructionResult, output: Bytes, gas: Gas) -> Self { + Self { + result, + output, + gas, + } + } + /// Returns whether the instruction result is a success. #[inline] pub const fn is_ok(&self) -> bool { diff --git a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs index 25dac1e046..28a1a471b9 100644 --- a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs @@ -1,5 +1,5 @@ -use crate::primitives::{Address, Eof, U256}; -use core::ops::Range; +use crate::primitives::{eof::EofDecodeError, Address, Bytes, Eof, TxEnv, U256}; +use std::boxed::Box; /// Inputs for EOF create call. #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -13,14 +13,34 @@ pub struct EOFCreateInput { pub value: U256, /// Init eof code that is going to be executed. pub eof_init_code: Eof, + /// Call data the input of the EOFCREATE call. + pub input: Bytes, /// Gas limit for the create call. pub gas_limit: u64, - /// Return memory range. If EOF creation Reverts it can return the - /// the memory range. - pub return_memory_range: Range, } impl EOFCreateInput { + /// Returns boxed EOFCreateInput or error. + /// Internally calls [`Self::new_tx`]. + pub fn new_tx_boxed(tx: &TxEnv, nonce: u64) -> Result, EofDecodeError> { + Ok(Box::new(Self::new_tx(tx, nonce)?)) + } + + /// Create new EOF crate input from transaction that has concatenated eof init code and calldata. + /// + /// Legacy transaction still have optional nonce so we need to obtain it. + pub fn new_tx(tx: &TxEnv, nonce: u64) -> Result { + let (eof_init_code, input) = Eof::decode_dangling(tx.data.clone())?; + Ok(EOFCreateInput { + caller: tx.caller, + created_address: tx.caller.create(nonce), + value: tx.value, + eof_init_code, + gas_limit: tx.gas_limit, + input, + }) + } + /// Returns a new instance of EOFCreateInput. pub fn new( caller: Address, @@ -28,7 +48,7 @@ impl EOFCreateInput { value: U256, eof_init_code: Eof, gas_limit: u64, - return_memory_range: Range, + input: Bytes, ) -> EOFCreateInput { EOFCreateInput { caller, @@ -36,7 +56,7 @@ impl EOFCreateInput { value, eof_init_code, gas_limit, - return_memory_range, + input, } } } diff --git a/crates/interpreter/src/interpreter_action/eof_create_outcome.rs b/crates/interpreter/src/interpreter_action/eof_create_outcome.rs index 9ec669a070..beeda24e50 100644 --- a/crates/interpreter/src/interpreter_action/eof_create_outcome.rs +++ b/crates/interpreter/src/interpreter_action/eof_create_outcome.rs @@ -1,5 +1,3 @@ -use core::ops::Range; - use crate::{Gas, InstructionResult, InterpreterResult}; use revm_primitives::{Address, Bytes}; @@ -14,8 +12,6 @@ pub struct EOFCreateOutcome { pub result: InterpreterResult, /// An optional address associated with the create operation. pub address: Address, - /// Return memory range. If EOF creation Reverts it can return bytes from the memory. - pub return_memory_range: Range, } impl EOFCreateOutcome { @@ -30,16 +26,8 @@ impl EOFCreateOutcome { /// # Returns /// /// A new [`EOFCreateOutcome`] instance. - pub fn new( - result: InterpreterResult, - address: Address, - return_memory_range: Range, - ) -> Self { - Self { - result, - address, - return_memory_range, - } + pub fn new(result: InterpreterResult, address: Address) -> Self { + Self { result, address } } /// Retrieves a reference to the [`InstructionResult`] from the [`InterpreterResult`]. @@ -80,9 +68,4 @@ impl EOFCreateOutcome { pub fn gas(&self) -> &Gas { &self.result.gas } - - /// Returns the memory range that Revert bytes are going to be written. - pub fn return_range(&self) -> Range { - self.return_memory_range.clone() - } } diff --git a/crates/primitives/src/bytecode/eof.rs b/crates/primitives/src/bytecode/eof.rs index 67580c1577..3f78bc26da 100644 --- a/crates/primitives/src/bytecode/eof.rs +++ b/crates/primitives/src/bytecode/eof.rs @@ -76,6 +76,26 @@ impl Eof { buffer.into() } + /// Decode EOF that have additional dangling bytes. + /// Assume that data section is fully filled. + pub fn decode_dangling(mut eof: Bytes) -> Result<(Self, Bytes), EofDecodeError> { + let (header, _) = EofHeader::decode(&eof)?; + let eof_size = header.body_size() + header.size(); + if eof_size > eof.len() { + return Err(EofDecodeError::MissingInput); + } + let dangling_data = eof.split_off(eof_size); + let body = EofBody::decode(&eof, &header)?; + Ok(( + Self { + header, + body, + raw: eof, + }, + dangling_data, + )) + } + /// Decode EOF from raw bytes. pub fn decode(raw: Bytes) -> Result { let (header, _) = EofHeader::decode(&raw)?; @@ -140,6 +160,42 @@ mod test { assert_eq!(bytes, eof.encode_slow()); } + #[test] + fn decode_eof_dangling() { + let test_cases = [ + ( + bytes!("ef000101000402000100010400000000800000fe"), + bytes!("010203"), + false, + ), + ( + bytes!("ef000101000402000100010400000000800000fe"), + bytes!(""), + false, + ), + ( + bytes!("ef000101000402000100010400000000800000"), + bytes!(""), + true, + ), + ]; + + for (eof_bytes, dangling_data, is_err) in test_cases { + let mut raw = eof_bytes.to_vec(); + raw.extend(&dangling_data); + let raw = Bytes::from(raw); + + let result = Eof::decode_dangling(raw.clone()); + assert_eq!(result.is_err(), is_err); + if is_err { + continue; + } + let (decoded_eof, decoded_dangling) = result.unwrap(); + assert_eq!(eof_bytes, decoded_eof.encode_slow()); + assert_eq!(decoded_dangling, dangling_data); + } + } + #[test] fn data_slice() { let bytes = bytes!("ef000101000402000100010400000000800000fe"); diff --git a/crates/primitives/src/bytecode/eof/header.rs b/crates/primitives/src/bytecode/eof/header.rs index bd7a33d7b7..1074f066a4 100644 --- a/crates/primitives/src/bytecode/eof/header.rs +++ b/crates/primitives/src/bytecode/eof/header.rs @@ -90,7 +90,10 @@ impl EofHeader { /// Returns body size. It is sum of code sizes, container sizes and data size. pub fn body_size(&self) -> usize { - self.sum_code_sizes + self.sum_container_sizes + self.data_size as usize + self.types_size as usize + + self.sum_code_sizes + + self.sum_container_sizes + + self.data_size as usize } /// Returns raw size of the EOF. diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 438451e2a9..36cd0db90d 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -388,6 +388,7 @@ pub enum SuccessReason { Stop, Return, SelfDestruct, + EofReturnContract, } /// Indicates that the EVM has experienced an exceptional halt. This causes execution to diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 6cdbdfe6fa..40107651cb 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -98,6 +98,8 @@ where } impl Host for Context { + /// Returns reference to Environment. + #[inline] fn env(&self) -> &Env { &self.evm.env } diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs index 109b16a7a6..d12e90c148 100644 --- a/crates/revm/src/context/inner_evm_context.rs +++ b/crates/revm/src/context/inner_evm_context.rs @@ -239,7 +239,6 @@ impl InnerEvmContext { output: Bytes::new(), }, inputs.created_address, - inputs.return_memory_range.clone(), )) }; @@ -280,7 +279,7 @@ impl InnerEvmContext { }; let contract = Contract::new( - Bytes::new(), + inputs.input.clone(), // fine to clone as it is Bytes. Bytecode::Eof(inputs.eof_init_code.clone()), None, @@ -295,7 +294,6 @@ impl InnerEvmContext { Ok(FrameOrResult::new_eofcreate_frame( inputs.created_address, - inputs.return_memory_range.clone(), checkpoint, interpreter, )) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 2fcf932452..bebd7bc57b 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,15 +2,17 @@ use crate::{ builder::{EvmBuilder, HandlerStage, SetGenericStage}, db::{Database, DatabaseCommit, EmptyDB}, handler::Handler, - interpreter::{Host, InterpreterAction, SharedMemory}, + interpreter::{ + analysis::validate_eof, CallInputs, CreateInputs, EOFCreateInput, EOFCreateOutcome, Gas, + Host, InstructionResult, InterpreterAction, InterpreterResult, SharedMemory, + }, primitives::{ - specification::SpecId, BlockEnv, CfgEnv, EVMError, EVMResult, EnvWithHandlerCfg, + specification::SpecId, BlockEnv, Bytes, CfgEnv, EVMError, EVMResult, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, ResultAndState, TransactTo, TxEnv, }, Context, ContextWithHandlerCfg, Frame, FrameOrResult, FrameResult, }; use core::fmt; -use revm_interpreter::{CallInputs, CreateInputs}; use std::vec::Vec; /// EVM call stack limit. @@ -324,6 +326,7 @@ impl Evm<'_, EXT, DB> { /// Transact pre-verified transaction. fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + let spec_id = self.spec_id(); let ctx = &mut self.context; let pre_exec = self.handler.pre_execution(); @@ -346,10 +349,61 @@ impl Evm<'_, EXT, DB> { ctx, CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), )?, - TransactTo::Create => exec.create( - ctx, - CreateInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - )?, + TransactTo::Create => { + // if first byte of data is magic 0xEF00, then it is EOFCreate. + if spec_id.is_enabled_in(SpecId::PRAGUE) + && ctx + .env() + .tx + .data + .get(0..=1) + .filter(|&t| t == [0xEF, 00]) + .is_some() + { + // TODO Should we just check 0xEF it seems excessive to switch to legacy only + // if it 0xEF00? + + // get nonce from tx (if set) or from account (if not). + // Nonce for call is bumped in deduct_caller while + // for CREATE it is not (it is done inside exec handlers). + let nonce = ctx.evm.env.tx.nonce.unwrap_or_else(|| { + let caller = ctx.evm.env.tx.caller; + ctx.evm + .load_account(caller) + .map(|(a, _)| a.info.nonce) + .unwrap_or_default() + }); + + // Create EOFCreateInput from transaction initdata. + let eofcreate = EOFCreateInput::new_tx_boxed(&ctx.evm.env.tx, nonce) + .ok() + .and_then(|eofcreate| { + // validate EOF initcode + validate_eof(&eofcreate.eof_init_code).ok()?; + Some(eofcreate) + }); + + if let Some(eofcreate) = eofcreate { + exec.eofcreate(ctx, eofcreate)? + } else { + // Return result, as code is invalid. + FrameOrResult::Result(FrameResult::EOFCreate(EOFCreateOutcome::new( + InterpreterResult::new( + InstructionResult::Stop, + Bytes::new(), + Gas::new(gas_limit), + ), + ctx.env().tx.caller.create(nonce), + ))) + } + } else { + // Safe to unwrap because we are sure that it is create tx. + exec.create( + ctx, + CreateInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), + )? + } + } }; // Starts the main running loop. diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index 7257650581..1988705ab1 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -33,7 +33,6 @@ pub struct CreateFrame { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EOFCreateFrame { pub created_address: Address, - pub return_memory_range: Range, pub frame_data: FrameData, } @@ -81,8 +80,8 @@ impl FrameResult { FrameResult::Create(outcome) => { Output::Create(outcome.result.output.clone(), outcome.address) } - FrameResult::EOFCreate(_) => { - panic!("EOFCreate can't be called from external world."); + FrameResult::EOFCreate(outcome) => { + Output::Create(outcome.result.output.clone(), Some(outcome.address)) } } } @@ -240,13 +239,11 @@ impl FrameOrResult { pub fn new_eofcreate_frame( created_address: Address, - return_memory_range: Range, checkpoint: JournalCheckpoint, interpreter: Interpreter, ) -> Self { Self::Frame(Frame::EOFCreate(Box::new(EOFCreateFrame { created_address, - return_memory_range, frame_data: FrameData { checkpoint, interpreter, @@ -278,15 +275,10 @@ impl FrameOrResult { })) } - pub fn new_eofcreate_result( - interpreter_result: InterpreterResult, - address: Address, - return_memory_range: Range, - ) -> Self { + pub fn new_eofcreate_result(interpreter_result: InterpreterResult, address: Address) -> Self { FrameOrResult::Result(FrameResult::EOFCreate(EOFCreateOutcome { result: interpreter_result, address, - return_memory_range, })) } diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index 9e1ff3ef54..e55d6c4474 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -183,7 +183,6 @@ pub fn eofcreate_return( Ok(EOFCreateOutcome::new( interpreter_result, frame.created_address, - frame.return_memory_range, )) }