Skip to content
Draft
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
117 changes: 111 additions & 6 deletions gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ impl<'config> Gasometer<'config> {
used_gas: 0,
refunded_gas: 0,
config,
zero_data_len: 0,
non_zero_data_len: 0,
}),
}
}
Expand Down Expand Up @@ -249,11 +251,19 @@ impl<'config> Gasometer<'config> {
access_list_storage_len,
authorization_list_len,
} => {
let calldata_cost = zero_data_len as u64 * self.config.gas_transaction_zero_data
+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data;

// Store calldata info for EIP-7623 adjustment after execution
if self.config.has_eip_7623 {
self.inner_mut()?.zero_data_len = zero_data_len;
self.inner_mut()?.non_zero_data_len = non_zero_data_len;
}

#[deny(clippy::let_and_return)]
let cost = self.config.gas_transaction_call
+ zero_data_len as u64 * self.config.gas_transaction_zero_data
+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
+ access_list_address_len as u64 * self.config.gas_access_list_address
+ calldata_cost + access_list_address_len as u64
* self.config.gas_access_list_address
+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key
+ authorization_list_len as u64 * self.config.gas_per_empty_account_cost;

Expand All @@ -279,12 +289,21 @@ impl<'config> Gasometer<'config> {
initcode_cost,
authorization_list_len,
} => {
let calldata_cost = zero_data_len as u64 * self.config.gas_transaction_zero_data
+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data;

// Store calldata info for EIP-7623 adjustment after execution
if self.config.has_eip_7623 {
self.inner_mut()?.zero_data_len = zero_data_len;
self.inner_mut()?.non_zero_data_len = non_zero_data_len;
}

let mut cost = self.config.gas_transaction_create
+ zero_data_len as u64 * self.config.gas_transaction_zero_data
+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
+ access_list_address_len as u64 * self.config.gas_access_list_address
+ calldata_cost + access_list_address_len as u64
* self.config.gas_access_list_address
+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key
+ authorization_list_len as u64 * self.config.gas_per_empty_account_cost;

if self.config.max_initcode_size.is_some() {
cost += initcode_cost;
}
Expand All @@ -310,6 +329,28 @@ impl<'config> Gasometer<'config> {
snapshot: self.snapshot(),
});

// EIP-7623 validation: Check if gas limit meets floor requirement
if self.config.has_eip_7623 {
let tokens_in_calldata = match cost {
TransactionCost::Call { zero_data_len, non_zero_data_len, .. } => {
zero_data_len as u64 + non_zero_data_len as u64 * 4
}
TransactionCost::Create { zero_data_len, non_zero_data_len, .. } => {
zero_data_len as u64 + non_zero_data_len as u64 * 4
}
};

if tokens_in_calldata > 0 {
let min_floor_cost = 21000 + tokens_in_calldata * self.config.gas_calldata_floor_per_token;
let required_gas_limit = gas_cost.max(min_floor_cost);

if self.gas_limit < required_gas_limit {
self.inner = Err(ExitError::OutOfGas);
return Err(ExitError::OutOfGas);
}
}
}

if self.gas() < gas_cost {
self.inner = Err(ExitError::OutOfGas);
return Err(ExitError::OutOfGas);
Expand All @@ -328,6 +369,66 @@ impl<'config> Gasometer<'config> {
refunded_gas: inner.refunded_gas,
})
}

/// Apply post-execution adjustments for various EIPs.
/// This method handles gas adjustments that need to be calculated after transaction execution.
pub fn post_execution(&mut self) -> Result<(), ExitError> {
// Apply EIP-7623 adjustments
if self.config.has_eip_7623 {
self.apply_eip_7623_adjustment()?;
}

Ok(())
}

/// Apply EIP-7623 adjustment after execution.
fn apply_eip_7623_adjustment(&mut self) -> Result<(), ExitError> {
// Get values from config before borrowing inner
let gas_transaction_call = self.config.gas_transaction_call;
let gas_transaction_create = self.config.gas_transaction_create;
let gas_transaction_zero_data = self.config.gas_transaction_zero_data;
let gas_transaction_non_zero_data = self.config.gas_transaction_non_zero_data;
let gas_calldata_floor_per_token = self.config.gas_calldata_floor_per_token;

let inner = self.inner_mut()?;

// Skip if no calldata was recorded
if inner.zero_data_len == 0 && inner.non_zero_data_len == 0 {
return Ok(());
}

// Calculate standard calldata cost
let standard_calldata_cost = inner.zero_data_len as u64 * gas_transaction_zero_data
+ inner.non_zero_data_len as u64 * gas_transaction_non_zero_data;

// Calculate execution gas (excluding the initial 21000 and calldata costs)
let base_cost = if gas_transaction_call == 21000 {
21000
} else {
gas_transaction_create // For create transactions
};

// execution_gas = total_used_gas - base_cost - standard_calldata_cost
let execution_gas = inner
.used_gas
.saturating_sub(base_cost)
.saturating_sub(standard_calldata_cost);

// Calculate floor cost using EIP-7623 token formula
let tokens_in_calldata = inner.zero_data_len as u64 + inner.non_zero_data_len as u64 * 4;
let floor_cost = tokens_in_calldata * gas_calldata_floor_per_token;

// Apply EIP-7623 formula: 21000 + max(standard_calldata + execution, floor_calldata)
let standard_total = standard_calldata_cost + execution_gas;
if floor_cost > standard_total {
// Calculate what the final cost should be
let target_total = base_cost + floor_cost;
// Adjust used_gas to match the target
inner.used_gas = target_total;
}

Ok(())
}
}

/// Calculate the call transaction cost.
Expand Down Expand Up @@ -801,6 +902,10 @@ struct Inner<'config> {
used_gas: u64,
refunded_gas: i64,
config: &'config Config,
/// EIP-7623: Track zero data length for floor cost calculation
zero_data_len: usize,
/// EIP-7623: Track non-zero data length for floor cost calculation
non_zero_data_len: usize,
}

impl Inner<'_> {
Expand Down
26 changes: 26 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ pub struct Config {
pub gas_auth_base_cost: u64,
/// EIP-7702: Gas cost per empty account in authorization list
pub gas_per_empty_account_cost: u64,
/// EIP-7623: Gas cost floor per calldata token (zero or non-zero byte)
pub gas_calldata_floor_per_token: u64,
/// Whether to throw out of gas error when
/// CALL/CALLCODE/DELEGATECALL requires more than maximum amount
/// of gas.
Expand Down Expand Up @@ -300,6 +302,8 @@ pub struct Config {
pub has_eip_6780: bool,
/// Has EIP-7702. See [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md)
pub has_eip_7702: bool,
/// Has EIP-7623. See [EIP-7623](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7623.md)
pub has_eip_7623: bool,
}

impl Config {
Expand Down Expand Up @@ -360,6 +364,8 @@ impl Config {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand Down Expand Up @@ -420,6 +426,8 @@ impl Config {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand Down Expand Up @@ -470,6 +478,8 @@ impl Config {
has_eip_7702,
gas_auth_base_cost,
gas_per_empty_account_cost,
has_eip_7623,
gas_calldata_floor_per_token,
} = inputs;

// See https://eips.ethereum.org/EIPS/eip-2929
Expand Down Expand Up @@ -539,6 +549,8 @@ impl Config {
has_eip_7702,
gas_auth_base_cost,
gas_per_empty_account_cost,
has_eip_7623,
gas_calldata_floor_per_token,
}
}
}
Expand All @@ -561,6 +573,8 @@ struct DerivedConfigInputs {
has_eip_7702: bool,
gas_auth_base_cost: u64,
gas_per_empty_account_cost: u64,
has_eip_7623: bool,
gas_calldata_floor_per_token: u64,
}

impl DerivedConfigInputs {
Expand All @@ -581,6 +595,8 @@ impl DerivedConfigInputs {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand All @@ -601,6 +617,8 @@ impl DerivedConfigInputs {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand All @@ -621,6 +639,8 @@ impl DerivedConfigInputs {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand All @@ -642,6 +662,8 @@ impl DerivedConfigInputs {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand All @@ -663,6 +685,8 @@ impl DerivedConfigInputs {
has_eip_7702: false,
gas_auth_base_cost: 0,
gas_per_empty_account_cost: 0,
has_eip_7623: false,
gas_calldata_floor_per_token: 0,
}
}

Expand All @@ -687,6 +711,8 @@ impl DerivedConfigInputs {
gas_auth_base_cost: 12500,
// PER_EMPTY_ACCOUNT_COST from EIP-7702
gas_per_empty_account_cost: 25000,
has_eip_7623: true,
gas_calldata_floor_per_token: 10,
}
}
}
39 changes: 30 additions & 9 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,22 +496,29 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
}
}

match self.create_inner(
let result = match self.create_inner(
caller,
CreateScheme::Legacy { caller },
value,
init_code,
Some(gas_limit),
false,
) {
Capture::Exit((s, _, v)) => emit_exit!(s, v),
Capture::Exit((s, _, v)) => (s, v),
Capture::Trap(rt) => {
let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY);
cs.push(rt.0);
let (s, _, v) = self.execute_with_call_stack(&mut cs, None);
emit_exit!(s, v)
(s, v)
}
};

// Apply post-execution adjustments
if let Err(e) = self.state.metadata_mut().gasometer.post_execution() {
return emit_exit!(e.into(), Vec::new());
}

emit_exit!(result.0, result.1)
}

/// Execute a `CREATE2` transaction.
Expand Down Expand Up @@ -560,7 +567,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
}
}

match self.create_inner(
let result = match self.create_inner(
caller,
CreateScheme::Create2 {
caller,
Expand All @@ -572,14 +579,21 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
Some(gas_limit),
false,
) {
Capture::Exit((s, _, v)) => emit_exit!(s, v),
Capture::Exit((s, _, v)) => (s, v),
Capture::Trap(rt) => {
let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY);
cs.push(rt.0);
let (s, _, v) = self.execute_with_call_stack(&mut cs, None);
emit_exit!(s, v)
(s, v)
}
};

// Apply post-execution adjustments
if let Err(e) = self.state.metadata_mut().gasometer.post_execution() {
return emit_exit!(e.into(), Vec::new());
}

emit_exit!(result.0, result.1)
}

/// Execute a `CREATE` transaction that force the contract address
Expand Down Expand Up @@ -714,7 +728,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
apparent_value: value,
};

match self.call_inner(
let result = match self.call_inner(
address,
Some(Transfer {
source: caller,
Expand All @@ -728,14 +742,21 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
false,
context,
) {
Capture::Exit((s, v)) => emit_exit!(s, v),
Capture::Exit((s, v)) => (s, v),
Capture::Trap(rt) => {
let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY);
cs.push(rt.0);
let (s, _, v) = self.execute_with_call_stack(&mut cs, Some(caller));
emit_exit!(s, v)
(s, v)
}
};

// Apply post-execution adjustments
if let Err(e) = self.state.metadata_mut().gasometer.post_execution() {
return emit_exit!(e.into(), Vec::new());
}

emit_exit!(result.0, result.1)
}

/// Get used gas for the current executor, given the price.
Expand Down
Loading