2021-04-08 14:52:10 +00:00
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
math , eth / common / eth_types ,
2021-04-22 16:05:58 +00:00
. / utils / [ macros_gen_opcodes , utils_numeric ] ,
2022-12-02 04:35:41 +00:00
. / op_codes , .. / .. / common / evmforks , .. / .. / errors
2021-04-08 14:52:10 +00:00
2022-09-26 04:56:54 +00:00
when defined ( evmc_enabled ) :
import evmc / evmc
2021-04-08 14:52:10 +00:00
# Gas Fee Schedule
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
type
GasFeeKind * = enum
GasZero , # Nothing paid for operations of the set Wzero.
GasBase , # Amount of gas to pay for operations of the set Wbase.
GasVeryLow , # Amount of gas to pay for operations of the set Wverylow.
GasLow , # Amount of gas to pay for operations of the set Wlow.
GasMid , # Amount of gas to pay for operations of the set Wmid.
GasHigh , # Amount of gas to pay for operations of the set Whigh.
GasExtCode , # Amount of gas to pay for operations of the set Wextcode.
GasBalance , # Amount of gas to pay for a BALANCE operation.
GasSload , # Paid for a SLOAD operation.
GasJumpDest , # Paid for a JUMPDEST operation.
GasSset , # Paid for an SSTORE operation when the storage value is set to non-zero from zero.
GasSreset , # Paid for an SSTORE operation when the storage value’ s zeroness remains unchanged or is set to zero.
2022-04-08 04:54:11 +00:00
RefundsClear , # Refund given (added into refund counter) when the storage value is set to zero from non-zero.
2021-04-08 14:52:10 +00:00
RefundSelfDestruct , # Refund given (added into refund counter) for self-destructing an account.
GasSelfDestruct , # Amount of gas to pay for a SELFDESTRUCT operation.
GasCreate , # Paid for a CREATE operation.
GasCodeDeposit , # Paid per byte for a CREATE operation to succeed in placing code into state.
GasCall , # Paid for a CALL operation.
GasCallValue , # Paid for a non-zero value transfer as part of the CALL operation.
GasCallStipend , # A stipend for the called contract subtracted from Gcallvalue for a non-zero value transfer.
GasNewAccount , # Paid for a CALL or SELFDESTRUCT operation which creates an account.
GasExp , # Partial payment for an EXP operation.
GasExpByte , # Partial payment when multiplied by ⌈log256(exponent)⌉ for the EXP operation.
GasMemory , # Paid for every additional word when expanding memory.
GasTXCreate , # Paid by all contract-creating transactions after the Homestead transition.
GasTXDataZero , # Paid for every zero byte of data or code for a transaction.
GasTXDataNonZero , # Paid for every non-zero byte of data or code for a transaction.
GasTransaction , # Paid for every transaction.
GasLog , # Partial payment for a LOG operation.
GasLogData , # Paid for each byte in a LOG operation’ s data.
GasLogTopic , # Paid for each topic of a LOG operation.
GasSha3 , # Paid for each SHA3 operation.
GasSha3Word , # Paid for each word (rounded up) for input data to a SHA3 operation.
GasCopy , # Partial payment for COPY operations, multiplied by words copied, rounded up.
GasBlockhash , # Payment for BLOCKHASH operation.
2023-01-04 13:11:33 +00:00
GasExtCodeHash , # Payment for contract's code hashing
GasInitcodeWord # Payment for each word (rounded up) for initcode
2023-03-21 13:27:12 +00:00
GasWarmStorageRead # Transient storage read and write cost.
2021-04-08 14:52:10 +00:00
GasFeeSchedule = array [ GasFeeKind , GasInt ]
GasParams * = object
# Yellow Paper, Appendix H - https://ethereum.github.io/yellowpaper/paper.pdf
# GasCost is a function of (σ , μ):
# - σ is the full system state
# - μ is the machine state
# In practice, we often require the following from
# - σ : an account address
# - μ: a value popped from the stack or its size.
case kind * : Op
of Sstore :
2022-09-26 04:56:54 +00:00
when defined ( evmc_enabled ) :
s_status * : evmc_storage_status
2022-04-08 04:54:11 +00:00
s_currentValue * : UInt256
s_originalValue * : UInt256
2021-04-08 14:52:10 +00:00
of Call , CallCode , DelegateCall , StaticCall :
c_isNewAccount * : bool
c_gasBalance * : GasInt
2022-04-08 04:54:11 +00:00
c_contractGas * : UInt256
2021-04-08 14:52:10 +00:00
c_currentMemSize * : GasNatural
c_memOffset * : GasNatural
c_memLength * : GasNatural
of Create :
cr_currentMemSize * : GasNatural
cr_memOffset * : GasNatural
cr_memLength * : GasNatural
of SelfDestruct :
sd_condition * : bool
else :
discard
GasCostKind * = enum
GckInvalidOp ,
GckFixed ,
GckDynamic ,
GckMemExpansion ,
GckComplex
GasResult = tuple [ gasCost , gasRefund : GasInt ]
GasCost = object
case kind * : GasCostKind
of GckInvalidOp :
discard
of GckFixed :
cost * : GasInt
of GckDynamic :
2023-02-14 14:37:21 +00:00
d_handler * : proc ( value : UInt256 ) : GasInt
{. nimcall , gcsafe , raises : [ CatchableError ] . }
2021-04-08 14:52:10 +00:00
of GckMemExpansion :
2023-02-14 14:37:21 +00:00
m_handler * : proc ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt
{. nimcall , gcsafe , raises : [ CatchableError ] . }
2021-04-08 14:52:10 +00:00
of GckComplex :
2023-02-14 14:37:21 +00:00
c_handler * : proc ( value : UInt256 , gasParams : GasParams ) : GasResult
{. nimcall , gcsafe , raises : [ CatchableError ] . }
2021-04-08 14:52:10 +00:00
# We use gasCost/gasRefund for:
# - Properly log and order cost and refund (for Sstore especially)
# - Allow to use unsigned integer in the future
# - CALL instruction requires passing the child message gas (Ccallgas in yellow paper)
GasCosts * = array [ Op , GasCost ]
const
2021-06-28 13:06:29 +00:00
# From EIP-2929
2021-04-08 14:52:10 +00:00
ColdSloadCost * = 2100
ColdAccountAccessCost * = 2600
WarmStorageReadCost * = 100
2021-06-28 13:06:29 +00:00
# From EIP-2930 (Berlin).
ACCESS_LIST_STORAGE_KEY_COST * = 1900 . GasInt
ACCESS_LIST_ADDRESS_COST * = 2400 . GasInt
2022-09-26 12:23:54 +00:00
when defined ( evmc_enabled ) :
type
# The gas cost specification for storage instructions.
StorageCostSpec = object
netCost : bool # Is this net gas cost metering schedule?
warmAccess : int16 # Storage warm access cost, YP: G_{warmaccess}
sset : int16 # Storage addition cost, YP: G_{sset}
reset : int16 # Storage modification cost, YP: G_{sreset}
clear : int16 # Storage deletion refund, YP: R_{sclear}
StorageStoreCost * = object
gasCost * : int16
gasRefund * : int16
# Table of gas cost specification for storage instructions per EVM revision.
2022-12-02 04:35:41 +00:00
func storageCostSpec ( ) : array [ EVMFork , StorageCostSpec ] {. compileTime . } =
2022-09-26 12:23:54 +00:00
# Legacy cost schedule.
const revs = [
FkFrontier , FkHomestead , FkTangerine ,
FkSpurious , FkByzantium , FkPetersburg ]
for rev in revs :
result [ rev ] = StorageCostSpec (
netCost : false , warmAccess : 200 , sset : 20000 , reset : 5000 , clear : 15000 )
# Net cost schedule.
result [ FkConstantinople ] = StorageCostSpec (
netCost : true , warmAccess : 200 , sset : 20000 , reset : 5000 , clear : 15000 )
result [ FkIstanbul ] = StorageCostSpec (
netCost : true , warmAccess : 800 , sset : 20000 , reset : 5000 , clear : 15000 )
result [ FkBerlin ] = StorageCostSpec (
netCost : true , warmAccess : WarmStorageReadCost , sset : 20000 ,
reset : 5000 - ColdSloadCost , clear : 15000 )
result [ FkLondon ] = StorageCostSpec (
netCost : true , warmAccess : WarmStorageReadCost , sset : 20000 ,
reset : 5000 - ColdSloadCost , clear : 4800 )
result [ FkParis ] = result [ FkLondon ]
result [ FkShanghai ] = result [ FkLondon ]
result [ FkCancun ] = result [ FkLondon ]
proc legacySStoreCost ( e : var array [ evmc_storage_status , StorageStoreCost ] ,
c : StorageCostSpec ) {. compileTime . } =
e [ EVMC_STORAGE_ADDED ] = StorageStoreCost ( gasCost : c . sset , gasRefund : 0 )
e [ EVMC_STORAGE_DELETED ] = StorageStoreCost ( gasCost : c . reset , gasRefund : c . clear )
e [ EVMC_STORAGE_MODIFIED ] = StorageStoreCost ( gasCost : c . reset , gasRefund : 0 )
e [ EVMC_STORAGE_ASSIGNED ] = e [ EVMC_STORAGE_MODIFIED ]
e [ EVMC_STORAGE_DELETED_ADDED ] = e [ EVMC_STORAGE_ADDED ]
e [ EVMC_STORAGE_MODIFIED_DELETED ] = e [ EVMC_STORAGE_DELETED ]
e [ EVMC_STORAGE_DELETED_RESTORED ] = e [ EVMC_STORAGE_ADDED ]
e [ EVMC_STORAGE_ADDED_DELETED ] = e [ EVMC_STORAGE_DELETED ]
e [ EVMC_STORAGE_MODIFIED_RESTORED ] = e [ EVMC_STORAGE_MODIFIED ]
proc netSStoreCost ( e : var array [ evmc_storage_status , StorageStoreCost ] ,
c : StorageCostSpec ) {. compileTime . } =
e [ EVMC_STORAGE_ASSIGNED ] = StorageStoreCost ( gasCost : c . warmAccess , gasRefund : 0 )
e [ EVMC_STORAGE_ADDED ] = StorageStoreCost ( gasCost : c . sset , gasRefund : 0 )
e [ EVMC_STORAGE_DELETED ] = StorageStoreCost ( gasCost : c . reset , gasRefund : c . clear )
e [ EVMC_STORAGE_MODIFIED ] = StorageStoreCost ( gasCost : c . reset , gasRefund : 0 )
e [ EVMC_STORAGE_DELETED_ADDED ] = StorageStoreCost ( gasCost : c . warmAccess , gasRefund : - c . clear )
e [ EVMC_STORAGE_MODIFIED_DELETED ] = StorageStoreCost ( gasCost : c . warmAccess , gasRefund : c . clear )
e [ EVMC_STORAGE_DELETED_RESTORED ] = StorageStoreCost ( gasCost : c . warmAccess ,
gasRefund : c . reset - c . warmAccess - c . clear )
e [ EVMC_STORAGE_ADDED_DELETED ] = StorageStoreCost ( gasCost : c . warmAccess ,
gasRefund : c . sset - c . warmAccess )
e [ EVMC_STORAGE_MODIFIED_RESTORED ] = StorageStoreCost ( gasCost : c . warmAccess ,
gasRefund : c . reset - c . warm_access )
2022-12-02 04:35:41 +00:00
proc storageStoreCost ( ) : array [ EVMFork , array [ evmc_storage_status , StorageStoreCost ] ] {. compileTime . } =
2022-09-26 12:23:54 +00:00
const tbl = storageCostSpec ( )
2022-12-02 04:35:41 +00:00
for rev in EVMFork :
2022-09-26 12:23:54 +00:00
let c = tbl [ rev ]
if not c . netCost : # legacy
legacySStoreCost ( result [ rev ] , c )
else : # net cost
netSStoreCost ( result [ rev ] , c )
const
SstoreCost * = storageStoreCost ( )
2022-12-02 04:35:41 +00:00
template gasCosts ( fork : EVMFork , prefix , ResultGasCostsName : untyped ) =
2021-04-08 14:52:10 +00:00
## Generate the gas cost for each forks and store them in a const
## named `ResultGasCostsName`
const FeeSchedule = gasFees [ fork ]
# ############### Helper functions ##############################
func ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. inline . } =
# Input: size (in bytes)
# Yellow Paper:
# Memory expansion cost
# = Cmem(μ′ i) − Cmem(μi)
# μi is memory size before opcode execution
# μ'i is the memory size after opcode execution
# Cmem(a) ≡ Gmemory · a + a² / 512
#
# Except when memLength = 0, where per eq (297),
# M(currentMemSize, f, l) = currentMemSize
let
prevWords : int64 = currentMemSize . wordCount
newWords : int64 = ( memOffset + memLength ) . wordCount
if memLength = = 0 or newWords < = prevWords :
# Special subcase of memory-expansion cost
# currentMemSize - currentMemSize = 0
# "Referencing a zero length range ... does not require memory to be extended
# to the beginning of the range."
#
# Also, don't credit EVM code for allocating memory
# then accessing lots of low memory. memoryGasCost,
# via go-ethereum, checks for this as special case.
return 0
let
prevCost = prevWords * static ( FeeSchedule [ GasMemory ] ) +
( prevWords ^ 2 ) shr 9 # div 512
newCost = newWords * static ( FeeSchedule [ GasMemory ] ) +
( newWords ^ 2 ) shr 9 # div 512
# TODO: add logging
result = max ( newCost - prevCost , 0 )
when fork > = FkTangerine :
func ` prefix all_but_one_64th ` ( gas : GasInt ) : GasInt {. inline . } =
## Computes all but 1/64th
## L(n) ≡ n − ⌊n/64⌋ - (floored(n/64))
# Introduced in EIP-150 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
# Note: The all-but-one-64th calculation should occur after the memory expansion fee is taken
# https://github.com/ethereum/yellowpaper/pull/442
result = gas - ( gas shr 6 )
# ############### Opcode gas functions ##############################
2022-04-08 04:54:11 +00:00
func ` prefix gasExp ` ( value : UInt256 ) : GasInt {. nimcall . } =
2021-04-08 14:52:10 +00:00
## Value is the exponent
result = static FeeSchedule [ GasExp ]
if not value . isZero :
result + = static ( FeeSchedule [ GasExpByte ] ) * ( 1 + log256 ( value ) )
2022-04-08 04:54:11 +00:00
func ` prefix gasCreate ` ( value : UInt256 , gasParams : GasParams ) : GasResult {. nimcall . } =
2021-04-08 14:52:10 +00:00
if value . isZero :
result . gasCost = static ( FeeSchedule [ GasCodeDeposit ] ) * gasParams . cr_memLength
else :
result . gasCost = static ( FeeSchedule [ GasCreate ] ) +
2023-01-04 13:11:33 +00:00
( static ( FeeSchedule [ GasInitcodeWord ] ) * gasParams . cr_memLength . wordCount ) +
2021-04-08 14:52:10 +00:00
` prefix gasMemoryExpansion ` (
gasParams . cr_currentMemSize ,
gasParams . cr_memOffset ,
gasParams . cr_memLength )
func ` prefix gasSha3 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasSha3 ] ) +
static ( FeeSchedule [ GasSha3Word ] ) * ( memLength ) . wordCount
func ` prefix gasCopy ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = static ( FeeSchedule [ GasVeryLow ] ) +
static ( FeeSchedule [ GasCopy ] ) * memLength . wordCount
result + = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
func ` prefix gasExtCodeCopy ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = static ( FeeSchedule [ GasExtCode ] ) +
static ( FeeSchedule [ GasCopy ] ) * memLength . wordCount
result + = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
func ` prefix gasLoadStore ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = static ( FeeSchedule [ GasVeryLow ] )
result + = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
2022-04-08 04:54:11 +00:00
func ` prefix gasSstore ` ( value : UInt256 , gasParams : GasParams ) : GasResult {. nimcall . } =
2021-04-08 14:52:10 +00:00
## Value is word to save
2022-09-26 12:23:54 +00:00
when defined ( evmc_enabled ) :
const c = SStoreCost [ fork ]
let sc = c [ gasParams . s_status ]
result . gasCost = sc . gasCost
result . gasRefund = sc . gasRefund
2021-04-08 14:52:10 +00:00
else :
2022-09-26 12:23:54 +00:00
when fork > = FkBerlin :
# EIP2929
const
SLOAD_GAS = WarmStorageReadCost
SSTORE_RESET_GAS = 5000 - ColdSloadCost
else :
const
SLOAD_GAS = FeeSchedule [ GasSload ]
SSTORE_RESET_GAS = FeeSchedule [ GasSreset ]
2021-04-08 14:52:10 +00:00
2022-09-26 04:56:54 +00:00
const
2023-05-16 04:15:10 +00:00
NoopGas {. used . } = SLOAD_GAS # if the value doesn't change.
DirtyGas {. used . } = SLOAD_GAS # if a dirty value is changed.
InitGas {. used . } = FeeSchedule [ GasSset ] # from clean zero to non-zero
InitRefund {. used . } = FeeSchedule [ GasSset ] - SLOAD_GAS # resetting to the original zero value
CleanGas {. used . } = SSTORE_RESET_GAS # from clean non-zero to something else
CleanRefund {. used . } = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value
ClearRefund {. used . } = FeeSchedule [ RefundsClear ] # clearing an originally existing storage slot
2022-09-26 04:56:54 +00:00
2021-04-08 14:52:10 +00:00
when fork < FkConstantinople or fork = = FkPetersburg :
let isStorageEmpty = gasParams . s_currentValue . isZero
# Gas cost - literal translation of Yellow Paper
result . gasCost = if value . isZero . not and isStorageEmpty :
InitGas
else :
CleanGas
# Refund
if value . isZero and not isStorageEmpty :
result . gasRefund = ClearRefund
else :
# 0. If *gasleft* is less than or equal to 2300, fail the current call.
# 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
# 2. If current value does not equal new value:
# 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
# 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
# 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
# 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
# 2.2.1. If original value is not 0:
# 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
# 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
# 2.2.2. If original value equals new value (this storage slot is reset):
# 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
# 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
# Gas sentry honoured, do the actual gas calculation based on the stored value
if gasParams . s_currentValue = = value : # noop (1)
result . gasCost = NoopGas
return
if gasParams . s_originalValue = = gasParams . s_currentValue :
if gasParams . s_originalValue . isZero : # create slot (2.1.1)
result . gasCost = InitGas
return
if value . isZero : # delete slot (2.1.2b)
result . gasRefund = ClearRefund
result . gasCost = CleanGas # write existing slot (2.1.2)
return
if not gasParams . s_originalValue . isZero :
if gasParams . s_currentValue . isZero : # recreate slot (2.2.1.1)
result . gasRefund - = ClearRefund
if value . isZero : # delete slot (2.2.1.2)
result . gasRefund + = ClearRefund
if gasParams . s_originalValue = = value :
if gasParams . s_originalValue . isZero : # reset to original inexistent slot (2.2.2.1)
result . gasRefund + = InitRefund
else : # reset to original existing slot (2.2.2.2)
result . gasRefund + = CleanRefund
result . gasCost = DirtyGas # dirty update (2.2)
func ` prefix gasLog0 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasLog ] ) +
static ( FeeSchedule [ GasLogData ] ) * memLength
func ` prefix gasLog1 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasLog ] ) +
static ( FeeSchedule [ GasLogData ] ) * memLength +
static ( FeeSchedule [ GasLogTopic ] )
func ` prefix gasLog2 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasLog ] ) +
static ( FeeSchedule [ GasLogData ] ) * memLength +
static ( 2 * FeeSchedule [ GasLogTopic ] )
func ` prefix gasLog3 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasLog ] ) +
static ( FeeSchedule [ GasLogData ] ) * memLength +
static ( 3 * FeeSchedule [ GasLogTopic ] )
func ` prefix gasLog4 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = ` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
result + = static ( FeeSchedule [ GasLog ] ) +
static ( FeeSchedule [ GasLogData ] ) * memLength +
static ( 4 * FeeSchedule [ GasLogTopic ] )
2022-04-08 04:54:11 +00:00
func ` prefix gasCall ` ( value : UInt256 , gasParams : GasParams ) : GasResult {. nimcall . } =
2021-04-08 14:52:10 +00:00
# From the Yellow Paper, going through the equation from bottom to top
# https://ethereum.github.io/yellowpaper/paper.pdf#appendix.H
#
# More readable info on the subtleties wiki page: https://github.com/ethereum/wiki/wiki/Subtleties#other-operations
# CALL has a multi-part gas cost:
#
# - 700 base
# - 9000 additional if the value is nonzero
# - 25000 additional if the destination account does not yet exist
#
# The child message of a nonzero-value CALL operation (NOT the top-level message arising from a transaction!)
# gains an additional 2300 gas on top of the gas supplied by the calling account;
# this stipend can be considered to be paid out of the 9000 mandatory additional fee for nonzero-value calls.
# This ensures that a call recipient will always have enough gas to log that it received funds.
#
# EIP150 goes over computation: https://github.com/ethereum/eips/issues/150
#
# The discussion for the draft EIP-5, which proposes to change the CALL opcode also goes over
# the current implementation - https://github.com/ethereum/EIPs/issues/8
# First we have to take into account the costs of memory expansion:
# Note there is a "bug" in the Ethereum Yellow Paper
# - https://github.com/ethereum/yellowpaper/issues/325
# μg already includes memory expansion costs but it is not
# plainly explained n the CALL opcode details
# i.e. Cmem(μ′ i) − Cmem(μi)
# Yellow Paper: μ′ i ≡ M(M(μi,μs[3],μs[4]),μs[5],μs[6])
# M is the memory expansion function
# μ′ i is passed through gasParams.memRequested
# TODO:
# - Py-EVM has costs for both input and output memory expansion
# https://github.com/ethereum/py-evm/blob/eed0bfe4499b394ee58113408e487e7d35ab88d6/evm/vm/logic/call.py#L56-L57
# - Parity only for the largest expansion
# https://github.com/paritytech/parity/blob/af1088ef61323f171915555023d8e993aaaed755/ethcore/evm/src/interpreter/gasometer.rs#L192-L195
# - Go-Ethereum only has one cost
# https://github.com/ethereum/go-ethereum/blob/13af27641829f61d1e6b383e37aab6caae22f2c1/core/vm/gas_table.go#L334
# ⚠⚠ Py-EVM seems wrong if memory is needed for both in and out.
result . gasCost = ` prefix gasMemoryExpansion ` (
gasParams . c_currentMemSize ,
gasParams . c_memOffset ,
gasParams . c_memLength
)
# Cnew_account
if gasParams . c_isNewAccount and gasParams . kind = = Call :
when fork < FkSpurious :
# Pre-EIP161 all account creation calls consumed 25000 gas.
result . gasCost + = static ( FeeSchedule [ GasNewAccount ] )
else :
# Afterwards, only those transfering value:
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-158.md
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
if not value . isZero :
result . gasCost + = static ( FeeSchedule [ GasNewAccount ] )
# Cxfer
if not value . isZero and gasParams . kind in { Call , CallCode } :
result . gasCost + = static ( FeeSchedule [ GasCallValue ] )
# Cextra
result . gasCost + = static ( FeeSchedule [ GasCall ] )
# Cgascap
when fork > = FkTangerine :
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
let gas = ` prefix all_but_one_64th ` ( gasParams . c_gasBalance - result . gasCost )
if gasParams . c_contractGas > high ( GasInt ) . u256 or
gas < gasParams . c_contractGas . truncate ( GasInt ) :
result . gasRefund = gas
else :
result . gasRefund = gasParams . c_contractGas . truncate ( GasInt )
else :
if gasParams . c_contractGas > high ( GasInt ) . u256 :
raise newException ( TypeError , " GasInt Overflow ( " & $ gasParams . kind & " ) " & $ gasParams . c_contractGas )
result . gasRefund = gasParams . c_contractGas . truncate ( GasInt )
result . gasCost + = result . gasRefund
# Ccallgas - Gas sent to the child message
if not value . isZero and gasParams . kind in { Call , CallCode } :
result . gasRefund + = static ( FeeSchedule [ GasCallStipend ] )
func ` prefix gasHalt ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
` prefix gasMemoryExpansion ` ( currentMemSize , memOffset , memLength )
2022-04-08 04:54:11 +00:00
func ` prefix gasSelfDestruct ` ( value : UInt256 , gasParams : GasParams ) : GasResult {. nimcall . } =
2021-04-08 14:52:10 +00:00
result . gasCost + = static ( FeeSchedule [ GasSelfDestruct ] )
when fork > = FkTangerine :
if gasParams . sd_condition :
result . gasCost + = static ( FeeSchedule [ GasNewAccount ] )
func ` prefix gasCreate2 ` ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt {. nimcall . } =
result = static ( FeeSchedule [ GasSha3Word ] ) * ( memLength ) . wordCount
# ###################################################################################################
let ` ResultGasCostsName ` * {. inject , compileTime . } : GasCosts = block :
# We use a block expression to avoid name redefinition conflicts
# with "fixed" and "dynamic"
# Syntactic sugar
func fixed ( gasFeeKind : static [ GasFeeKind ] ) : GasCost =
GasCost ( kind : GckFixed , cost : static ( FeeSchedule [ gasFeeKind ] ) )
2023-02-14 14:37:21 +00:00
func dynamic ( handler : proc ( value : UInt256 ) : GasInt
{. nimcall , gcsafe , raises : [ CatchableError ] . } ) : GasCost =
GasCost ( kind : GckDynamic , d_handler : handler )
2021-04-08 14:52:10 +00:00
2023-02-14 14:37:21 +00:00
func memExpansion ( handler : proc ( currentMemSize , memOffset , memLength : GasNatural ) : GasInt
{. nimcall , gcsafe , raises : [ CatchableError ] . } ) : GasCost =
2021-04-08 14:52:10 +00:00
GasCost ( kind : GckMemExpansion , m_handler : handler )
2023-02-14 14:37:21 +00:00
func complex ( handler : proc ( value : UInt256 , gasParams : GasParams ) : GasResult
{. nimcall , gcsafe , raises : [ CatchableError ] . } ) : GasCost =
2021-04-08 14:52:10 +00:00
GasCost ( kind : GckComplex , c_handler : handler )
# Returned value
fill_enum_table_holes ( Op , GasCost ( kind : GckInvalidOp ) ) :
[
# 0s: Stop and Arithmetic Operations
Stop : fixed GasZero ,
Add : fixed GasVeryLow ,
Mul : fixed GasLow ,
Sub : fixed GasVeryLow ,
Div : fixed GasLow ,
Sdiv : fixed GasLow ,
Mod : fixed GasLow ,
Smod : fixed GasLow ,
Addmod : fixed GasMid ,
Mulmod : fixed GasMid ,
Exp : dynamic ` prefix gasExp ` ,
SignExtend : fixed GasLow ,
# 10s: Comparison & Bitwise Logic Operations
Lt : fixed GasVeryLow ,
Gt : fixed GasVeryLow ,
Slt : fixed GasVeryLow ,
Sgt : fixed GasVeryLow ,
Eq : fixed GasVeryLow ,
IsZero : fixed GasVeryLow ,
And : fixed GasVeryLow ,
Or : fixed GasVeryLow ,
Xor : fixed GasVeryLow ,
Not : fixed GasVeryLow ,
Byte : fixed GasVeryLow ,
Shl : fixed GasVeryLow ,
Shr : fixed GasVeryLow ,
Sar : fixed GasVeryLow ,
# 20s: SHA3
Sha3 : memExpansion ` prefix gasSha3 ` ,
# 30s: Environmental Information
Address : fixed GasBase ,
Balance : fixed GasBalance ,
Origin : fixed GasBase ,
Caller : fixed GasBase ,
CallValue : fixed GasBase ,
CallDataLoad : fixed GasVeryLow ,
CallDataSize : fixed GasBase ,
CallDataCopy : memExpansion ` prefix gasCopy ` ,
CodeSize : fixed GasBase ,
CodeCopy : memExpansion ` prefix gasCopy ` ,
GasPrice : fixed GasBase ,
2022-04-08 04:54:11 +00:00
ExtCodeSize : fixed GasExtCode ,
2021-04-08 14:52:10 +00:00
ExtCodeCopy : memExpansion ` prefix gasExtCodeCopy ` ,
ReturnDataSize : fixed GasBase ,
ReturnDataCopy : memExpansion ` prefix gasCopy ` ,
ExtCodeHash : fixed GasExtCodeHash ,
# 40s: Block Information
Blockhash : fixed GasBlockhash ,
Coinbase : fixed GasBase ,
Timestamp : fixed GasBase ,
Number : fixed GasBase ,
Difficulty : fixed GasBase ,
GasLimit : fixed GasBase ,
2021-05-15 08:31:58 +00:00
ChainIdOp : fixed GasBase ,
2021-04-08 14:52:10 +00:00
SelfBalance : fixed GasLow ,
2021-06-27 13:19:22 +00:00
BaseFee : fixed GasBase ,
2023-06-24 13:56:44 +00:00
BlobHash : fixed GasVeryLow ,
2021-04-08 14:52:10 +00:00
# 50s: Stack, Memory, Storage and Flow Operations
Pop : fixed GasBase ,
Mload : memExpansion ` prefix gasLoadStore ` ,
Mstore : memExpansion ` prefix gasLoadStore ` ,
Mstore8 : memExpansion ` prefix gasLoadStore ` ,
Sload : fixed GasSload ,
Sstore : complex ` prefix gasSstore ` ,
Jump : fixed GasMid ,
JumpI : fixed GasHigh ,
Pc : fixed GasBase ,
Msize : fixed GasBase ,
Gas : fixed GasBase ,
JumpDest : fixed GasJumpDest ,
2023-06-25 01:24:45 +00:00
# 5c & 5d: Transient storage operations
Tload : fixed GasWarmStorageRead ,
Tstore : fixed GasWarmStorageRead ,
2023-06-26 09:58:59 +00:00
# 5e: Memory copy
Mcopy : memExpansion ` prefix gasCopy ` ,
2021-04-08 14:52:10 +00:00
2023-01-04 13:11:33 +00:00
# 5f, 60s & 70s: Push Operations
Push0 : fixed GasBase ,
2021-04-08 14:52:10 +00:00
Push1 : fixed GasVeryLow ,
Push2 : fixed GasVeryLow ,
Push3 : fixed GasVeryLow ,
Push4 : fixed GasVeryLow ,
Push5 : fixed GasVeryLow ,
Push6 : fixed GasVeryLow ,
Push7 : fixed GasVeryLow ,
Push8 : fixed GasVeryLow ,
Push9 : fixed GasVeryLow ,
Push10 : fixed GasVeryLow ,
Push11 : fixed GasVeryLow ,
Push12 : fixed GasVeryLow ,
Push13 : fixed GasVeryLow ,
Push14 : fixed GasVeryLow ,
Push15 : fixed GasVeryLow ,
Push16 : fixed GasVeryLow ,
Push17 : fixed GasVeryLow ,
Push18 : fixed GasVeryLow ,
Push19 : fixed GasVeryLow ,
Push20 : fixed GasVeryLow ,
Push21 : fixed GasVeryLow ,
Push22 : fixed GasVeryLow ,
Push23 : fixed GasVeryLow ,
Push24 : fixed GasVeryLow ,
Push25 : fixed GasVeryLow ,
Push26 : fixed GasVeryLow ,
Push27 : fixed GasVeryLow ,
Push28 : fixed GasVeryLow ,
Push29 : fixed GasVeryLow ,
Push30 : fixed GasVeryLow ,
Push31 : fixed GasVeryLow ,
Push32 : fixed GasVeryLow ,
# 80s: Duplication Operations
Dup1 : fixed GasVeryLow ,
Dup2 : fixed GasVeryLow ,
Dup3 : fixed GasVeryLow ,
Dup4 : fixed GasVeryLow ,
Dup5 : fixed GasVeryLow ,
Dup6 : fixed GasVeryLow ,
Dup7 : fixed GasVeryLow ,
Dup8 : fixed GasVeryLow ,
Dup9 : fixed GasVeryLow ,
Dup10 : fixed GasVeryLow ,
Dup11 : fixed GasVeryLow ,
Dup12 : fixed GasVeryLow ,
Dup13 : fixed GasVeryLow ,
Dup14 : fixed GasVeryLow ,
Dup15 : fixed GasVeryLow ,
Dup16 : fixed GasVeryLow ,
# 90s: Exchange Operations
Swap1 : fixed GasVeryLow ,
Swap2 : fixed GasVeryLow ,
Swap3 : fixed GasVeryLow ,
Swap4 : fixed GasVeryLow ,
Swap5 : fixed GasVeryLow ,
Swap6 : fixed GasVeryLow ,
Swap7 : fixed GasVeryLow ,
Swap8 : fixed GasVeryLow ,
Swap9 : fixed GasVeryLow ,
Swap10 : fixed GasVeryLow ,
Swap11 : fixed GasVeryLow ,
Swap12 : fixed GasVeryLow ,
Swap13 : fixed GasVeryLow ,
Swap14 : fixed GasVeryLow ,
Swap15 : fixed GasVeryLow ,
Swap16 : fixed GasVeryLow ,
# a0s: Logging Operations
Log0 : memExpansion ` prefix gasLog0 ` ,
Log1 : memExpansion ` prefix gasLog1 ` ,
Log2 : memExpansion ` prefix gasLog2 ` ,
Log3 : memExpansion ` prefix gasLog3 ` ,
Log4 : memExpansion ` prefix gasLog4 ` ,
# f0s: System operations
Create : complex ` prefix gasCreate ` ,
Call : complex ` prefix gasCall ` ,
CallCode : complex ` prefix gasCall ` ,
Return : memExpansion ` prefix gasHalt ` ,
DelegateCall : complex ` prefix gasCall ` ,
Create2 : memExpansion ` prefix gasCreate2 ` ,
StaticCall : complex ` prefix gasCall ` ,
Revert : memExpansion ` prefix gasHalt ` ,
Invalid : fixed GasZero ,
SelfDestruct : complex ` prefix gasSelfDestruct `
]
# Generate the fork-specific gas costs tables
const
BaseGasFees : GasFeeSchedule = [
# Fee Schedule for the initial Ethereum forks
GasZero : 0 'i64 ,
GasBase : 2 ,
GasVeryLow : 3 ,
GasLow : 5 ,
GasMid : 8 ,
GasHigh : 10 ,
GasExtCode : 20 , # Changed to 700 in Tangerine (EIP150)
GasBalance : 20 , # Changed to 400 in Tangerine (EIP150)
GasSload : 50 , # Changed to 200 in Tangerine (EIP150)
GasJumpDest : 1 ,
GasSset : 20_000 ,
GasSreset : 5_000 ,
2022-04-08 04:54:11 +00:00
RefundsClear : 15_000 ,
2021-04-08 14:52:10 +00:00
RefundSelfDestruct : 24_000 ,
GasSelfDestruct : 0 , # Changed to 5000 in Tangerine (EIP150)
GasCreate : 32000 ,
GasCodeDeposit : 200 ,
GasCall : 40 , # Changed to 700 in Tangerine (EIP150)
GasCallValue : 9000 ,
GasCallStipend : 2300 ,
GasNewAccount : 25_000 ,
GasExp : 10 ,
GasExpByte : 10 , # Changed to 50 in Spurious Dragon (EIP160)
GasMemory : 3 ,
GasTXCreate : 0 , # Changed to 32000 in Homestead (EIP2)
GasTXDataZero : 4 ,
GasTXDataNonZero : 68 ,
GasTransaction : 21000 ,
GasLog : 375 ,
GasLogData : 8 ,
GasLogTopic : 375 ,
GasSha3 : 30 ,
GasSha3Word : 6 ,
GasCopy : 3 ,
GasBlockhash : 20 ,
2023-01-04 13:11:33 +00:00
GasExtCodeHash : 400 ,
2023-03-21 13:27:12 +00:00
GasInitcodeWord : 0 , # Changed to 2 in EIP-3860
GasWarmStorageRead : WarmStorageReadCost
2021-04-08 14:52:10 +00:00
]
# Create the schedule for each forks
func homesteadGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
result = previousFees
result [ GasTXCreate ] = 32000
func tangerineGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
result = previousFees
result [ GasExtCode ] = 700
result [ GasSload ] = 200
result [ GasSelfDestruct ] = 5000
result [ GasBalance ] = 400
result [ GasCall ] = 700
func spuriousGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-160.md
result = previousFees
result [ GasExpByte ] = 50
func istanbulGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
# https://eips.ethereum.org/EIPS/eip-1884
result = previousFees
result [ GasSload ] = 800
result [ GasExtCodeHash ] = 700
result [ GasBalance ] = 700
result [ GasTXDataNonZero ] = 16
func berlinGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
# https://eips.ethereum.org/EIPS/eip-2929
result = previousFees
2021-09-22 02:34:13 +00:00
# these opcodes gas are calculated inside opcode
result [ GasBalance ] = 0
result [ GasExtCodeHash ] = 0
result [ GasExtCode ] = 0
2021-04-08 14:52:10 +00:00
# SLOAD gasCost become fully dynamic, see sloadEIP2929
2022-04-08 04:54:11 +00:00
result [ GasSload ] = 0
2021-04-08 14:52:10 +00:00
result [ GasCall ] = WarmStorageReadCost
2021-06-28 13:06:29 +00:00
func londonGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
result = previousFees
# EIP-3529 RefundsClear(4800) =
# EIP-2929(5000 - ColdSloadCost) +
# EIP-2930(ACCESS_LIST_STORAGE_KEY_COST)
result [ RefundsClear ] =
5000 - ColdSloadCost +
ACCESS_LIST_STORAGE_KEY_COST
2023-01-04 13:11:33 +00:00
func shanghaiGasFees ( previousFees : GasFeeSchedule ) : GasFeeSchedule =
result = previousFees
result [ GasInitcodeWord ] = 2 . GasInt # INITCODE_WORD_COST from EIP-3860
2021-04-08 14:52:10 +00:00
const
HomesteadGasFees = BaseGasFees . homesteadGasFees
TangerineGasFees = HomesteadGasFees . tangerineGasFees
SpuriousGasFees = TangerineGasFees . spuriousGasFees
IstanbulGasFees = SpuriousGasFees . istanbulGasFees
BerlinGasFees = IstanbulGasFees . berlinGasFees
2021-06-28 13:06:29 +00:00
LondonGasFees = BerlinGasFees . londonGasFees
2023-01-04 13:11:33 +00:00
ShanghaiGasFees = LondonGasFees . shanghaiGasFees
2021-04-08 14:52:10 +00:00
2022-12-02 04:35:41 +00:00
gasFees * : array [ EVMFork , GasFeeSchedule ] = [
2021-04-08 14:52:10 +00:00
FkFrontier : BaseGasFees ,
FkHomestead : HomesteadGasFees ,
FkTangerine : TangerineGasFees ,
FkSpurious : SpuriousGasFees ,
FkByzantium : SpuriousGasFees ,
FkConstantinople : SpuriousGasFees ,
FkPetersburg : SpuriousGasFees ,
FkIstanbul : IstanbulGasFees ,
2021-06-28 02:01:18 +00:00
FkBerlin : BerlinGasFees ,
2022-09-26 12:23:54 +00:00
FkLondon : LondonGasFees ,
FkParis : LondonGasFees ,
2023-01-04 13:11:33 +00:00
FkShanghai : ShanghaiGasFees ,
FkCancun : ShanghaiGasFees ,
2021-04-08 14:52:10 +00:00
]
gasCosts ( FkFrontier , base , BaseGasCosts )
gasCosts ( FkHomestead , homestead , HomesteadGasCosts )
gasCosts ( FkTangerine , tangerine , TangerineGasCosts )
gasCosts ( FkSpurious , spurious , SpuriousGasCosts )
gasCosts ( FkConstantinople , constantinople , ConstantinopleGasCosts )
gasCosts ( FkIstanbul , istanbul , IstanbulGasCosts )
gasCosts ( FkBerlin , berlin , BerlinGasCosts )
2021-06-28 02:01:18 +00:00
gasCosts ( FkLondon , london , LondonGasCosts )
2023-01-04 13:11:33 +00:00
gasCosts ( FkShanghai , shanghai , ShanghaiGasCosts )
2021-04-08 14:52:10 +00:00
2022-12-02 04:35:41 +00:00
proc forkToSchedule * ( fork : EVMFork ) : GasCosts =
2021-04-08 14:52:10 +00:00
if fork < FkHomestead :
BaseGasCosts
elif fork < FkTangerine :
HomesteadGasCosts
elif fork < FkSpurious :
TangerineGasCosts
elif fork = = FkConstantinople :
ConstantinopleGasCosts # with EIP-1283
elif fork < FkIstanbul :
SpuriousGasCosts
elif fork < FkBerlin :
IstanbulGasCosts
2021-06-28 02:01:18 +00:00
elif fork < FkLondon :
2021-04-08 14:52:10 +00:00
BerlinGasCosts
2023-01-04 13:11:33 +00:00
elif fork < FkShanghai :
2022-02-08 09:17:59 +00:00
LondonGasCosts
2023-01-04 13:11:33 +00:00
else :
ShanghaiGasCosts
2021-04-08 14:52:10 +00:00
const
## Precompile costs
GasSHA256 * = 60
GasSHA256Word * = 12
GasRIPEMD160 * = 600
GasRIPEMD160Word * = 120
GasIdentity * = 15
GasIdentityWord * = 3
GasECRecover * = 3000
GasECAdd * = 500
GasECAddIstanbul * = 150
GasECMul * = 40000
GasECMulIstanbul * = 6000
GasECPairingBase * = 100000
GasECPairingBaseIstanbul * = 45000
GasECPairingPerPoint * = 80000
GasECPairingPerPointIstanbul * = 34000
# The Yellow Paper is special casing the GasQuadDivisor.
# It is defined in Appendix G with the other GasFeeKind constants
# instead of Appendix E for precompiled contracts
GasQuadDivisor * = 20
GasQuadDivisorEIP2565 * = 3
# EIP2537 BLS12 381
Bls12381G1AddGas * = 600
Bls12381G1MulGas * = 12000
Bls12381G2AddGas * = 4500
Bls12381G2MulGas * = 55000
Bls12381PairingBaseGas * = 115000
Bls12381PairingPerPairGas * = 23000
Bls12381MapG1Gas * = 5500
Bls12381MapG2Gas * = 110000