Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(EOF): EIP-7698 eof creation transaction #1467

Merged
merged 7 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions crates/interpreter/src/instruction_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl From<SuccessReason> for InstructionResult {
SuccessReason::Return => InstructionResult::Return,
SuccessReason::Stop => InstructionResult::Stop,
SuccessReason::SelfDestruct => InstructionResult::SelfDestruct,
SuccessReason::EofReturnContract => InstructionResult::ReturnContract,
}
}
}
Expand Down Expand Up @@ -269,9 +270,7 @@ impl From<InstructionResult> 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),
}
}
}
Expand Down
43 changes: 15 additions & 28 deletions crates/interpreter/src/instructions/contract.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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<Range<usize>> {
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<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
require_eof!(interpreter);
Expand All @@ -52,10 +30,20 @@ pub fn eofcreate<H: Host + ?Sized>(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 {
Expand Down Expand Up @@ -86,7 +74,7 @@ pub fn eofcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H)
value,
eof,
gas_limit,
return_range,
input,
)),
};

Expand Down Expand Up @@ -150,8 +138,7 @@ pub fn return_contract<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &
pub fn extcall_input(interpreter: &mut Interpreter) -> Option<Bytes> {
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
Expand Down
6 changes: 3 additions & 3 deletions crates/interpreter/src/instructions/contract/call_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ pub fn get_memory_input_and_out_ranges(
) -> Option<(Bytes, Range<usize>)> {
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,
Expand Down
33 changes: 21 additions & 12 deletions crates/interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
34 changes: 27 additions & 7 deletions crates/interpreter/src/interpreter_action/eof_create_inputs.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -13,30 +13,50 @@ 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<usize>,
}

impl EOFCreateInput {
/// Returns boxed EOFCreateInput or error.
/// Internally calls [`Self::new_tx`].
pub fn new_tx_boxed(tx: &TxEnv, nonce: u64) -> Result<Box<Self>, 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<Self, EofDecodeError> {
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,
created_address: Address,
value: U256,
eof_init_code: Eof,
gas_limit: u64,
return_memory_range: Range<usize>,
input: Bytes,
) -> EOFCreateInput {
EOFCreateInput {
caller,
created_address,
value,
eof_init_code,
gas_limit,
return_memory_range,
input,
}
}
}
21 changes: 2 additions & 19 deletions crates/interpreter/src/interpreter_action/eof_create_outcome.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::ops::Range;

use crate::{Gas, InstructionResult, InterpreterResult};
use revm_primitives::{Address, Bytes};

Expand All @@ -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<usize>,
}

impl EOFCreateOutcome {
Expand All @@ -30,16 +26,8 @@ impl EOFCreateOutcome {
/// # Returns
///
/// A new [`EOFCreateOutcome`] instance.
pub fn new(
result: InterpreterResult,
address: Address,
return_memory_range: Range<usize>,
) -> 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`].
Expand Down Expand Up @@ -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<usize> {
self.return_memory_range.clone()
}
}
56 changes: 56 additions & 0 deletions crates/primitives/src/bytecode/eof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, EofDecodeError> {
let (header, _) = EofHeader::decode(&raw)?;
Expand Down Expand Up @@ -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");
Expand Down
5 changes: 4 additions & 1 deletion crates/primitives/src/bytecode/eof/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions crates/primitives/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ pub enum SuccessReason {
Stop,
Return,
SelfDestruct,
EofReturnContract,
}

/// Indicates that the EVM has experienced an exceptional halt. This causes execution to
Expand Down
2 changes: 2 additions & 0 deletions crates/revm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ where
}

impl<EXT, DB: Database> Host for Context<EXT, DB> {
/// Returns reference to Environment.
#[inline]
fn env(&self) -> &Env {
&self.evm.env
}
Expand Down
Loading
Loading