use crate::core::utils::{U256_ONE, U256_VALUE_32, U256_ZERO, U64_MAX};
use crate::core::ExitError;
use crate::gasometer::consts;
use crate::Config;
use primitive_types::{H256, U256};
pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitError> {
if config.err_on_call_with_more_gas && U256::from(after_gas) < gas {
Err(ExitError::OutOfGas)
} else {
Ok(())
}
}
pub fn suicide_refund(already_removed: bool) -> i64 {
if already_removed {
0
} else {
i64::from(consts::R_SUICIDE)
}
}
#[allow(clippy::collapsible_else_if)]
pub fn sstore_refund(original: H256, current: H256, new: H256, config: &Config) -> i64 {
if config.sstore_gas_metering {
if current == new {
0
} else {
if original == current && new == H256::default() {
config.refund_sstore_clears
} else {
let mut refund = 0;
if original != H256::default() {
if current == H256::default() {
refund -= config.refund_sstore_clears;
} else if new == H256::default() {
refund += config.refund_sstore_clears;
}
}
if original == new {
let for_refund = if original == H256::default() {
config.gas_sstore_set - config.gas_sload
} else {
config.gas_sstore_reset - config.gas_sload
};
refund += i64::try_from(for_refund).unwrap_or(i64::MAX);
}
refund
}
}
} else {
if current != H256::default() && new == H256::default() {
config.refund_sstore_clears
} else {
0
}
}
}
pub fn create2_cost(len: U256) -> Result<u64, ExitError> {
let base = U256::from(consts::G_CREATE);
let sha_addup_base = len / U256_VALUE_32
+ if len % U256_VALUE_32 == U256_ZERO {
U256_ZERO
} else {
U256_ONE
};
let sha_addup = U256::from(consts::G_SHA3WORD)
.checked_mul(sha_addup_base)
.ok_or(ExitError::OutOfGas)?;
let gas = base.checked_add(sha_addup).ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
pub fn exp_cost(power: U256, config: &Config) -> Result<u64, ExitError> {
if power == U256_ZERO {
Ok(u64::from(consts::G_EXP))
} else {
let gas = U256::from(consts::G_EXP)
.checked_add(
U256::from(config.gas_expbyte)
.checked_mul(U256::from(super::utils::log2floor(power) / 8 + 1))
.ok_or(ExitError::OutOfGas)?,
)
.ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
}
pub fn verylowcopy_cost(len: U256) -> Result<u64, ExitError> {
let wordd = len / U256_VALUE_32;
let is_wordr = (len % U256_VALUE_32) == U256_ZERO;
let gas = U256::from(consts::G_VERYLOW)
.checked_add(
U256::from(consts::G_COPY)
.checked_mul(if is_wordr { wordd } else { wordd + U256_ONE })
.ok_or(ExitError::OutOfGas)?,
)
.ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
pub fn ext_codecopy_cost(len: U256, is_cold: bool, config: &Config) -> Result<u64, ExitError> {
let wordd = len / U256_VALUE_32;
let is_wordr = (len % U256_VALUE_32) == U256_ZERO;
let gas = U256::from(non_delegated_access_cost(
is_cold,
config.gas_ext_code,
config,
))
.checked_add(
U256::from(consts::G_COPY)
.checked_mul(if is_wordr { wordd } else { wordd + U256_ONE })
.ok_or(ExitError::OutOfGas)?,
)
.ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
pub fn log_cost(n: u8, len: U256) -> Result<u64, ExitError> {
let gas = U256::from(consts::G_LOG)
.checked_add(
U256::from(consts::G_LOGDATA)
.checked_mul(len)
.ok_or(ExitError::OutOfGas)?,
)
.ok_or(ExitError::OutOfGas)?
.checked_add(U256::from(consts::G_LOGTOPIC * u32::from(n)))
.ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
pub fn sha3_cost(len: U256) -> Result<u64, ExitError> {
let wordd = len / U256_VALUE_32;
let is_wordr = (len % U256_VALUE_32) == U256_ZERO;
let gas = U256::from(consts::G_SHA3)
.checked_add(
U256::from(consts::G_SHA3WORD)
.checked_mul(if is_wordr { wordd } else { wordd + U256_ONE })
.ok_or(ExitError::OutOfGas)?,
)
.ok_or(ExitError::OutOfGas)?;
if gas > U64_MAX {
return Err(ExitError::OutOfGas);
}
Ok(gas.as_u64())
}
pub const fn sload_cost(is_cold: bool, config: &Config) -> u64 {
if config.increase_state_access_gas {
if is_cold {
config.gas_sload_cold
} else {
config.gas_storage_read_warm
}
} else {
config.gas_sload
}
}
pub const fn storage_read_warm(config: &Config) -> u64 {
config.gas_storage_read_warm
}
#[allow(clippy::collapsible_else_if)]
pub fn sstore_cost(
original: H256,
current: H256,
new: H256,
gas: u64,
is_cold: bool,
config: &Config,
) -> Result<u64, ExitError> {
let gas_cost = if config.estimate {
config.gas_sstore_set
} else {
if config.sstore_gas_metering {
if config.sstore_revert_under_stipend && gas <= config.call_stipend {
return Err(ExitError::OutOfGas);
}
if new == current {
config.gas_sload
} else {
if original == current {
if original.is_zero() {
config.gas_sstore_set
} else {
config.gas_sstore_reset
}
} else {
config.gas_sload
}
}
} else {
if current.is_zero() && !new.is_zero() {
config.gas_sstore_set
} else {
config.gas_sstore_reset
}
}
};
Ok(
if is_cold {
gas_cost + config.gas_sload_cold
} else {
gas_cost
},
)
}
pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
let eip161 = !config.empty_considered_exists;
let should_charge_topup = if eip161 {
value != U256_ZERO && !target_exists
} else {
!target_exists
};
let suicide_gas_topup = if should_charge_topup {
config.gas_suicide_new_account
} else {
0
};
let mut gas = config.gas_suicide + suicide_gas_topup;
if config.increase_state_access_gas && is_cold {
gas += config.gas_account_access_cold;
}
gas
}
#[allow(clippy::fn_params_excessive_bools)]
pub fn call_cost(
value: U256,
is_cold: bool,
delegated_designator_is_cold: Option<bool>,
is_call_or_callcode: bool,
is_call_or_staticcall: bool,
new_account: bool,
config: &Config,
) -> u64 {
let transfers_value = value != U256::default();
address_access_cost(
is_cold,
delegated_designator_is_cold,
config.gas_call,
config,
) + xfer_cost(is_call_or_callcode, transfers_value)
+ new_cost(is_call_or_staticcall, new_account, transfers_value, config)
}
pub const fn non_delegated_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 {
match (config.increase_state_access_gas, is_cold) {
(false, _) => regular_value,
(true, true) => config.gas_account_access_cold,
(true, false) => config.gas_storage_read_warm,
}
}
pub const fn address_access_cost(
is_cold: bool,
delegated_designator_is_cold: Option<bool>,
regular_value: u64,
config: &Config,
) -> u64 {
if config.increase_state_access_gas {
let mut gas = if is_cold {
config.gas_account_access_cold
} else {
config.gas_storage_read_warm
};
if config.has_authorization_list {
if let Some(target_is_cold) = delegated_designator_is_cold {
if target_is_cold {
gas += config.gas_account_access_cold;
} else {
gas += config.gas_storage_read_warm;
}
}
}
gas
} else {
regular_value
}
}
fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 {
if is_call_or_callcode && transfers_value {
u64::from(consts::G_CALLVALUE)
} else {
0
}
}
fn new_cost(
is_call_or_staticcall: bool,
new_account: bool,
transfers_value: bool,
config: &Config,
) -> u64 {
let eip161 = !config.empty_considered_exists;
if is_call_or_staticcall {
if eip161 {
if transfers_value && new_account {
u64::from(consts::G_NEWACCOUNT)
} else {
0
}
} else if new_account {
u64::from(consts::G_NEWACCOUNT)
} else {
0
}
} else {
0
}
}