Saner EVM gasCosts (#2457)

This is also a part of preparations before converting GasInt to uint64
This commit is contained in:
andri lim 2024-07-05 11:55:13 +07:00 committed by GitHub
parent 23c00ce88c
commit 6fe7411ac0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 210 additions and 237 deletions

View File

@ -340,9 +340,8 @@ proc writeContract*(c: Computation) =
# transaction too, before self-destruction wipes the account at the end. # transaction too, before self-destruction wipes the account at the end.
let let
gasParams = GasParams(kind: Create, cr_memLength: len) gasParams = GasParamsCr(memLength: len)
res = c.gasCosts[Create].cr_handler(0.u256, gasParams) codeCost = c.gasCosts[Create].cr_handler(0.u256, gasParams)
codeCost = res.gasCost
if codeCost <= c.gasMeter.gasRemaining: if codeCost <= c.gasMeter.gasRemaining:
c.gasMeter.consumeGas(codeCost, c.gasMeter.consumeGas(codeCost,

View File

@ -65,25 +65,22 @@ type
# - σ: an account address # - σ: an account address
# - μ: a value popped from the stack or its size. # - μ: a value popped from the stack or its size.
case kind*: Op kind*: Op
of Sstore: isNewAccount*: bool
s_currentValue*: UInt256 gasBalance*: GasInt
s_originalValue*: UInt256 contractGas*: UInt256
of Call, CallCode, DelegateCall, StaticCall: currentMemSize*: GasNatural
c_isNewAccount*: bool memOffset*: GasNatural
c_gasBalance*: GasInt memLength*: GasNatural
c_contractGas*: UInt256
c_currentMemSize*: GasNatural GasParamsSs* = object
c_memOffset*: GasNatural currentValue*: UInt256
c_memLength*: GasNatural originalValue*: UInt256
of Create:
cr_currentMemSize*: GasNatural GasParamsCr* = object
cr_memOffset*: GasNatural currentMemSize*: GasNatural
cr_memLength*: GasNatural memOffset*: GasNatural
of SelfDestruct: memLength*: GasNatural
sd_condition*: bool
else:
discard
GasCostKind* = enum GasCostKind* = enum
GckInvalidOp, GckInvalidOp,
@ -91,10 +88,14 @@ type
GckDynamic, GckDynamic,
GckMemExpansion, GckMemExpansion,
GckCreate, GckCreate,
GckComplex, GckCall,
GckLater GckLater,
GckSuicide,
GckSstore
GasResult = tuple[gasCost, gasRefund: GasInt] # gasRefund of sstore can be a negative number
SStoreGasResult = tuple[gasCost: GasInt, gasRefund: int64]
CallGasResult = tuple[gasCost, childGasLimit: GasInt]
GasCost = object GasCost = object
case kind*: GasCostKind case kind*: GasCostKind
@ -109,15 +110,21 @@ type
m_handler*: proc(currentMemSize, memOffset, memLength: GasNatural): GasInt m_handler*: proc(currentMemSize, memOffset, memLength: GasNatural): GasInt
{.nimcall, gcsafe, raises: [].} {.nimcall, gcsafe, raises: [].}
of GckCreate: of GckCreate:
cr_handler*: proc(value: UInt256, gasParams: GasParams): GasResult cr_handler*: proc(value: UInt256, params: GasParamsCr): GasInt
{.nimcall, gcsafe, raises: [].} {.nimcall, gcsafe, raises: [].}
of GckComplex: of GckCall:
c_handler*: proc(value: UInt256, gasParams: GasParams): EvmResult[GasResult] c_handler*: proc(value: UInt256, params: GasParams): EvmResult[CallGasResult]
{.nimcall, gcsafe, raises: [].} {.nimcall, gcsafe, raises: [].}
# We use gasCost/gasRefund for: # We use gasCost/gasRefund for:
# - Properly log and order cost and refund (for Sstore especially) # - Properly log and order cost and refund (for Sstore especially)
# - Allow to use unsigned integer in the future # - Allow to use unsigned integer in the future
# - CALL instruction requires passing the child message gas (Ccallgas in yellow paper) # - CALL instruction requires passing the child message gas (Ccallgas in yellow paper)
of GckSuicide:
sc_handler*: proc(condition: bool): GasInt
{.nimcall, gcsafe, raises: [].}
of GckSstore:
ss_handler*: proc(value: UInt256, params: GasParamsSs): SStoreGasResult
{.nimcall, gcsafe, raises: [].}
GasCosts* = array[Op, GasCost] GasCosts* = array[Op, GasCost]
@ -198,16 +205,13 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
if not value.isZero: if not value.isZero:
result += static(FeeSchedule[GasExpByte]) * (1 + log256(value)) result += static(FeeSchedule[GasExpByte]) * (1 + log256(value))
func `prefix gasCreate`(value: UInt256, gasParams: GasParams): GasResult {.nimcall.} = func `prefix gasCreate`(value: UInt256, params: GasParamsCr): GasInt {.nimcall.} =
if value.isZero: if value.isZero:
result.gasCost = static(FeeSchedule[GasCodeDeposit]) * gasParams.cr_memLength result = static(FeeSchedule[GasCodeDeposit]) * params.memLength
else: else:
result.gasCost = static(FeeSchedule[GasCreate]) + result = static(FeeSchedule[GasCreate]) +
(static(FeeSchedule[GasInitcodeWord]) * gasParams.cr_memLength.wordCount) + (static(FeeSchedule[GasInitcodeWord]) * params.memLength.wordCount) +
`prefix gasMemoryExpansion`( `prefix gasMemoryExpansion`(params.currentMemSize, params.memOffset, params.memLength)
gasParams.cr_currentMemSize,
gasParams.cr_memOffset,
gasParams.cr_memLength)
func `prefix gasSha3`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} = func `prefix gasSha3`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
@ -229,9 +233,9 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
result = static(FeeSchedule[GasVeryLow]) result = static(FeeSchedule[GasVeryLow])
result += `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength) result += `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength)
func `prefix gasSstore`(value: UInt256, gasParams: GasParams): EvmResult[GasResult] {.nimcall.} = func `prefix gasSstore`(value: UInt256, params: GasParamsSs): SStoreGasResult {.nimcall.} =
## Value is word to save ## Value is word to save
var res: GasResult var res: SStoreGasResult
when fork >= FkBerlin: when fork >= FkBerlin:
# EIP2929 # EIP2929
const const
@ -252,13 +256,13 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
ClearRefund {.used.} = FeeSchedule[RefundsClear]# clearing an originally existing storage slot ClearRefund {.used.} = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
when fork < FkConstantinople or fork == FkPetersburg: when fork < FkConstantinople or fork == FkPetersburg:
let isStorageEmpty = gasParams.s_currentValue.isZero let isStorageEmpty = params.currentValue.isZero
# Gas cost - literal translation of Yellow Paper # Gas cost - literal translation of Yellow Paper
res.gasCost = if value.isZero.not and isStorageEmpty: res.gasCost = if value.isZero.not and isStorageEmpty:
InitGas InitGas
else: else:
CleanGas CleanGas
# Refund # Refund
if value.isZero and not isStorageEmpty: if value.isZero and not isStorageEmpty:
@ -279,35 +283,35 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
# 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas 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 # Gas sentry honoured, do the actual gas calculation based on the stored value
if gasParams.s_currentValue == value: # noop (1) if params.currentValue == value: # noop (1)
res.gasCost = NoopGas res.gasCost = NoopGas
return ok(res) return res
if gasParams.s_originalValue == gasParams.s_currentValue: if params.originalValue == params.currentValue:
if gasParams.s_originalValue.isZero: # create slot (2.1.1) if params.originalValue.isZero: # create slot (2.1.1)
res.gasCost = InitGas res.gasCost = InitGas
return ok(res) return res
if value.isZero: # delete slot (2.1.2b) if value.isZero: # delete slot (2.1.2b)
res.gasRefund = ClearRefund res.gasRefund = ClearRefund
res.gasCost = CleanGas # write existing slot (2.1.2) res.gasCost = CleanGas # write existing slot (2.1.2)
return ok(res) return res
if not gasParams.s_originalValue.isZero: if not params.originalValue.isZero:
if gasParams.s_currentValue.isZero: # recreate slot (2.2.1.1) if params.currentValue.isZero: # recreate slot (2.2.1.1)
res.gasRefund -= ClearRefund res.gasRefund -= ClearRefund
if value.isZero: # delete slot (2.2.1.2) if value.isZero: # delete slot (2.2.1.2)
res.gasRefund += ClearRefund res.gasRefund += ClearRefund
if gasParams.s_originalValue == value: if params.originalValue == value:
if gasParams.s_originalValue.isZero: # reset to original inexistent slot (2.2.2.1) if params.originalValue.isZero: # reset to original inexistent slot (2.2.2.1)
res.gasRefund += InitRefund res.gasRefund += InitRefund
else: # reset to original existing slot (2.2.2.2) else: # reset to original existing slot (2.2.2.2)
res.gasRefund += CleanRefund res.gasRefund += CleanRefund
res.gasCost = DirtyGas # dirty update (2.2) res.gasCost = DirtyGas # dirty update (2.2)
ok(res) res
func `prefix gasLog0`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} = func `prefix gasLog0`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
result = `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength) result = `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength)
@ -343,7 +347,7 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
static(FeeSchedule[GasLogData]) * memLength + static(FeeSchedule[GasLogData]) * memLength +
static(4 * FeeSchedule[GasLogTopic]) static(4 * FeeSchedule[GasLogTopic])
func `prefix gasCall`(value: UInt256, gasParams: GasParams): EvmResult[GasResult] {.nimcall.} = func `prefix gasCall`(value: UInt256, params: GasParams): EvmResult[CallGasResult] {.nimcall.} =
# From the Yellow Paper, going through the equation from bottom to top # From the Yellow Paper, going through the equation from bottom to top
# https://ethereum.github.io/yellowpaper/paper.pdf#appendix.H # https://ethereum.github.io/yellowpaper/paper.pdf#appendix.H
@ -365,87 +369,73 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
# The discussion for the draft EIP-5, which proposes to change the CALL opcode also goes over # 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 # the current implementation - https://github.com/ethereum/EIPs/issues/8
# Both gasCost and childGasLimit can go below zero
# but that is an OOG condition. Leaving this function
# both of them are positive integers.
# First we have to take into account the costs of memory expansion: var gasCost: int64 = `prefix gasMemoryExpansion`(
# Note there is a "bug" in the Ethereum Yellow Paper params.currentMemSize,
# - https://github.com/ethereum/yellowpaper/issues/325 params.memOffset,
# μg already includes memory expansion costs but it is not params.memLength)
# plainly explained n the CALL opcode details var childGasLimit: int64
# 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.
var res: GasResult
res.gasCost = `prefix gasMemoryExpansion`(
gasParams.c_currentMemSize,
gasParams.c_memOffset,
gasParams.c_memLength
)
# Cnew_account # Cnew_account
if gasParams.c_isNewAccount and gasParams.kind == Call: if params.isNewAccount and params.kind == Call:
when fork < FkSpurious: when fork < FkSpurious:
# Pre-EIP161 all account creation calls consumed 25000 gas. # Pre-EIP161 all account creation calls consumed 25000 gas.
res.gasCost += static(FeeSchedule[GasNewAccount]) gasCost += static(FeeSchedule[GasNewAccount])
else: else:
# Afterwards, only those transfering value: # 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-158.md
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
if not value.isZero: if not value.isZero:
res.gasCost += static(FeeSchedule[GasNewAccount]) gasCost += static(FeeSchedule[GasNewAccount])
# Cxfer # Cxfer
if not value.isZero and gasParams.kind in {Call, CallCode}: if not value.isZero and params.kind in {Call, CallCode}:
res.gasCost += static(FeeSchedule[GasCallValue]) gasCost += static(FeeSchedule[GasCallValue])
# Cextra # Cextra
res.gasCost += static(FeeSchedule[GasCall]) gasCost += static(FeeSchedule[GasCall])
# Cgascap # Cgascap
when fork >= FkTangerine: when fork >= FkTangerine:
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
let gas = `prefix all_but_one_64th`(gasParams.c_gasBalance - res.gasCost) let gas = `prefix all_but_one_64th`(params.gasBalance - gasCost)
if gasParams.c_contractGas > high(GasInt).u256 or if params.contractGas > high(int64).u256 or
gas < gasParams.c_contractGas.truncate(GasInt): gas < params.contractGas.truncate(int64):
res.gasRefund = gas childGasLimit = gas
else: else:
res.gasRefund = gasParams.c_contractGas.truncate(GasInt) childGasLimit = params.contractGas.truncate(int64)
else: else:
if gasParams.c_contractGas > high(GasInt).u256: if params.contractGas > high(int64).u256:
return err(gasErr(GasIntOverflow)) return err(gasErr(GasIntOverflow))
res.gasRefund = gasParams.c_contractGas.truncate(GasInt) childGasLimit = params.contractGas.truncate(int64)
if res.gasRefund > 0: # skip check if gasRefund is negative if childGasLimit > 0: # skip check if childGasLimit is negative
if res.gasCost.u256 + res.gasRefund.u256 > high(GasInt).u256: if gasCost.u256 + childGasLimit.u256 > high(int64).u256:
return err(gasErr(GasIntOverflow)) return err(gasErr(GasIntOverflow))
res.gasCost += res.gasRefund gasCost += childGasLimit
# Ccallgas - Gas sent to the child message # Ccallgas - Gas sent to the child message
if not value.isZero and gasParams.kind in {Call, CallCode}: if not value.isZero and params.kind in {Call, CallCode}:
res.gasRefund += static(FeeSchedule[GasCallStipend]) childGasLimit += static(FeeSchedule[GasCallStipend])
ok(res) if gasCost <= 0 and childGasLimit <= 0:
return err(opErr(OutOfGas))
# at this point gasCost and childGasLimit is always > 0
ok( (gasCost, childGasLimit) )
func `prefix gasHalt`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} = func `prefix gasHalt`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
`prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength) `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength)
func `prefix gasSelfDestruct`(value: UInt256, gasParams: GasParams): EvmResult[GasResult] {.nimcall.} = func `prefix gasSelfDestruct`(condition: bool): GasInt {.nimcall.} =
var res: GasResult result += static(FeeSchedule[GasSelfDestruct])
res.gasCost += static(FeeSchedule[GasSelfDestruct])
when fork >= FkTangerine: when fork >= FkTangerine:
if gasParams.sd_condition: if condition:
res.gasCost += static(FeeSchedule[GasNewAccount]) result += static(FeeSchedule[GasNewAccount])
ok(res)
func `prefix gasCreate2`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} = func `prefix gasCreate2`(currentMemSize, memOffset, memLength: GasNatural): GasInt {.nimcall.} =
result = static(FeeSchedule[GasSha3Word]) * (memLength).wordCount result = static(FeeSchedule[GasSha3Word]) * (memLength).wordCount
@ -475,14 +465,22 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
{.nimcall, gcsafe, raises: [].}): GasCost = {.nimcall, gcsafe, raises: [].}): GasCost =
GasCost(kind: GckMemExpansion, m_handler: handler) GasCost(kind: GckMemExpansion, m_handler: handler)
func complex(handler: proc(value: UInt256, gasParams: GasParams): EvmResult[GasResult] func handleCall(handler: proc(value: UInt256, gasParams: GasParams): EvmResult[CallGasResult]
{.nimcall, gcsafe, raises: [].}): GasCost = {.nimcall, gcsafe, raises: [].}): GasCost =
GasCost(kind: GckComplex, c_handler: handler) GasCost(kind: GckCall, c_handler: handler)
func handleCreate(handler: proc(value: UInt256, gasParams: GasParams): GasResult func handleCreate(handler: proc(value: UInt256, gasParams: GasParamsCr): GasInt
{.nimcall, gcsafe, raises: [].}): GasCost = {.nimcall, gcsafe, raises: [].}): GasCost =
GasCost(kind: GckCreate, cr_handler: handler) GasCost(kind: GckCreate, cr_handler: handler)
func handleSuicide(handler: proc(condition: bool): GasInt
{.nimcall, gcsafe, raises: [].}): GasCost =
GasCost(kind: GckSuicide, sc_handler: handler)
func handleSstore(handler: proc(value: UInt256, gasParams: GasParamsSs): SStoreGasResult
{.nimcall, gcsafe, raises: [].}): GasCost =
GasCost(kind: GckSstore, ss_handler: handler)
# Returned value # Returned value
fill_enum_table_holes(Op, GasCost(kind: GckInvalidOp)): fill_enum_table_holes(Op, GasCost(kind: GckInvalidOp)):
[ [
@ -556,7 +554,7 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
Mstore: memExpansion `prefix gasLoadStore`, Mstore: memExpansion `prefix gasLoadStore`,
Mstore8: memExpansion `prefix gasLoadStore`, Mstore8: memExpansion `prefix gasLoadStore`,
Sload: fixedOrLater GasSload, Sload: fixedOrLater GasSload,
Sstore: complex `prefix gasSstore`, Sstore: handleSstore `prefix gasSstore`,
Jump: fixed GasMid, Jump: fixed GasMid,
JumpI: fixed GasHigh, JumpI: fixed GasHigh,
Pc: fixed GasBase, Pc: fixed GasBase,
@ -651,15 +649,15 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
# f0s: System operations # f0s: System operations
Create: handleCreate `prefix gasCreate`, Create: handleCreate `prefix gasCreate`,
Call: complex `prefix gasCall`, Call: handleCall `prefix gasCall`,
CallCode: complex `prefix gasCall`, CallCode: handleCall `prefix gasCall`,
Return: memExpansion `prefix gasHalt`, Return: memExpansion `prefix gasHalt`,
DelegateCall: complex `prefix gasCall`, DelegateCall: handleCall `prefix gasCall`,
Create2: memExpansion `prefix gasCreate2`, Create2: memExpansion `prefix gasCreate2`,
StaticCall: complex `prefix gasCall`, StaticCall: handleCall `prefix gasCall`,
Revert: memExpansion `prefix gasHalt`, Revert: memExpansion `prefix gasHalt`,
Invalid: fixed GasZero, Invalid: fixed GasZero,
SelfDestruct: complex `prefix gasSelfDestruct` SelfDestruct: handleSuicide `prefix gasSelfDestruct`
] ]
# Generate the fork-specific gas costs tables # Generate the fork-specific gas costs tables

View File

@ -210,22 +210,22 @@ proc callOp(k: var VmCtx): EvmResultVoid =
let let
p = ? cpt.callParams p = ? cpt.callParams
res = ? cpt.gasCosts[Call].c_handler(
var
(gasCost, childGasLimit) = ? cpt.gasCosts[Call].c_handler(
p.value, p.value,
GasParams( GasParams(
kind: Call, kind: Call,
c_isNewAccount: not cpt.accountExists(p.contractAddress), isNewAccount: not cpt.accountExists(p.contractAddress),
c_gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929, gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929,
c_contractGas: p.gas, contractGas: p.gas,
c_currentMemSize: cpt.memory.len, currentMemSize: cpt.memory.len,
c_memOffset: p.memOffset, memOffset: p.memOffset,
c_memLength: p.memLength)) memLength: p.memLength))
var (gasCost, childGasLimit) = res
# at this point gasCost is always > 0
gasCost += p.gasCallEIP2929 gasCost += p.gasCallEIP2929
if gasCost >= 0: ? cpt.opcodeGastCost(Call, gasCost, reason = $Call)
? cpt.opcodeGastCost(Call, gasCost, reason = $Call)
cpt.returnData.setLen(0) cpt.returnData.setLen(0)
@ -237,9 +237,6 @@ proc callOp(k: var VmCtx): EvmResultVoid =
cpt.gasMeter.returnGas(childGasLimit) cpt.gasMeter.returnGas(childGasLimit)
return ok() return ok()
if gasCost < 0 and childGasLimit <= 0:
return err(opErr(OutOfGas))
cpt.memory.extend(p.memInPos, p.memInLen) cpt.memory.extend(p.memInPos, p.memInLen)
cpt.memory.extend(p.memOutPos, p.memOutLen) cpt.memory.extend(p.memOutPos, p.memOutLen)
@ -288,21 +285,22 @@ proc callCodeOp(k: var VmCtx): EvmResultVoid =
let let
cpt = k.cpt cpt = k.cpt
p = ? cpt.callCodeParams p = ? cpt.callCodeParams
res = ? cpt.gasCosts[CallCode].c_handler(
var
(gasCost, childGasLimit) = ? cpt.gasCosts[CallCode].c_handler(
p.value, p.value,
GasParams( GasParams(
kind: CallCode, kind: CallCode,
c_isNewAccount: not cpt.accountExists(p.contractAddress), isNewAccount: not cpt.accountExists(p.contractAddress),
c_gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929, gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929,
c_contractGas: p.gas, contractGas: p.gas,
c_currentMemSize: cpt.memory.len, currentMemSize: cpt.memory.len,
c_memOffset: p.memOffset, memOffset: p.memOffset,
c_memLength: p.memLength)) memLength: p.memLength))
var (gasCost, childGasLimit) = res # at this point gasCost is always > 0
gasCost += p.gasCallEIP2929 gasCost += p.gasCallEIP2929
if gasCost >= 0: ? cpt.opcodeGastCost(CallCode, gasCost, reason = $CallCode)
? cpt.opcodeGastCost(CallCode, gasCost, reason = $CallCode)
cpt.returnData.setLen(0) cpt.returnData.setLen(0)
@ -314,9 +312,6 @@ proc callCodeOp(k: var VmCtx): EvmResultVoid =
cpt.gasMeter.returnGas(childGasLimit) cpt.gasMeter.returnGas(childGasLimit)
return ok() return ok()
if gasCost < 0 and childGasLimit <= 0:
return err(opErr(OutOfGas))
cpt.memory.extend(p.memInPos, p.memInLen) cpt.memory.extend(p.memInPos, p.memInLen)
cpt.memory.extend(p.memOutPos, p.memOutLen) cpt.memory.extend(p.memOutPos, p.memOutLen)
@ -366,21 +361,22 @@ proc delegateCallOp(k: var VmCtx): EvmResultVoid =
let let
cpt = k.cpt cpt = k.cpt
p = ? cpt.delegateCallParams p = ? cpt.delegateCallParams
res = ? cpt.gasCosts[DelegateCall].c_handler(
var
(gasCost, childGasLimit) = ? cpt.gasCosts[DelegateCall].c_handler(
p.value, p.value,
GasParams( GasParams(
kind: DelegateCall, kind: DelegateCall,
c_isNewAccount: not cpt.accountExists(p.contractAddress), isNewAccount: not cpt.accountExists(p.contractAddress),
c_gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929, gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929,
c_contractGas: p.gas, contractGas: p.gas,
c_currentMemSize: cpt.memory.len, currentMemSize: cpt.memory.len,
c_memOffset: p.memOffset, memOffset: p.memOffset,
c_memLength: p.memLength)) memLength: p.memLength))
var (gasCost, childGasLimit) = res # at this point gasCost is always > 0
gasCost += p.gasCallEIP2929 gasCost += p.gasCallEIP2929
if gasCost >= 0: ? cpt.opcodeGastCost(DelegateCall, gasCost, reason = $DelegateCall)
? cpt.opcodeGastCost(DelegateCall, gasCost, reason = $DelegateCall)
cpt.returnData.setLen(0) cpt.returnData.setLen(0)
if cpt.msg.depth >= MaxCallDepth: if cpt.msg.depth >= MaxCallDepth:
@ -391,9 +387,6 @@ proc delegateCallOp(k: var VmCtx): EvmResultVoid =
cpt.gasMeter.returnGas(childGasLimit) cpt.gasMeter.returnGas(childGasLimit)
return ok() return ok()
if gasCost < 0 and childGasLimit <= 0:
return err(opErr(OutOfGas))
cpt.memory.extend(p.memInPos, p.memInLen) cpt.memory.extend(p.memInPos, p.memInLen)
cpt.memory.extend(p.memOutPos, p.memOutLen) cpt.memory.extend(p.memOutPos, p.memOutLen)
@ -438,21 +431,22 @@ proc staticCallOp(k: var VmCtx): EvmResultVoid =
let let
cpt = k.cpt cpt = k.cpt
p = ? cpt.staticCallParams p = ? cpt.staticCallParams
res = ? cpt.gasCosts[StaticCall].c_handler(
var
(gasCost, childGasLimit) = ? cpt.gasCosts[StaticCall].c_handler(
p.value, p.value,
GasParams( GasParams(
kind: StaticCall, kind: StaticCall,
c_isNewAccount: not cpt.accountExists(p.contractAddress), isNewAccount: not cpt.accountExists(p.contractAddress),
c_gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929, gasBalance: cpt.gasMeter.gasRemaining - p.gasCallEIP2929,
c_contractGas: p.gas, contractGas: p.gas,
c_currentMemSize: cpt.memory.len, currentMemSize: cpt.memory.len,
c_memOffset: p.memOffset, memOffset: p.memOffset,
c_memLength: p.memLength)) memLength: p.memLength))
var (gasCost, childGasLimit) = res # at this point gasCost is always > 0
gasCost += p.gasCallEIP2929 gasCost += p.gasCallEIP2929
if gasCost >= 0: ? cpt.opcodeGastCost(StaticCall, gasCost, reason = $StaticCall)
? cpt.opcodeGastCost(StaticCall, gasCost, reason = $StaticCall)
cpt.returnData.setLen(0) cpt.returnData.setLen(0)
@ -464,9 +458,6 @@ proc staticCallOp(k: var VmCtx): EvmResultVoid =
cpt.gasMeter.returnGas(childGasLimit) cpt.gasMeter.returnGas(childGasLimit)
return ok() return ok()
if gasCost < 0 and childGasLimit <= 0:
return err(opErr(OutOfGas))
cpt.memory.extend(p.memInPos, p.memInLen) cpt.memory.extend(p.memInPos, p.memInLen)
cpt.memory.extend(p.memOutPos, p.memOutLen) cpt.memory.extend(p.memOutPos, p.memOutLen)

View File

@ -101,15 +101,14 @@ proc createOp(k: var VmCtx): EvmResultVoid =
return err(opErr(InvalidInitCode)) return err(opErr(InvalidInitCode))
let let
gasParams = GasParams( gasParams = GasParamsCr(
kind: Create, currentMemSize: cpt.memory.len,
cr_currentMemSize: cpt.memory.len, memOffset: memPos,
cr_memOffset: memPos, memLength: memLen)
cr_memLength: memLen) gasCost = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
res = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
? cpt.opcodeGastCost(Create, ? cpt.opcodeGastCost(Create,
res.gasCost, reason = "CREATE: GasCreate + memLen * memory expansion") gasCost, reason = "CREATE: GasCreate + memLen * memory expansion")
cpt.memory.extend(memPos, memLen) cpt.memory.extend(memPos, memLen)
cpt.returnData.setLen(0) cpt.returnData.setLen(0)
@ -182,14 +181,13 @@ proc create2Op(k: var VmCtx): EvmResultVoid =
return err(opErr(InvalidInitCode)) return err(opErr(InvalidInitCode))
let let
gasParams = GasParams( gasParams = GasParamsCr(
kind: Create, currentMemSize: cpt.memory.len,
cr_currentMemSize: cpt.memory.len, memOffset: memPos,
cr_memOffset: memPos, memLength: memLen)
cr_memLength: memLen)
res = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
let gasCost = res.gasCost + cpt.gasCosts[Create2].m_handler(0, 0, memLen) var gasCost = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
gasCost = gasCost + cpt.gasCosts[Create2].m_handler(0, 0, memLen)
? cpt.opcodeGastCost(Create2, ? cpt.opcodeGastCost(Create2,
gasCost, reason = "CREATE2: GasCreate + memLen * memory expansion") gasCost, reason = "CREATE2: GasCreate + memLen * memory expansion")

View File

@ -58,11 +58,10 @@ else:
proc sstoreImpl(c: Computation, slot, newValue: UInt256): EvmResultVoid = proc sstoreImpl(c: Computation, slot, newValue: UInt256): EvmResultVoid =
let let
currentValue = c.getStorage(slot) currentValue = c.getStorage(slot)
gasParam = GasParams( gasParam = GasParamsSs(
kind: Op.Sstore, currentValue: currentValue)
s_currentValue: currentValue)
res = ? c.gasCosts[Sstore].c_handler(newValue, gasParam) res = c.gasCosts[Sstore].ss_handler(newValue, gasParam)
? c.opcodeGastCost(Sstore, res.gasCost, "SSTORE") ? c.opcodeGastCost(Sstore, res.gasCost, "SSTORE")
if res.gasRefund > 0: if res.gasRefund > 0:
@ -78,12 +77,11 @@ else:
stateDB = c.vmState.readOnlyStateDB stateDB = c.vmState.readOnlyStateDB
currentValue = c.getStorage(slot) currentValue = c.getStorage(slot)
gasParam = GasParams( gasParam = GasParamsSs(
kind: Op.Sstore, currentValue: currentValue,
s_currentValue: currentValue, originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot))
s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot))
res = ? c.gasCosts[Sstore].c_handler(newValue, gasParam) res = c.gasCosts[Sstore].ss_handler(newValue, gasParam)
? c.opcodeGastCost(Sstore, res.gasCost + coldAccess, "SSTORE") ? c.opcodeGastCost(Sstore, res.gasCost + coldAccess, "SSTORE")

View File

@ -86,17 +86,15 @@ proc selfDestructOp(k: var VmCtx): EvmResultVoid =
proc selfDestructEIP150Op(k: var VmCtx): EvmResultVoid = proc selfDestructEIP150Op(k: var VmCtx): EvmResultVoid =
## selfDestructEip150 (auto generated comment) ## selfDestructEip150 (auto generated comment)
let cpt = k.cpt let
let beneficiary = ? cpt.stack.popAddress() cpt = k.cpt
block: beneficiary = ? cpt.stack.popAddress()
let gasParams = GasParams( condition = not cpt.accountExists(beneficiary)
kind: SelfDestruct, gasCost = cpt.gasCosts[SelfDestruct].sc_handler(condition)
sd_condition: not cpt.accountExists(beneficiary))
let res = ? cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams) ? cpt.opcodeGastCost(SelfDestruct,
? cpt.opcodeGastCost(SelfDestruct, gasCost, reason = "SELFDESTRUCT EIP150")
res.gasCost, reason = "SELFDESTRUCT EIP150") cpt.selfDestruct(beneficiary)
cpt.selfDestruct(beneficiary)
ok() ok()
proc selfDestructEIP161Op(k: var VmCtx): EvmResultVoid = proc selfDestructEIP161Op(k: var VmCtx): EvmResultVoid =
@ -104,20 +102,16 @@ proc selfDestructEIP161Op(k: var VmCtx): EvmResultVoid =
let cpt = k.cpt let cpt = k.cpt
? checkInStaticContext(cpt) ? checkInStaticContext(cpt)
let beneficiary = ? cpt.stack.popAddress() let
block: beneficiary = ? cpt.stack.popAddress()
let isDead = not cpt.accountExists(beneficiary)
isDead = not cpt.accountExists(beneficiary) balance = cpt.getBalance(cpt.msg.contractAddress)
balance = cpt.getBalance(cpt.msg.contractAddress) condition = isDead and not balance.isZero
gasCost = cpt.gasCosts[SelfDestruct].sc_handler(condition)
let gasParams = GasParams( ? cpt.opcodeGastCost(SelfDestruct,
kind: SelfDestruct, gasCost, reason = "SELFDESTRUCT EIP161")
sd_condition: isDead and not balance.isZero) cpt.selfDestruct(beneficiary)
let res = ? cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams)
? cpt.opcodeGastCost(SelfDestruct,
res.gasCost, reason = "SELFDESTRUCT EIP161")
cpt.selfDestruct(beneficiary)
ok() ok()
proc selfDestructEIP2929Op(k: var VmCtx): EvmResultVoid = proc selfDestructEIP2929Op(k: var VmCtx): EvmResultVoid =
@ -125,32 +119,27 @@ proc selfDestructEIP2929Op(k: var VmCtx): EvmResultVoid =
let cpt = k.cpt let cpt = k.cpt
? checkInStaticContext(cpt) ? checkInStaticContext(cpt)
let beneficiary = ? cpt.stack.popAddress() let
block: beneficiary = ? cpt.stack.popAddress()
let isDead = not cpt.accountExists(beneficiary)
isDead = not cpt.accountExists(beneficiary) balance = cpt.getBalance(cpt.msg.contractAddress)
balance = cpt.getBalance(cpt.msg.contractAddress) condition = isDead and not balance.isZero
let var
gasParams = GasParams( gasCost = cpt.gasCosts[SelfDestruct].sc_handler(condition)
kind: SelfDestruct,
sd_condition: isDead and not balance.isZero)
res = ? cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams)
var gasCost = res.gasCost when evmc_enabled:
if cpt.host.accessAccount(beneficiary) == EVMC_ACCESS_COLD:
when evmc_enabled: gasCost = gasCost + ColdAccountAccessCost
if cpt.host.accessAccount(beneficiary) == EVMC_ACCESS_COLD: else:
cpt.vmState.mutateStateDB:
if not db.inAccessList(beneficiary):
db.accessList(beneficiary)
gasCost = gasCost + ColdAccountAccessCost gasCost = gasCost + ColdAccountAccessCost
else:
cpt.vmState.mutateStateDB:
if not db.inAccessList(beneficiary):
db.accessList(beneficiary)
gasCost = gasCost + ColdAccountAccessCost
? cpt.opcodeGastCost(SelfDestruct, ? cpt.opcodeGastCost(SelfDestruct,
gasCost, reason = "SELFDESTRUCT EIP2929") gasCost, reason = "SELFDESTRUCT EIP2929")
cpt.selfDestruct(beneficiary) cpt.selfDestruct(beneficiary)
ok() ok()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------