Skip to content

refactor(levm): extract common gas calculation to auxiliar function #3401

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

Closed
wants to merge 3 commits into from
Closed
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
211 changes: 105 additions & 106 deletions crates/vm/levm/src/gas_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub const SELFDESTRUCT_REFUND: u64 = 24000;
pub const DEFAULT_STATIC: u64 = 0;
pub const DEFAULT_COLD_DYNAMIC: u64 = 2600;
pub const DEFAULT_WARM_DYNAMIC: u64 = 100;
pub const DEFAULT_POSITIVE_VALUE: u64 = 9000;
pub const DEFAULT_POSITIVE_VALUE_STIPEND: u64 = 2300;

pub const SLOAD_STATIC: u64 = 0;
pub const SLOAD_COLD_DYNAMIC: u64 = 2100;
Expand Down Expand Up @@ -124,27 +126,8 @@ pub const EXTCODECOPY_DYNAMIC_BASE: u64 = 3;
pub const EXTCODECOPY_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
pub const EXTCODECOPY_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;

pub const CALL_STATIC: u64 = DEFAULT_STATIC;
pub const CALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
pub const CALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
pub const CALL_POSITIVE_VALUE: u64 = 9000;
pub const CALL_POSITIVE_VALUE_STIPEND: u64 = 2300;
pub const CALL_TO_EMPTY_ACCOUNT: u64 = 25000;

pub const CALLCODE_STATIC: u64 = DEFAULT_STATIC;
pub const CALLCODE_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
pub const CALLCODE_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
pub const CALLCODE_POSITIVE_VALUE: u64 = 9000;
pub const CALLCODE_POSITIVE_VALUE_STIPEND: u64 = 2300;

pub const DELEGATECALL_STATIC: u64 = DEFAULT_STATIC;
pub const DELEGATECALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
pub const DELEGATECALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;

pub const STATICCALL_STATIC: u64 = DEFAULT_STATIC;
pub const STATICCALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
pub const STATICCALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;

// Costs in gas for call opcodes
pub const WARM_ADDRESS_ACCESS_COST: u64 = 100;
pub const COLD_ADDRESS_ACCESS_COST: u64 = 2600;
Expand Down Expand Up @@ -647,6 +630,7 @@ pub fn extcodehash(address_was_cold: bool) -> Result<u64, VMError> {
)
}

// CALL opcodes - CALL, CALLCODE, DELEGATECALL, STATICCALL
#[allow(clippy::too_many_arguments)]
pub fn call(
new_memory_size: usize,
Expand All @@ -657,40 +641,24 @@ pub fn call(
gas_from_stack: U256,
gas_left: u64,
) -> Result<(u64, u64), VMError> {
let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
let value_is_zero = value_to_transfer.is_zero();
let positive_value_cost = Some(positive_value_cost(value_is_zero));
let value_to_empty_account = Some(value_to_empty_account(address_is_empty, value_is_zero));

let address_access_cost = address_access_cost(
let call_gas_costs = calculate_call_gas_costs(
new_memory_size,
current_memory_size,
address_was_cold,
CALL_STATIC,
CALL_COLD_DYNAMIC,
CALL_WARM_DYNAMIC,
positive_value_cost,
value_to_empty_account,
)?;
let positive_value_cost = if !value_to_transfer.is_zero() {
CALL_POSITIVE_VALUE
} else {
0
};

let value_to_empty_account = if address_is_empty && !value_to_transfer.is_zero() {
CALL_TO_EMPTY_ACCOUNT
} else {
0
};

let call_gas_costs = memory_expansion_cost
.checked_add(address_access_cost)
.ok_or(OutOfGas)?
.checked_add(positive_value_cost)
.ok_or(OutOfGas)?
.checked_add(value_to_empty_account)
.ok_or(OutOfGas)?;

calculate_cost_and_gas_limit_call(
value_to_transfer.is_zero(),
gas_from_stack,
gas_left,
call_gas_costs,
CALL_POSITIVE_VALUE_STIPEND,
DEFAULT_POSITIVE_VALUE_STIPEND,
)
}

Expand All @@ -702,31 +670,23 @@ pub fn callcode(
gas_from_stack: U256,
gas_left: u64,
) -> Result<(u64, u64), VMError> {
let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
let address_access_cost = address_access_cost(
let value_is_zero = value_to_transfer.is_zero();
let positive_value_cost = Some(positive_value_cost(value_is_zero));

let call_gas_costs = calculate_call_gas_costs(
new_memory_size,
current_memory_size,
address_was_cold,
DELEGATECALL_STATIC,
DELEGATECALL_COLD_DYNAMIC,
DELEGATECALL_WARM_DYNAMIC,
positive_value_cost,
None,
)?;

let positive_value_cost = if !value_to_transfer.is_zero() {
CALLCODE_POSITIVE_VALUE
} else {
0
};
let call_gas_costs = memory_expansion_cost
.checked_add(address_access_cost)
.ok_or(OutOfGas)?
.checked_add(positive_value_cost)
.ok_or(OutOfGas)?;

calculate_cost_and_gas_limit_call(
value_to_transfer.is_zero(),
value_is_zero,
gas_from_stack,
gas_left,
call_gas_costs,
CALLCODE_POSITIVE_VALUE_STIPEND,
DEFAULT_POSITIVE_VALUE_STIPEND,
)
}

Expand All @@ -737,19 +697,14 @@ pub fn delegatecall(
gas_from_stack: U256,
gas_left: u64,
) -> Result<(u64, u64), VMError> {
let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;

let address_access_cost = address_access_cost(
let call_gas_costs = calculate_call_gas_costs(
new_memory_size,
current_memory_size,
address_was_cold,
DELEGATECALL_STATIC,
DELEGATECALL_COLD_DYNAMIC,
DELEGATECALL_WARM_DYNAMIC,
None,
None,
)?;

let call_gas_costs = memory_expansion_cost
.checked_add(address_access_cost)
.ok_or(OutOfGas)?;

calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
}

Expand All @@ -760,20 +715,90 @@ pub fn staticcall(
gas_from_stack: U256,
gas_left: u64,
) -> Result<(u64, u64), VMError> {
let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
let call_gas_costs = calculate_call_gas_costs(
new_memory_size,
current_memory_size,
address_was_cold,
None,
None,
)?;

calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
}

/// Calculates the gas cost for sending more than 0 wei.
fn positive_value_cost(value_is_zero: bool) -> u64 {
if !value_is_zero {
DEFAULT_POSITIVE_VALUE
} else {
0
}
}

/// Calculates the gas cost in case an empty account is "being created" in the state, meaning that more than 0
/// wei is being sent to a previously empty account (no balance, no nonce, no code).
fn value_to_empty_account(address_is_empty: bool, value_is_zero: bool) -> u64 {
if address_is_empty && !value_is_zero {
CALL_TO_EMPTY_ACCOUNT
} else {
0
}
}

/// Sums all the different gas costs for CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
/// Memory expansion and address access cost are common to all. Costs for transferring positive
/// value and transferring to empty accounts are custom for each and therefore might exist or not.
fn calculate_call_gas_costs(
new_memory_size: usize,
current_memory_size: usize,
address_was_cold: bool,
positive_value_cost: Option<u64>,
value_to_empty_account: Option<u64>,
) -> Result<u64, VMError> {
// Calculate memory expansion cost (common to all CALL opcodes)
let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
// Calculate address access cost (common to all CALL opcodes)
let address_access_cost = address_access_cost(
address_was_cold,
STATICCALL_STATIC,
STATICCALL_COLD_DYNAMIC,
STATICCALL_WARM_DYNAMIC,
DEFAULT_STATIC,
DEFAULT_COLD_DYNAMIC,
DEFAULT_WARM_DYNAMIC,
)?;

let call_gas_costs = memory_expansion_cost
// Sum all costs. Positive value cost and value to empty account costs might exist or not for different CALL opcodes.
// In case they don't exists (value is None), 0 is added to the total costs.
Ok(memory_expansion_cost
.checked_add(address_access_cost)
.ok_or(OutOfGas)?;
.ok_or(OutOfGas)?
.checked_add(positive_value_cost.unwrap_or_default())
.ok_or(OutOfGas)?
.checked_add(value_to_empty_account.unwrap_or_default())
.ok_or(OutOfGas)?)
}

calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
fn calculate_cost_and_gas_limit_call(
value_is_zero: bool,
gas_from_stack: U256,
gas_left: u64,
call_gas_costs: u64,
stipend: u64,
) -> Result<(u64, u64), VMError> {
let gas_stipend = if value_is_zero { 0 } else { stipend };
let gas_left = gas_left.checked_sub(call_gas_costs).ok_or(OutOfGas)?;

// EIP 150, https://eips.ethereum.org/EIPS/eip-150
let max_gas_for_call = gas_left.checked_sub(gas_left / 64).ok_or(OutOfGas)?;

let gas: u64 = gas_from_stack
.min(max_gas_for_call.into())
.try_into()
.map_err(|_err| ExceptionalHalt::OutOfGas)?;

Ok((
gas.checked_add(call_gas_costs)
.ok_or(ExceptionalHalt::OutOfGas)?,
gas.checked_add(gas_stipend)
.ok_or(ExceptionalHalt::OutOfGas)?,
))
}

pub fn fake_exponential(factor: U256, numerator: U256, denominator: U256) -> Result<U256, VMError> {
Expand Down Expand Up @@ -911,32 +936,6 @@ pub fn max_message_call_gas(current_call_frame: &CallFrame) -> Result<u64, VMErr
Ok(remaining_gas)
}

fn calculate_cost_and_gas_limit_call(
value_is_zero: bool,
gas_from_stack: U256,
gas_left: u64,
call_gas_costs: u64,
stipend: u64,
) -> Result<(u64, u64), VMError> {
let gas_stipend = if value_is_zero { 0 } else { stipend };
let gas_left = gas_left.checked_sub(call_gas_costs).ok_or(OutOfGas)?;

// EIP 150, https://eips.ethereum.org/EIPS/eip-150
let max_gas_for_call = gas_left.checked_sub(gas_left / 64).ok_or(OutOfGas)?;

let gas: u64 = gas_from_stack
.min(max_gas_for_call.into())
.try_into()
.map_err(|_err| ExceptionalHalt::OutOfGas)?;

Ok((
gas.checked_add(call_gas_costs)
.ok_or(ExceptionalHalt::OutOfGas)?,
gas.checked_add(gas_stipend)
.ok_or(ExceptionalHalt::OutOfGas)?,
))
}

pub fn bls12_msm(k: usize, discount_table: &[u64; 128], mul_cost: u64) -> Result<u64, VMError> {
if k == 0 {
return Ok(0);
Expand Down
Loading
Loading