Some of Shanghai: EIP-3651, EIP-3855, EIP-3860 (#1406)

* EIP-3651: Warm COINBASE

* EIP-3855: PUSH0 instruction

* EIP-3860: Limit and meter initcode
This commit is contained in:
Adam Spitz 2023-01-04 08:11:33 -05:00 committed by GitHub
parent 63c6000bad
commit 4bf4aeba94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 79 additions and 8 deletions

View File

@ -56,6 +56,10 @@ const
# contract is not subject to this limit.
EIP170_MAX_CODE_SIZE* = 0x6000
# See EIP-3860 (https://eips.ethereum.org/EIPS/eip-3860). Maximum initcode
# size when creating a new contract.
EIP3860_MAX_INITCODE_SIZE* = 2 * EIP170_MAX_CODE_SIZE
# EIP
MaxPrecompilesAddr* = 0xFFFF

View File

@ -259,6 +259,10 @@ proc validateTransaction*(
debug "invalid tx: Eip1559 Tx type detected before London"
return false
if fork >= FkShanghai and tx.contractCreation and tx.payload.len >= EIP3860_MAX_INITCODE_SIZE:
debug "invalid tx: initcode size exceeds maximum"
return false
# Note that the following check bears some plausibility but is _not_
# covered by the eip-1559 reference (sort of) pseudo code, for details
# see `https://eips.ethereum.org/EIPS/eip-1559#specification`_

View File

@ -52,7 +52,8 @@ type
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.
GasExtCodeHash # Payment for contract's code hashing
GasExtCodeHash, # Payment for contract's code hashing
GasInitcodeWord # Payment for each word (rounded up) for initcode
GasFeeSchedule = array[GasFeeKind, GasInt]
@ -278,6 +279,7 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
result.gasCost = static(FeeSchedule[GasCodeDeposit]) * gasParams.cr_memLength
else:
result.gasCost = static(FeeSchedule[GasCreate]) +
(static(FeeSchedule[GasInitcodeWord]) * gasParams.cr_memLength.wordCount) +
`prefix gasMemoryExpansion`(
gasParams.cr_currentMemSize,
gasParams.cr_memOffset,
@ -620,7 +622,8 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
ReturnSub: fixed GasLow,
JumpSub: fixed GasHigh,
# 60s & 70s: Push Operations
# 5f, 60s & 70s: Push Operations
Push0: fixed GasBase,
Push1: fixed GasVeryLow,
Push2: fixed GasVeryLow,
Push3: fixed GasVeryLow,
@ -749,7 +752,8 @@ const
GasSha3Word: 6,
GasCopy: 3,
GasBlockhash: 20,
GasExtCodeHash: 400
GasExtCodeHash: 400,
GasInitcodeWord: 0 # Changed to 2 in EIP-3860
]
# Create the schedule for each forks
@ -802,6 +806,10 @@ func londonGasFees(previousFees: GasFeeSchedule): GasFeeSchedule =
5000 - ColdSloadCost +
ACCESS_LIST_STORAGE_KEY_COST
func shanghaiGasFees(previousFees: GasFeeSchedule): GasFeeSchedule =
result = previousFees
result[GasInitcodeWord] = 2.GasInt # INITCODE_WORD_COST from EIP-3860
const
HomesteadGasFees = BaseGasFees.homesteadGasFees
TangerineGasFees = HomesteadGasFees.tangerineGasFees
@ -809,6 +817,7 @@ const
IstanbulGasFees = SpuriousGasFees.istanbulGasFees
BerlinGasFees = IstanbulGasFees.berlinGasFees
LondonGasFees = BerlinGasFees.londonGasFees
ShanghaiGasFees = LondonGasFees.shanghaiGasFees
gasFees*: array[EVMFork, GasFeeSchedule] = [
FkFrontier: BaseGasFees,
@ -822,8 +831,8 @@ const
FkBerlin: BerlinGasFees,
FkLondon: LondonGasFees,
FkParis: LondonGasFees,
FkShanghai: LondonGasFees,
FkCancun: LondonGasFees,
FkShanghai: ShanghaiGasFees,
FkCancun: ShanghaiGasFees,
]
gasCosts(FkFrontier, base, BaseGasCosts)
@ -834,6 +843,7 @@ gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts)
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
gasCosts(FkBerlin, berlin, BerlinGasCosts)
gasCosts(FkLondon, london, LondonGasCosts)
gasCosts(FkShanghai, shanghai, ShanghaiGasCosts)
proc forkToSchedule*(fork: EVMFork): GasCosts =
if fork < FkHomestead:
@ -850,8 +860,10 @@ proc forkToSchedule*(fork: EVMFork): GasCosts =
IstanbulGasCosts
elif fork < FkLondon:
BerlinGasCosts
else:
elif fork < FkShanghai:
LondonGasCosts
else:
ShanghaiGasCosts
const
## Precompile costs

View File

@ -129,9 +129,9 @@ type
BeginSub = 0x5c, ## Marks the entry point to a subroutine
ReturnSub = 0x5d, ## Returns control to the caller of a subroutine.
JumpSub = 0x5e, ## Transfers control to a subroutine.
Nop0x5F = 0x5f, ## ..
# 60s & 70s: Push Operations.
# 5f, 60s & 70s: Push Operations.
Push0 = 0x5f, ## Place 0 on stack. EIP-3855
Push1 = 0x60, ## Place 1-byte item on stack.
Push2 = 0x61, ## Place 2-byte item on stack.

View File

@ -34,6 +34,7 @@ const
(vm2OpExecBlockData, "BlockData"),
(vm2OpExecMemory, "Memory"),
(vm2OpExecPush, "Push"),
(vm2OpExecPushZero, "PushZero"),
(vm2OpExecDup, "Dup"),
(vm2OpExecSwap, "Swap"),
(vm2OpExecLog, "Log"),

View File

@ -91,6 +91,11 @@ const
k.cpt.stack.top(0)
# EIP-3860
if k.cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
trace "Initcode size exceeds maximum", initcodeSize = memLen
return
let gasParams = GasParams(
kind: Create,
cr_currentMemSize: k.cpt.memory.len,
@ -163,6 +168,11 @@ const
k.cpt.stack.top(0)
# EIP-3860
if k.cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
trace "Initcode size exceeds maximum", initcodeSize = memLen
return
let gasParams = GasParams(
kind: Create,
cr_currentMemSize: k.cpt.memory.len,

View File

@ -81,6 +81,12 @@ const
Vm2OpLondonAndLater* =
Vm2OpBerlinAndLater - {FkBerlin}
Vm2OpParisAndLater* =
Vm2OpLondonAndLater - {FkLondon}
Vm2OpShanghaiAndLater* =
Vm2OpParisAndLater - {FkParis}
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -58,6 +58,28 @@ genOphHandlers fnName, fnInfo, inxRange, pushImpl
genOphList fnName, fnInfo, inxRange, "vm2OpExecPush", opName
# Push0 needs to be slightly different because it's only available after
# Shanghai (EIP-3855). But it still seems like it belongs in this file.
# (Alternatively, we could make genOphList accept some more information
# about which opcodes are for which forks, but that seems uglier than
# just adding Push0 here as a special case.)
const
push0Op: Vm2OpFn = proc (k: var Vm2Ctx) =
## 0x5f, push 0 onto the stack
k.cpt.stack.push(0)
vm2OpExecPushZero*: seq[Vm2OpExec] = @[
(opCode: Push0, ## 0x5f, push 0 onto the stack
forks: Vm2OpShanghaiAndLater,
name: "Push0",
info: "Push 0 on the stack",
exec: (prep: vm2OpIgnore,
run: push0Op,
post: vm2OpIgnore))]
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -28,6 +28,8 @@ proc intrinsicGas*(tx: Transaction, fork: EVMFork): GasInt =
if tx.contractCreation:
result = result + gasFees[fork][GasTXCreate]
if fork >= FkShanghai:
result = result + (gasFees[fork][GasInitcodeWord] * tx.payload.len)
if tx.txType > TxLegacy:
result = result + tx.accessList.len * ACCESS_LIST_ADDRESS_COST
@ -121,6 +123,9 @@ proc validate*(tx: Transaction, fork: EVMFork) =
if tx.intrinsicGas(fork) > tx.gasLimit:
raise newException(ValidationError, "Insufficient gas")
if fork >= FkShanghai and tx.contractCreation and tx.payload.len >= EIP3860_MAX_INITCODE_SIZE:
raise newException(ValidationError, "Initcode size exceeds max")
# check signature validity
var sender: EthAddress
if not tx.getSender(sender):

View File

@ -73,6 +73,8 @@ func intrinsicGas*(call: CallParams, fork: EVMFork): GasInt {.inline.} =
# EIP-2 (Homestead) extra intrinsic gas for contract creations.
if call.isCreate:
gas += gasFees[fork][GasTXCreate]
if fork >= FkShanghai:
gas += (gasFees[fork][GasInitcodeWord] * call.input.len.wordCount)
# Input data cost, reduced in EIP-2028 (Istanbul).
let gasZero = gasFees[fork][GasTXDataZero]
@ -99,6 +101,11 @@ proc initialAccessListEIP2929(call: CallParams) =
# access list itself, after calculating the new contract address.
if not call.isCreate:
db.accessList(call.to)
# EIP3651 adds coinbase to the list of addresses that should start warm.
if vmState.fork >= FkShanghai:
db.accessList(vmState.coinbase)
# TODO: Check this only adds the correct subset of precompiles.
for c in activePrecompiles():
db.accessList(c)