EIP2929 implementation
This commit is contained in:
parent
f2b483d6ad
commit
3db535aa39
|
@ -39,3 +39,6 @@ proc add*(ac: var AccessList, address: EthAddress, slot: UInt256) =
|
|||
val[].incl slot
|
||||
do:
|
||||
ac.slots[address] = toHashSet([slot])
|
||||
|
||||
proc clear*(ac: var AccessList) {.inline.} =
|
||||
ac.slots.clear()
|
||||
|
|
|
@ -452,6 +452,10 @@ proc persist*(ac: var AccountsCache, clearCache: bool = true) =
|
|||
else:
|
||||
for x in cleanAccounts:
|
||||
ac.savePoint.cache.del x
|
||||
|
||||
# EIP2929
|
||||
ac.savePoint.accessList.clear()
|
||||
|
||||
ac.isDirty = false
|
||||
|
||||
iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) =
|
||||
|
|
|
@ -3,7 +3,7 @@ import options, sets,
|
|||
../db/[db_chain, accounts_cache],
|
||||
../utils, ../constants, ../transaction,
|
||||
../vm_state, ../vm_types, ../vm_state_transactions,
|
||||
../vm/[computation, message],
|
||||
../vm/[computation, message, precompiles],
|
||||
../vm/interpreter/vm_forks,
|
||||
./dao, ../config
|
||||
|
||||
|
@ -13,6 +13,16 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
|||
trace "Sender", sender
|
||||
trace "txHash", rlpHash = tx.rlpHash
|
||||
|
||||
# EIP2929
|
||||
if fork >= FkBerlin:
|
||||
vmState.mutateStateDB:
|
||||
db.accessList(sender)
|
||||
if not tx.isContractCreation:
|
||||
#If it's a create-tx, the destination will be added inside evm.create
|
||||
db.accessList(tx.getRecipient)
|
||||
for c in activePrecompiles():
|
||||
db.accessList(c)
|
||||
|
||||
if validateTransaction(vmState, tx, sender, fork):
|
||||
var c = setupComputation(vmState, tx, sender, fork)
|
||||
vmState.mutateStateDB:
|
||||
|
|
|
@ -258,6 +258,12 @@ proc execCreate*(c: Computation) =
|
|||
c.vmState.mutateStateDB:
|
||||
db.incNonce(c.msg.sender)
|
||||
|
||||
# We add this to the access list _before_ taking a snapshot.
|
||||
# Even if the creation fails, the access-list change should not be rolled back
|
||||
# EIP2929
|
||||
if c.fork >= FkBerlin:
|
||||
db.accessList(c.msg.contractAddress)
|
||||
|
||||
c.snapshot()
|
||||
defer:
|
||||
c.dispose()
|
||||
|
|
|
@ -115,6 +115,11 @@ type
|
|||
|
||||
GasCosts* = array[Op, GasCost]
|
||||
|
||||
const
|
||||
ColdSloadCost* = 2100
|
||||
ColdAccountAccessCost* = 2600
|
||||
WarmStorageReadCost* = 100
|
||||
|
||||
template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||
|
||||
## Generate the gas cost for each forks and store them in a const
|
||||
|
@ -214,13 +219,24 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
|||
|
||||
func `prefix gasSstore`(value: Uint256, gasParams: Gasparams): GasResult {.nimcall.} =
|
||||
## Value is word to save
|
||||
|
||||
when fork >= FkBerlin:
|
||||
# EIP2929
|
||||
const
|
||||
SLOAD_GAS = WarmStorageReadCost
|
||||
SSTORE_RESET_GAS = 5000 - ColdSloadCost
|
||||
else:
|
||||
const
|
||||
SLOAD_GAS = FeeSchedule[GasSload]
|
||||
SSTORE_RESET_GAS = FeeSchedule[GasSreset]
|
||||
|
||||
const
|
||||
NoopGas = FeeSchedule[GasSload] # if the value doesn't change.
|
||||
DirtyGas = FeeSchedule[GasSload] # if a dirty value is changed.
|
||||
NoopGas = SLOAD_GAS # if the value doesn't change.
|
||||
DirtyGas = SLOAD_GAS # if a dirty value is changed.
|
||||
InitGas = FeeSchedule[GasSset] # from clean zero to non-zero
|
||||
InitRefund = FeeSchedule[GasSset] - FeeSchedule[GasSload] # resetting to the original zero value
|
||||
CleanGas = FeeSchedule[GasSreset]# from clean non-zero to something else
|
||||
CleanRefund = FeeSchedule[GasSreset] - FeeSchedule[GasSload] # resetting to the original non-zero value
|
||||
InitRefund = FeeSchedule[GasSset] - SLOAD_GAS # resetting to the original zero value
|
||||
CleanGas = SSTORE_RESET_GAS # from clean non-zero to something else
|
||||
CleanRefund = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value
|
||||
ClearRefund = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
|
||||
|
||||
when defined(evmc_enabled):
|
||||
|
@ -724,6 +740,7 @@ gasCosts(FkTangerine, tangerine, TangerineGasCosts)
|
|||
gasCosts(FkSpurious, spurious, SpuriousGasCosts)
|
||||
gasCosts(FkConstantinople, constantinople, ConstantinopleGasCosts)
|
||||
gasCosts(FkIstanbul, istanbul, IstanbulGasCosts)
|
||||
gasCosts(FkBerlin, berlin, BerlinGasCosts)
|
||||
|
||||
proc forkToSchedule*(fork: Fork): GasCosts =
|
||||
if fork < FkHomestead:
|
||||
|
@ -736,8 +753,10 @@ proc forkToSchedule*(fork: Fork): GasCosts =
|
|||
ConstantinopleGasCosts # with EIP-1283
|
||||
elif fork < FkIstanbul:
|
||||
SpuriousGasCosts
|
||||
else:
|
||||
elif fork < FkBerlin:
|
||||
IstanbulGasCosts
|
||||
else:
|
||||
BerlinGasCosts
|
||||
|
||||
const
|
||||
## Precompile costs
|
||||
|
|
|
@ -23,6 +23,15 @@ logScope:
|
|||
# ##################################
|
||||
# Syntactic sugar
|
||||
|
||||
proc gasEip2929AccountCheck(c: Computation, address: EthAddress) =
|
||||
c.vmState.mutateStateDB:
|
||||
let gasCost = if not db.inAccessList(address):
|
||||
db.accessList(address)
|
||||
ColdAccountAccessCost
|
||||
else:
|
||||
WarmStorageReadCost
|
||||
c.gasMeter.consumeGas(gasCost, reason = "gasEIP2929AccountCheck")
|
||||
|
||||
template push(x: typed) {.dirty.} =
|
||||
## Push an expression on the computation stack
|
||||
c.stack.push x
|
||||
|
@ -763,7 +772,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
(memOutPos, memOutLen)
|
||||
|
||||
let contractAddress = when opCode in {Call, StaticCall}: destination else: c.msg.contractAddress
|
||||
var (childGasFee, childGasLimit) = c.gasCosts[opCode].c_handler(
|
||||
var (gasCost, childGasLimit) = c.gasCosts[opCode].c_handler(
|
||||
value,
|
||||
GasParams(kind: opCode,
|
||||
c_isNewAccount: not c.accountExists(contractAddress),
|
||||
|
@ -781,8 +790,12 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
# if c.fork >= FkBerlin and destination.toInt <= MaxPrecompilesAddr:
|
||||
# childGasFee = childGasFee - 660.GasInt
|
||||
|
||||
if childGasFee >= 0:
|
||||
c.gasMeter.consumeGas(childGasFee, reason = $opCode)
|
||||
# EIP2929
|
||||
if c.fork >= FkBerlin:
|
||||
c.gasEip2929AccountCheck(destination)
|
||||
|
||||
if gasCost >= 0:
|
||||
c.gasMeter.consumeGas(gasCost, reason = $opCode)
|
||||
|
||||
c.returnData.setLen(0)
|
||||
|
||||
|
@ -792,7 +805,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
c.gasMeter.returnGas(childGasLimit)
|
||||
return
|
||||
|
||||
if childGasFee < 0 and childGasLimit <= 0:
|
||||
if gasCost < 0 and childGasLimit <= 0:
|
||||
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callName.astToStr & ")")
|
||||
|
||||
c.memory.extend(memInPos, memInLen)
|
||||
|
@ -957,3 +970,86 @@ op sarOp, inline = true:
|
|||
op extCodeHash, inline = true:
|
||||
let address = c.stack.popAddress()
|
||||
push: c.getCodeHash(address)
|
||||
|
||||
op balanceEIP2929, inline = true:
|
||||
## 0x31, Get balance of the given account.
|
||||
let address = c.stack.popAddress()
|
||||
c.gasEip2929AccountCheck(address)
|
||||
push: c.getBalance(address)
|
||||
|
||||
op extCodeHashEIP2929, inline = true:
|
||||
let address = c.stack.popAddress()
|
||||
c.gasEip2929AccountCheck(address)
|
||||
push: c.getCodeHash(address)
|
||||
|
||||
op extCodeSizeEIP2929, inline = true:
|
||||
## 0x3b, Get size of an account's code
|
||||
let address = c.stack.popAddress()
|
||||
c.gasEip2929AccountCheck(address)
|
||||
push: c.getCodeSize(address)
|
||||
|
||||
op extCodeCopyEIP2929, inline = true:
|
||||
## 0x3c, Copy an account's code to memory.
|
||||
let address = c.stack.popAddress()
|
||||
let (memStartPos, codeStartPos, size) = c.stack.popInt(3)
|
||||
let (memPos, codePos, len) = (memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
|
||||
|
||||
c.gasMeter.consumeGas(
|
||||
c.gasCosts[ExtCodeCopy].m_handler(c.memory.len, memPos, len),
|
||||
reason="ExtCodeCopy fee")
|
||||
|
||||
c.gasEip2929AccountCheck(address)
|
||||
|
||||
let codeBytes = c.getCode(address)
|
||||
c.memory.writePaddedResult(codeBytes, memPos, codePos, len)
|
||||
|
||||
op selfDestructEIP2929, inline = false:
|
||||
checkInStaticContext(c)
|
||||
|
||||
let
|
||||
beneficiary = c.stack.popAddress()
|
||||
isDead = not c.accountExists(beneficiary)
|
||||
balance = c.getBalance(c.msg.contractAddress)
|
||||
|
||||
let gasParams = GasParams(kind: SelfDestruct,
|
||||
sd_condition: isDead and not balance.isZero
|
||||
)
|
||||
|
||||
var gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
|
||||
|
||||
c.vmState.mutateStateDB:
|
||||
if not db.inAccessList(beneficiary):
|
||||
db.accessList(beneficiary)
|
||||
gasCost = gasCost + ColdAccountAccessCost
|
||||
|
||||
c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161")
|
||||
c.selfDestruct(beneficiary)
|
||||
|
||||
op sloadEIP2929, inline = true, slot:
|
||||
## 0x54, Load word from storage.
|
||||
c.vmState.mutateStateDB:
|
||||
let gasCost = if not db.inAccessList(c.msg.contractAddress, slot):
|
||||
db.accessList(c.msg.contractAddress, slot)
|
||||
ColdSloadCost
|
||||
else:
|
||||
WarmStorageReadCost
|
||||
c.gasMeter.consumeGas(gasCost, reason = "sloadEIP2929")
|
||||
|
||||
push: c.getStorage(slot)
|
||||
|
||||
op sstoreEIP2929, inline = false, slot, newValue:
|
||||
checkInStaticContext(c)
|
||||
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
|
||||
|
||||
if c.gasMeter.gasRemaining <= SentryGasEIP2200:
|
||||
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
|
||||
|
||||
c.vmState.mutateStateDB:
|
||||
if not db.inAccessList(c.msg.contractAddress, slot):
|
||||
db.accessList(c.msg.contractAddress, slot)
|
||||
c.gasMeter.consumeGas(ColdSloadCost, reason = "sstoreEIP2929")
|
||||
|
||||
when evmc_enabled:
|
||||
sstoreEvmc(c, slot, newValue)
|
||||
else:
|
||||
sstoreNetGasMeteringImpl(c, slot, newValue)
|
||||
|
|
|
@ -235,6 +235,14 @@ proc genBerlinJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTi
|
|||
result[ReturnSub] = newIdentNode "returnSub"
|
||||
result[JumpSub] = newIdentNode "jumpSub"
|
||||
|
||||
result[Balance] = newIdentNode "balanceEIP2929"
|
||||
result[ExtCodeHash] = newIdentNode "extCodeHashEIP2929"
|
||||
result[ExtCodeSize] = newIdentNode "extCodeSizeEIP2929"
|
||||
result[ExtCodeCopy] = newIdentNode "extCodeCopyEIP2929"
|
||||
result[SelfDestruct] = newIdentNode "selfDestructEIP2929"
|
||||
result[SLoad] = newIdentNode "sloadEIP2929"
|
||||
result[SStore] = newIdentNode "sstoreEIP2929"
|
||||
|
||||
let BerlinOpDispatch {.compileTime.}: array[Op, NimNode] = genBerlinJumpTable(IstanbulOpDispatch)
|
||||
|
||||
proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
|
||||
|
|
|
@ -28,6 +28,12 @@ type
|
|||
paBlsMapG1
|
||||
paBlsMapG2
|
||||
|
||||
iterator activePrecompiles*(): EthAddress =
|
||||
var res: EthAddress
|
||||
for c in PrecompileAddresses.low..PrecompileAddresses.high:
|
||||
res[^1] = c.byte
|
||||
yield res
|
||||
|
||||
proc getSignature(computation: Computation): (array[32, byte], Signature) =
|
||||
# input is Hash, V, R, S
|
||||
template data: untyped = computation.msg.data
|
||||
|
|
Loading…
Reference in New Issue