Skip to content

Commit

Permalink
feat(EIP-7623): Increase calldata cost. backport from rel/v51 (#1965)
Browse files Browse the repository at this point in the history
* feat(EIP-7623): Increase calldata cost. backport from rel/v51

* bump devnet5 tests
  • Loading branch information
rakita authored Jan 6, 2025
1 parent 894bcfb commit 04688b7
Show file tree
Hide file tree
Showing 259 changed files with 369,816 additions and 160,591 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ethereum-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ jobs:
ethtests/EIPTests/StateTests/stEIP2537/ \
ethtests/EIPTests/StateTests/stEOF \
tests/eof_suite/eest/state_tests \
tests/eof_suite/evmone/state_tests \
# tests/prague_suite/state_tests
tests/pectra_devnet5/state_tests/prague/eip7623_increase_calldata_cost
- name: Run EOF validation tests
run: |
cross run --target ${{matrix.target}} --profile ${{ matrix.profile }} -p revme -- eof-validation \
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions crates/context/interface/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ pub enum InvalidTransaction {
/// - initial stipend gas
/// - gas for access list and input data
CallGasCostMoreThanGasLimit,
/// Gas floor calculated from EIP-7623 Increase calldata cost
/// is more than the gas limit.
///
/// Tx data is too large to be executed.
GasFloorMoreThanGasLimit,
/// EIP-3607 Reject transactions from senders with deployed code
RejectCallerWithCode,
/// Transaction account does not have enough amount of ether to cover transferred value and gas_limit*gas_price.
Expand Down Expand Up @@ -346,6 +351,9 @@ impl fmt::Display for InvalidTransaction {
Self::CallGasCostMoreThanGasLimit => {
write!(f, "call gas cost exceeds the gas limit")
}
Self::GasFloorMoreThanGasLimit => {
write!(f, "gas floor exceeds the gas limit")
}
Self::RejectCallerWithCode => {
write!(f, "reject transactions from senders with deployed code")
}
Expand Down
3 changes: 1 addition & 2 deletions crates/handler/interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ all = "warn"
[dependencies]
# revm
primitives.workspace = true
interpreter.workspace = true

[dev-dependencies]
database.workspace = true

[features]
default = ["std"]
std = []
serde = ["std", "primitives/serde", "interpreter/serde"]
serde = ["std", "primitives/serde"]
2 changes: 1 addition & 1 deletion crates/handler/interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ pub use post_execution::PostExecutionHandler;
pub use pre_execution::PreExecutionHandler;
pub use precompile_provider::PrecompileProvider;
pub use util::FrameOrResultGen;
pub use validation::ValidationHandler;
pub use validation::{InitialAndFloorGas, ValidationHandler};
10 changes: 10 additions & 0 deletions crates/handler/interface/src/post_execution.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
use crate::InitialAndFloorGas;

pub trait PostExecutionHandler {
type Context;
type Error;
type ExecResult;
type Output;

/// Calculate final refund.
fn eip7623_check_gas_floor(
&self,
context: &mut Self::Context,
exec_result: &mut Self::ExecResult,
init_and_floor_gas: InitialAndFloorGas,
);

/// Calculate final refund.
fn refund(
&self,
Expand Down
4 changes: 2 additions & 2 deletions crates/handler/interface/src/precompile_provider.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use interpreter::InterpreterResult;
use primitives::{Address, Bytes};

pub trait PrecompileProvider: Clone {
type Context;
type Output;
type Error;

/// Create a new precompile.
Expand All @@ -15,7 +15,7 @@ pub trait PrecompileProvider: Clone {
address: &Address,
bytes: &Bytes,
gas_limit: u64,
) -> Result<Option<InterpreterResult>, Self::Error>;
) -> Result<Option<Self::Output>, Self::Error>;

/// Get the warm addresses.
fn warm_addresses(&self) -> impl Iterator<Item = Address>;
Expand Down
15 changes: 14 additions & 1 deletion crates/handler/interface/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,18 @@ pub trait ValidationHandler {
fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error>;

/// Validate initial gas.
fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result<u64, Self::Error>;
fn validate_initial_tx_gas(
&self,
context: &Self::Context,
) -> Result<InitialAndFloorGas, Self::Error>;
}

/// Init and floor gas from transaction
#[derive(Clone, Copy, Debug, Default)]
pub struct InitialAndFloorGas {
/// Initial gas for transaction.
pub initial_gas: u64,
/// If transaction is a Call and Prague is enabled
/// floor_gas is at least amount of gas that is going to be spent.
pub floor_gas: u64,
}
4 changes: 2 additions & 2 deletions crates/handler/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<CTX, ERROR, PRECOMPILE, INSTRUCTION>
where
CTX: EthFrameContext,
ERROR: EthFrameError<CTX>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR, Output = InterpreterResult>,
{
/// Make call frame
#[inline]
Expand Down Expand Up @@ -456,7 +456,7 @@ impl<CTX, ERROR, PRECOMPILE, INSTRUCTION> Frame
where
CTX: EthFrameContext,
ERROR: EthFrameError<CTX>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR, Output = InterpreterResult>,
INSTRUCTION: InstructionProvider<WIRE = EthInterpreter<()>, Host = CTX>,
{
type Context = CTX;
Expand Down
12 changes: 12 additions & 0 deletions crates/handler/src/post_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ where
type ExecResult = FrameResult;
type Output = ResultAndState<HALTREASON>;

fn eip7623_check_gas_floor(
&self,
_context: &mut Self::Context,
exec_result: &mut Self::ExecResult,
init_and_floor_gas: handler_interface::InitialAndFloorGas,
) {
let gas_result = exec_result.gas_mut();
if gas_result.spent() < init_and_floor_gas.floor_gas {
let _ = gas_result.record_cost(init_and_floor_gas.floor_gas - gas_result.spent());
}
}

fn refund(
&self,
context: &mut Self::Context,
Expand Down
8 changes: 0 additions & 8 deletions crates/handler/src/pre_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,6 @@ where

// Load access list
context.load_access_list()?;
// if let Some(access_list) = context.tx().access_list().cloned() {
// for access_list in access_list.iter() {
// context.journal().warm_account_and_storage(
// access_list.0,
// access_list.1.map(|i| U256::from_be_bytes(i.0)),
// )?;
// }
// };

Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions crates/handler/src/precompile_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ where
{
type Context = CTX;
type Error = ERROR;
type Output = InterpreterResult;

fn new(context: &mut Self::Context) -> Self {
let spec = context.cfg().spec().into();
Expand Down
30 changes: 22 additions & 8 deletions crates/handler/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use context_interface::{
Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, TransactionGetter,
};
use core::cmp::{self, Ordering};
use handler_interface::ValidationHandler;
use interpreter::gas;
use handler_interface::{InitialAndFloorGas, ValidationHandler};
use interpreter::gas::{self};
use primitives::{B256, U256};
use specification::{eip4844, hardfork::SpecId};
use state::Account;
Expand Down Expand Up @@ -69,7 +69,10 @@ where
validate_tx_against_account::<CTX, ERROR>(&account, context)
}

fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result<u64, Self::Error> {
fn validate_initial_tx_gas(
&self,
context: &Self::Context,
) -> Result<InitialAndFloorGas, Self::Error> {
let spec = context.cfg().spec().into();
validate_initial_tx_gas::<&Self::Context, InvalidTransaction>(context, spec)
.map_err(Into::into)
Expand Down Expand Up @@ -328,15 +331,19 @@ where
}

/// Validate initial transaction gas.
pub fn validate_initial_tx_gas<CTX, Error>(context: CTX, spec_id: SpecId) -> Result<u64, Error>
pub fn validate_initial_tx_gas<CTX, Error>(
context: CTX,
spec_id: SpecId,
) -> Result<InitialAndFloorGas, Error>
where
CTX: TransactionGetter,
CTX: TransactionGetter + CfgGetter,
Error: From<InvalidTransaction>,
{
let spec = context.cfg().spec().into();
let tx = context.tx();
let (accounts, storages) = tx.access_list_nums().unwrap_or_default();

let initial_gas_spend = gas::validate_initial_tx_gas(
let gas = gas::calculate_initial_tx_gas(
spec_id,
tx.input(),
tx.kind().is_create(),
Expand All @@ -346,10 +353,17 @@ where
);

// Additional check to see if limit is big enough to cover initial gas.
if initial_gas_spend > tx.gas_limit() {
if gas.initial_gas > tx.gas_limit() {
return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into());
}
Ok(initial_gas_spend)

// EIP-7623: Increase calldata cost
// floor gas should be less than gas limit.
if spec.is_enabled_in(SpecId::PRAGUE) && gas.floor_gas > tx.gas_limit() {
return Err(InvalidTransaction::GasFloorMoreThanGasLimit.into());
};

Ok(gas)
}

/// Helper trait that summarizes ValidationHandler requirements from Context.
Expand Down
4 changes: 2 additions & 2 deletions crates/inspector/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use revm::{
interpreter_types::{Jumps, LoopControl},
table::CustomInstruction,
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, FrameInput, Host,
Instruction, InstructionResult, Interpreter, InterpreterTypes,
Instruction, InstructionResult, Interpreter, InterpreterResult, InterpreterTypes,
},
precompile::PrecompileErrors,
primitives::{Address, Log, U256},
Expand Down Expand Up @@ -254,7 +254,7 @@ where
+ Host
+ InspectorCtx<IT = EthInterpreter>,
ERROR: From<JournalDBError<CTX>> + From<PrecompileErrors>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR>,
PRECOMPILE: PrecompileProvider<Context = CTX, Error = ERROR, Output = InterpreterResult>,
{
type Context = CTX;
type Error = ERROR;
Expand Down
1 change: 1 addition & 0 deletions crates/interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bytecode.workspace = true
primitives.workspace = true
specification.workspace = true
context-interface.workspace = true
handler-interface.workspace = true

# optional
serde = { version = "1.0", default-features = false, features = [
Expand Down
59 changes: 40 additions & 19 deletions crates/interpreter/src/gas/calc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::constants::*;
use crate::{num_words, tri, SStoreResult, SelfDestructResult, StateLoad};
use context_interface::journaled_state::{AccountLoad, Eip7702CodeLoad};
use handler_interface::InitialAndFloorGas;
use primitives::U256;
use specification::{eip7702, hardfork::SpecId};

Expand Down Expand Up @@ -347,34 +348,31 @@ pub const fn memory_gas(num_words: usize) -> u64 {

/// Initial gas that is deducted for transaction to be included.
/// Initial gas contains initial stipend gas, gas for access list and input data.
pub fn validate_initial_tx_gas(
///
/// # Returns
///
/// - Intrinsic gas
/// - Number of tokens in calldata
pub fn calculate_initial_tx_gas(
spec_id: SpecId,
input: &[u8],
is_create: bool,
access_list_accounts: u64,
access_list_storages: u64,
authorization_list_num: u64,
) -> u64 {
let mut initial_gas = 0;
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;
) -> InitialAndFloorGas {
let mut gas = InitialAndFloorGas::default();

// Initdate stipend
initial_gas += zero_data_len * TRANSACTION_ZERO_DATA;
// EIP-2028: Transaction data gas cost reduction
initial_gas += non_zero_data_len
* if spec_id.is_enabled_in(SpecId::ISTANBUL) {
16
} else {
68
};
let tokens_in_calldata = get_tokens_in_calldata(input, spec_id.is_enabled_in(SpecId::ISTANBUL));
gas.initial_gas += tokens_in_calldata * STANDARD_TOKEN_COST;

// Get number of access list account and storages.
initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS;
initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY;
gas.initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS;
gas.initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY;

// Base stipend
initial_gas += if is_create {
gas.initial_gas += if is_create {
if spec_id.is_enabled_in(SpecId::HOMESTEAD) {
// EIP-2: Homestead Hard-fork Changes
53000
Expand All @@ -388,13 +386,36 @@ pub fn validate_initial_tx_gas(
// EIP-3860: Limit and meter initcode
// Init code stipend for bytecode analysis
if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create {
initial_gas += initcode_cost(input.len())
gas.initial_gas += initcode_cost(input.len())
}

// EIP-7702
if spec_id.is_enabled_in(SpecId::PRAGUE) {
initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST;
gas.initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST;

// Calculate gas floor for EIP-7623
gas.floor_gas = calc_tx_floor_cost(tokens_in_calldata);
}

initial_gas
gas
}

/// Retrieve the total number of tokens in calldata.
#[inline]
pub fn get_tokens_in_calldata(input: &[u8], is_istanbul: bool) -> u64 {
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;
let non_zero_data_multiplier = if is_istanbul {
// EIP-2028: Transaction data gas cost reduction
NON_ZERO_BYTE_MULTIPLIER_ISTANBUL
} else {
NON_ZERO_BYTE_MULTIPLIER
};
zero_data_len + non_zero_data_len * non_zero_data_multiplier
}

/// Calculate the transaction cost floor as specified in EIP-7623.
#[inline]
pub fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 {
tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000
}
16 changes: 13 additions & 3 deletions crates/interpreter/src/gas/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,19 @@ pub const SSTORE_SET: u64 = 20000;
pub const SSTORE_RESET: u64 = 5000;
pub const REFUND_SSTORE_CLEARS: i64 = 15000;

pub const TRANSACTION_ZERO_DATA: u64 = 4;
pub const TRANSACTION_NON_ZERO_DATA_INIT: u64 = 16;
pub const TRANSACTION_NON_ZERO_DATA_FRONTIER: u64 = 68;
/// The standard cost of calldata token.
pub const STANDARD_TOKEN_COST: u64 = 4;
/// The cost of a non-zero byte in calldata.
pub const NON_ZERO_BYTE_DATA_COST: u64 = 68;
/// The multiplier for a non zero byte in calldata.
pub const NON_ZERO_BYTE_MULTIPLIER: u64 = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST;
/// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028).
pub const NON_ZERO_BYTE_DATA_COST_ISTANBUL: u64 = 16;
/// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028).
pub const NON_ZERO_BYTE_MULTIPLIER_ISTANBUL: u64 =
NON_ZERO_BYTE_DATA_COST_ISTANBUL / STANDARD_TOKEN_COST;
// The cost floor per token as defined by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028).
pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10;

pub const EOF_CREATE_GAS: u64 = 32000;

Expand Down
Loading

0 comments on commit 04688b7

Please sign in to comment.