Skip to content

Commit

Permalink
feat(EOF): remove TXCREATE (#1415)
Browse files Browse the repository at this point in the history
  • Loading branch information
rakita authored May 13, 2024
1 parent 38c4d3e commit a67c30e
Show file tree
Hide file tree
Showing 6 changed files with 10 additions and 159 deletions.
16 changes: 4 additions & 12 deletions crates/interpreter/src/gas/calc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::constants::*;
use crate::{
num_words,
primitives::{Address, Bytes, SpecId, U256},
primitives::{Address, SpecId, U256},
SelfDestructResult,
};
use std::vec::Vec;
Expand Down Expand Up @@ -358,18 +358,10 @@ pub fn validate_initial_tx_gas(
input: &[u8],
is_create: bool,
access_list: &[(Address, Vec<U256>)],
initcodes: &[Bytes],
) -> u64 {
let mut initial_gas = 0;
let mut zero_data_len = input.iter().filter(|v| **v == 0).count() as u64;
let mut non_zero_data_len = input.len() as u64 - zero_data_len;

// Enabling of initcode is checked in `validate_env` handler.
for initcode in initcodes {
let zeros = initcode.iter().filter(|v| **v == 0).count() as u64;
zero_data_len += zeros;
non_zero_data_len += initcode.len() as u64 - zeros;
}
let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64;
let non_zero_data_len = input.len() as u64 - zero_data_len;

// initdate stipend
initial_gas += zero_data_len * TRANSACTION_ZERO_DATA;
Expand Down Expand Up @@ -403,7 +395,7 @@ pub fn validate_initial_tx_gas(
};

// EIP-3860: Limit and meter initcode
// Initcode stipend for bytecode analysis
// Init code stipend for bytecode analysis
if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create {
initial_gas += initcode_cost(input.len() as u64)
}
Expand Down
84 changes: 2 additions & 82 deletions crates/interpreter/src/instructions/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ pub use call_helpers::{
use revm_primitives::{keccak256, BerlinSpec};

use crate::{
analysis::validate_eof,
gas::{self, cost_per_word, BASE, EOF_CREATE_GAS, KECCAK256WORD},
gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD},
instructions::utility::read_u16,
interpreter::Interpreter,
primitives::{Address, Bytes, Eof, Spec, SpecId::*, B256, U256},
primitives::{Address, Bytes, Eof, Spec, SpecId::*, U256},
CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInput, Host,
InstructionResult, InterpreterAction, InterpreterResult, LoadAccountResult, MAX_INITCODE_SIZE,
};
Expand Down Expand Up @@ -91,85 +90,6 @@ pub fn eofcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H)
interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) };
}

pub fn txcreate<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
require_eof!(interpreter);
gas!(interpreter, EOF_CREATE_GAS);
pop!(
interpreter,
tx_initcode_hash,
value,
salt,
data_offset,
data_size
);
let tx_initcode_hash = B256::from(tx_initcode_hash);

// resize memory and get return range.
let Some(return_range) = resize_memory(interpreter, data_offset, data_size) else {
return;
};

// fetch initcode, if not found push ZERO.
let Some(initcode) = host
.env()
.tx
.eof_initcodes_hashed
.get(&tx_initcode_hash)
.cloned()
else {
push!(interpreter, U256::ZERO);
return;
};

// deduct gas for validation
gas_or_fail!(interpreter, cost_per_word(initcode.len() as u64, BASE));

// deduct gas for hash. TODO check order of actions.
gas_or_fail!(
interpreter,
cost_per_word(initcode.len() as u64, KECCAK256WORD)
);

let Ok(eof) = Eof::decode(initcode.clone()) else {
push!(interpreter, U256::ZERO);
return;
};

// Data section should be full, push zero to stack and return if not.
if !eof.body.is_data_filled {
push!(interpreter, U256::ZERO);
return;
}

// Validate initcode
if validate_eof(&eof).is_err() {
push!(interpreter, U256::ZERO);
return;
}

// Create new address. Gas for it is already deducted.
let created_address = interpreter
.contract
.caller
.create2(salt.to_be_bytes(), tx_initcode_hash);

let gas_limit = interpreter.gas().remaining();
// spend all gas. It will be reimbursed after frame returns.
gas!(interpreter, gas_limit);

interpreter.next_action = InterpreterAction::EOFCreate {
inputs: Box::new(EOFCreateInput::new(
interpreter.contract.target_address,
created_address,
value,
eof,
gas_limit,
return_range,
)),
};
interpreter.instruction_result = InstructionResult::CallOrCreate;
}

pub fn return_contract<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
require_init_eof!(interpreter);
let deploy_container_index = unsafe { read_u16(interpreter.instruction_pointer) };
Expand Down
2 changes: 1 addition & 1 deletion crates/interpreter/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ opcodes! {
// 0xEA
// 0xEB
0xEC => EOFCREATE => contract::eofcreate => stack_io(4, 1), immediate_size(1);
0xED => TXCREATE => contract::txcreate => stack_io(5, 1);
// 0xED
0xEE => RETURNCONTRACT => contract::return_contract => stack_io(2, 0), immediate_size(1), terminating;
// 0xEF
0xF0 => CREATE => contract::create::<false, H, SPEC> => stack_io(3, 1), not_eof;
Expand Down
55 changes: 2 additions & 53 deletions crates/primitives/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub mod handler_cfg;
pub use handler_cfg::{CfgEnvWithHandlerCfg, EnvWithHandlerCfg, HandlerCfg};

use crate::{
calc_blob_gasprice, Account, Address, Bytes, HashMap, InvalidHeader, InvalidTransaction, Spec,
SpecId, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256,
calc_blob_gasprice, Account, Address, Bytes, InvalidHeader, InvalidTransaction, Spec, SpecId,
B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256,
VERSIONED_HASH_VERSION_KZG,
};
use core::cmp::{min, Ordering};
Expand Down Expand Up @@ -189,41 +189,6 @@ impl Env {
}
}

if SPEC::enabled(SpecId::PRAGUE) {
if !self.tx.eof_initcodes.is_empty() {
// If initcode is set other fields must be empty
if !self.tx.blob_hashes.is_empty() {
return Err(InvalidTransaction::BlobVersionedHashesNotSupported);
}
// EOF Create tx extends EIP-1559 tx. It must have max_fee_per_blob_gas
if self.tx.max_fee_per_blob_gas.is_some() {
return Err(InvalidTransaction::MaxFeePerBlobGasNotSupported);
}
// EOF Create must have a to address
if matches!(self.tx.transact_to, TransactTo::Call(_)) {
return Err(InvalidTransaction::EofCrateShouldHaveToAddress);
}
} else {
// If initcode is set check its bounds.
if self.tx.eof_initcodes.len() > 256 {
return Err(InvalidTransaction::EofInitcodesNumberLimit);
}
if self
.tx
.eof_initcodes_hashed
.iter()
.any(|(_, i)| i.len() >= MAX_INITCODE_SIZE)
{
return Err(InvalidTransaction::EofInitcodesSizeLimit);
}
}
} else {
// Initcode set when not supported.
if !self.tx.eof_initcodes.is_empty() {
return Err(InvalidTransaction::EofInitcodesNotSupported);
}
}

Ok(())
}

Expand Down Expand Up @@ -584,20 +549,6 @@ pub struct TxEnv {
/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
pub max_fee_per_blob_gas: Option<U256>,

/// EOF Initcodes for EOF CREATE transaction
///
/// Incorporated as part of the Prague upgrade via [EOF]
///
/// [EOF]: https://eips.ethereum.org/EIPS/eip-4844
pub eof_initcodes: Vec<Bytes>,

/// Internal Temporary field that stores the hashes of the EOF initcodes.
///
/// Those are always cleared after the transaction is executed.
/// And calculated/overwritten every time transaction starts.
/// They are calculated from the [`Self::eof_initcodes`] field.
pub eof_initcodes_hashed: HashMap<B256, Bytes>,

#[cfg_attr(feature = "serde", serde(flatten))]
#[cfg(feature = "optimism")]
/// Optimism fields.
Expand Down Expand Up @@ -642,8 +593,6 @@ impl Default for TxEnv {
access_list: Vec::new(),
blob_hashes: Vec::new(),
max_fee_per_blob_gas: None,
eof_initcodes: Vec::new(),
eof_initcodes_hashed: HashMap::new(),
#[cfg(feature = "optimism")]
optimism: OptimismFields::default(),
}
Expand Down
9 changes: 0 additions & 9 deletions crates/primitives/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,6 @@ pub enum InvalidTransaction {
},
/// Blob transaction contains a versioned hash with an incorrect version
BlobVersionNotSupported,
/// EOF TxCreate transaction is not supported before Prague hardfork.
EofInitcodesNotSupported,
/// EOF TxCreate transaction max initcode number reached.
EofInitcodesNumberLimit,
/// EOF initcode in TXCreate is too large.
EofInitcodesSizeLimit,
/// EOF crate should have `to` address
EofCrateShouldHaveToAddress,
/// System transactions are not supported post-regolith hardfork.
Expand Down Expand Up @@ -346,10 +340,7 @@ impl fmt::Display for InvalidTransaction {
write!(f, "too many blobs, have {have}, max {max}")
}
Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
Self::EofInitcodesNotSupported => write!(f, "EOF initcodes not supported"),
Self::EofCrateShouldHaveToAddress => write!(f, "EOF crate should have `to` address"),
Self::EofInitcodesSizeLimit => write!(f, "EOF initcodes size limit"),
Self::EofInitcodesNumberLimit => write!(f, "EOF initcodes number limit"),
#[cfg(feature = "optimism")]
Self::DepositSystemTxPostRegolith => {
write!(
Expand Down
3 changes: 1 addition & 2 deletions crates/revm/src/handler/mainnet/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,9 @@ pub fn validate_initial_tx_gas<SPEC: Spec, DB: Database>(
let input = &env.tx.data;
let is_create = env.tx.transact_to.is_create();
let access_list = &env.tx.access_list;
let initcodes = &env.tx.eof_initcodes;

let initial_gas_spend =
gas::validate_initial_tx_gas(SPEC::SPEC_ID, input, is_create, access_list, initcodes);
gas::validate_initial_tx_gas(SPEC::SPEC_ID, input, is_create, access_list);

// Additional check to see if limit is big enough to cover initial gas.
if initial_gas_spend > env.tx.gas_limit {
Expand Down

0 comments on commit a67c30e

Please sign in to comment.