use evm_interpreter::ExitException;
use evm_interpreter::uint::{H256, U256, U256Ext};
use super::consts::*;
use crate::standard::Config;
pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitException> {
if !config.eip150_no_err_on_call_with_more_gas && U256::from(after_gas) < gas {
Err(ExitException::OutOfGas)
} else {
Ok(())
}
}
pub fn suicide_refund(already_removed: bool) -> i64 {
if already_removed { 0 } else { R_SUICIDE }
}
#[allow(clippy::collapsible_else_if)]
pub fn sstore_refund(original: H256, current: H256, new: H256, config: &Config) -> i64 {
if config.eip2200_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 {
if original == H256::default() {
refund += (config.gas_sstore_set() - config.gas_sload()) as i64;
} else {
refund += (config.gas_sstore_reset() - config.gas_sload()) as i64;
}
}
refund
}
}
} else {
if current != H256::default() && new == H256::default() {
config.refund_sstore_clears()
} else {
0
}
}
}
pub fn create2_cost(len: U256) -> Result<u64, ExitException> {
let base = U256::from(G_CREATE);
let sha_addup_base = len / U256::VALUE_32
+ if (len % U256::VALUE_32).is_zero() {
U256::ZERO
} else {
U256::ONE
};
let sha_addup = U256::from(G_SHA3WORD)
.checked_mul(sha_addup_base)
.ok_or(ExitException::OutOfGas)?;
let gas = base.checked_add(sha_addup).ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
pub fn exp_cost(power: U256, config: &Config) -> Result<u64, ExitException> {
if power == U256::ZERO {
Ok(G_EXP)
} else {
let gas = U256::from(G_EXP)
.checked_add(
U256::from(config.gas_expbyte())
.checked_mul(U256::from(power.log2floor() / 8 + 1))
.ok_or(ExitException::OutOfGas)?,
)
.ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
}
pub fn verylowcopy_cost(len: U256) -> Result<u64, ExitException> {
let wordd = len / U256::VALUE_32;
let wordr = len % U256::VALUE_32;
let gas = U256::from(G_VERYLOW)
.checked_add(
U256::from(G_COPY)
.checked_mul(if wordr == U256::ZERO {
wordd
} else {
wordd + U256::ONE
})
.ok_or(ExitException::OutOfGas)?,
)
.ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
pub fn extcodecopy_cost(len: U256, is_cold: bool, config: &Config) -> Result<u64, ExitException> {
let wordd = len / U256::VALUE_32;
let wordr = len % U256::VALUE_32;
let gas = U256::from(address_access_cost(is_cold, config.gas_ext_code(), config))
.checked_add(
U256::from(G_COPY)
.checked_mul(if wordr == U256::ZERO {
wordd
} else {
wordd + U256::ONE
})
.ok_or(ExitException::OutOfGas)?,
)
.ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
pub fn log_cost(n: u8, len: U256) -> Result<u64, ExitException> {
let gas = U256::from(G_LOG)
.checked_add(
U256::from(G_LOGDATA)
.checked_mul(len)
.ok_or(ExitException::OutOfGas)?,
)
.ok_or(ExitException::OutOfGas)?
.checked_add(U256::from(G_LOGTOPIC * n as u64))
.ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
pub fn sha3_cost(len: U256) -> Result<u64, ExitException> {
let wordd = len / U256::VALUE_32;
let wordr = len % U256::VALUE_32;
let gas = U256::from(G_SHA3)
.checked_add(
U256::from(G_SHA3WORD)
.checked_mul(if wordr == U256::ZERO {
wordd
} else {
wordd + U256::ONE
})
.ok_or(ExitException::OutOfGas)?,
)
.ok_or(ExitException::OutOfGas)?;
if gas > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas);
}
Ok(gas.low_u64())
}
pub fn sload_cost(is_cold: bool, config: &Config) -> u64 {
if config.eip2929_increase_state_access_gas {
if is_cold {
config.gas_sload_cold()
} else {
config.gas_storage_read_warm()
}
} else {
config.gas_sload()
}
}
#[allow(clippy::collapsible_else_if)]
pub fn sstore_cost(
original: H256,
current: H256,
new: H256,
gas: u64,
is_cold: bool,
config: &Config,
) -> Result<u64, ExitException> {
let gas_cost = if config.eip2200_sstore_gas_metering {
if config.eip2200_sstore_revert_under_stipend && gas <= config.call_stipend() {
return Err(ExitException::OutOfGas);
}
if new == current {
config.gas_sload()
} else {
if original == current {
if original == H256::zero() {
config.gas_sstore_set()
} else {
config.gas_sstore_reset()
}
} else {
config.gas_sload()
}
}
} else {
if current == H256::zero() && new != H256::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 tload_cost(config: &Config) -> Result<u64, ExitException> {
Ok(config.gas_storage_read_warm())
}
pub fn tstore_cost(config: &Config) -> Result<u64, ExitException> {
Ok(config.gas_storage_read_warm())
}
pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
let eip161 = config.runtime.eip161_empty_check;
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.eip2929_increase_state_access_gas && is_cold {
gas += config.gas_account_access_cold()
}
gas
}
pub fn call_cost(
value: U256,
is_cold: 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, config.gas_call(), config)
+ xfer_cost(is_call_or_callcode, transfers_value)
+ new_cost(is_call_or_staticcall, new_account, transfers_value, config)
}
pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 {
if config.eip2929_increase_state_access_gas {
if is_cold {
config.gas_account_access_cold()
} else {
config.gas_storage_read_warm()
}
} else {
regular_value
}
}
fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 {
if is_call_or_callcode && transfers_value {
G_CALLVALUE
} else {
0
}
}
fn new_cost(
is_call_or_staticcall: bool,
new_account: bool,
transfers_value: bool,
config: &Config,
) -> u64 {
let eip161 = config.runtime.eip161_empty_check;
if is_call_or_staticcall {
if eip161 {
if transfers_value && new_account {
G_NEWACCOUNT
} else {
0
}
} else if new_account {
G_NEWACCOUNT
} else {
0
}
} else {
0
}
}
pub fn memory_gas(a: usize) -> Result<u64, ExitException> {
let a = a as u64;
G_MEMORY
.checked_mul(a)
.ok_or(ExitException::OutOfGas)?
.checked_add(a.checked_mul(a).ok_or(ExitException::OutOfGas)? / 512)
.ok_or(ExitException::OutOfGas)
}