re-integrated handlers with op codes 0xf2/return ..0xff/selfdestruct

This commit is contained in:
Jordan Hrycaj 2021-04-15 18:42:43 +01:00 committed by zah
parent 7436e516fd
commit 1bb6ef43a1
6 changed files with 251 additions and 140 deletions

View File

@ -51,7 +51,9 @@ proc mkOpTable(select: Fork): array[Op,Vm2OpExec] {.compileTime.} =
result.importList(select, vm2OpExecDup, "Dup") result.importList(select, vm2OpExecDup, "Dup")
result.importList(select, vm2OpExecSwap, "Swap") result.importList(select, vm2OpExecSwap, "Swap")
result.importList(select, vm2OpExecLog, "Log") result.importList(select, vm2OpExecLog, "Log")
result.importList(select, vm2OpExecSysOP, "SysOp") #result.importList(select, vm2OpExecCreate, "Create")
#result.importList(select, vm2OpExecCall, "Call")
result.importList(select, vm2OpExecSysOp, "SysOp")
for op in Op: for op in Op:
if select notin result[op].forks: if select notin result[op].forks:

View File

@ -59,6 +59,7 @@ else:
Computation* = ref object Computation* = ref object
returnStack*: seq[int] returnStack*: seq[int]
output*: seq[byte]
vmState*: BaseVMState vmState*: BaseVMState
gasMeter*: GasMeter gasMeter*: GasMeter
stack*: Stack stack*: Stack

View File

@ -47,7 +47,6 @@ else:
emvcStatic = 1 emvcStatic = 1
ColdAccountAccessCost = 2 ColdAccountAccessCost = 2
WarmStorageReadCost = 3 WarmStorageReadCost = 3
type type
GasInt = int GasInt = int

View File

@ -47,8 +47,8 @@ else:
const const
ColdSloadCost = 42 ColdSloadCost = 42
WarmStorageReadCost = 43 WarmStorageReadCost = 43
var
var blindGasCosts: array[Op,int] blindGasCosts: array[Op,int]
# copied from stack.nim # copied from stack.nim
macro genTupleType(len: static[int], elemType: untyped): untyped = macro genTupleType(len: static[int], elemType: untyped): untyped =

View File

@ -12,32 +12,260 @@
## ====================================== ## ======================================
## ##
const
kludge {.intdefine.}: int = 0
breakCircularDependency {.used.} = kludge > 0
import import
../op_codes, ./oph_defs, ../../../errors.nim ../../../errors,
./oph_defs,
./oph_helpers,
stint
# ------------------------------------------------------------------------------
# Kludge BEGIN
# ------------------------------------------------------------------------------
when not breakCircularDependency:
import
../../../db/accounts_cache,
../../stack,
../../v2computation,
../../v2memory,
../../v2state,
../../v2types,
../gas_meter,
../utils/v2utils_numeric,
../v2gas_costs,
eth/common
else:
import macros
type
GasInt = int
GasResult = tuple[gasCost, gasRefund: GasInt]
const
ColdAccountAccessCost = 42
var
blindGasCosts: array[Op,int]
blindAddress: EthAddress
blindGasResult: GasResult
# copied from stack.nim
macro genTupleType(len: static[int], elemType: untyped): untyped =
result = nnkTupleConstr.newNimNode()
for i in 0 ..< len: result.add(elemType)
# function stubs from stack.nim (to satisfy compiler logic)
proc popAddress(x: var Stack): EthAddress = blindAddress
proc popInt(x: var Stack, n: static[int]): auto =
var rc: genTupleType(n, UInt256)
return rc
# function stubs from v2computation.nim (to satisfy compiler logic)
proc gasCosts(c: Computation): array[Op,int] = blindGasCosts
proc setError(c: Computation, msg: string, burnsGas = false) = discard
proc selfDestruct(c: Computation, address: EthAddress) = discard
proc accountExists(c: Computation, address: EthAddress): bool = false
proc getBalance[T](c: Computation, address: T): Uint256 = 0.u256
# function stubs from v2utils_numeric.nim
func cleanMemRef(x: UInt256): int = 0
# function stubs from v2memory.nim
proc len(mem: Memory): int = 0
proc extend(mem: var Memory; startPos: Natural; size: Natural) = discard
proc read(mem: var Memory, startPos: Natural, size: Natural): seq[byte] = @[]
# function stubs from v2state.nim
template mutateStateDB(vmState: BaseVMState, body: untyped) =
block:
var db {.inject.} = vmState.accountDb
body
# function stubs from gas_meter.nim
proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard
# stubs from v2gas_costs.nim
type GasParams = object
case kind*: Op
of SelfDestruct:
sd_condition: bool
else:
discard
proc c_handler(x: int; y: Uint256, z: GasParams): GasResult = blindGasResult
proc m_handler(x: int; curMemSize, memOffset, memLen: int64): int = 0
# function stubs from accounts_cache.nim:
func inAccessList[A,B](ac: A; address: B): bool = false
proc accessList[A,B](ac: var A; address: B) = discard
# ------------------------------------------------------------------------------
# Kludge END
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private # Private
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
const const
invalidOp: Vm2OpFn = returnOp: Vm2OpFn = proc(k: Vm2Ctx) =
proc(k: Vm2Ctx) {.gcsafe.} = ## 0xf3, Halt execution returning output data.
let (startPos, size) = k.cpt.stack.popInt(2)
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
k.cpt.gasMeter.consumeGas(
k.cpt.gasCosts[Return].m_handler(k.cpt.memory.len, pos, len),
reason = "RETURN")
k.cpt.memory.extend(pos, len)
k.cpt.output = k.cpt.memory.read(pos, len)
revertOp: Vm2OpFn = proc(k: Vm2Ctx) =
## 0xfd, Halt execution reverting state changes but returning data
## and remaining gas.
let (startPos, size) = k.cpt.stack.popInt(2)
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
k.cpt.gasMeter.consumeGas(
k.cpt.gasCosts[Revert].m_handler(k.cpt.memory.len, pos, len),
reason = "REVERT")
k.cpt.memory.extend(pos, len)
k.cpt.output = k.cpt.memory.read(pos, len)
# setError(msg, false) will signal cheap revert
k.cpt.setError("REVERT opcode executed", false)
invalidOp: Vm2OpFn = proc(k: Vm2Ctx) =
raise newException(InvalidInstruction, raise newException(InvalidInstruction,
"Invalid instruction, received an opcode " & "Invalid instruction, received an opcode " &
"not implemented in the current fork.") "not implemented in the current fork.")
# -----------
selfDestructOp: Vm2OpFn = proc(k: Vm2Ctx) =
## 0xff, Halt execution and register account for later deletion.
let beneficiary = k.cpt.stack.popAddress()
k.cpt.selfDestruct(beneficiary)
selfDestructEIP150Op: Vm2OpFn = proc(k: Vm2Ctx) =
## selfDestructEip150 (auto generated comment)
let beneficiary = k.cpt.stack.popAddress()
let gasParams = GasParams(
kind: SelfDestruct,
sd_condition: not k.cpt.accountExists(beneficiary))
let gasCost =
k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
k.cpt.gasMeter.consumeGas(
gasCost, reason = "SELFDESTRUCT EIP150")
k.cpt.selfDestruct(beneficiary)
selfDestructEip161Op: Vm2OpFn = proc(k: Vm2Ctx) =
## selfDestructEip161 (auto generated comment)
checkInStaticContext(k.cpt)
let
beneficiary = k.cpt.stack.popAddress()
isDead = not k.cpt.accountExists(beneficiary)
balance = k.cpt.getBalance(k.cpt.msg.contractAddress)
let gasParams = GasParams(
kind: SelfDestruct,
sd_condition: isDead and not balance.isZero)
let gasCost =
k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
k.cpt.gasMeter.consumeGas(
gasCost, reason = "SELFDESTRUCT EIP161")
k.cpt.selfDestruct(beneficiary)
selfDestructEIP2929Op: Vm2OpFn = proc(k: Vm2Ctx) =
## selfDestructEIP2929 (auto generated comment)
checkInStaticContext(k.cpt)
let
beneficiary = k.cpt.stack.popAddress()
isDead = not k.cpt.accountExists(beneficiary)
balance = k.cpt.getBalance(k.cpt.msg.contractAddress)
let gasParams = GasParams(
kind: SelfDestruct,
sd_condition: isDead and not balance.isZero)
var gasCost =
k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
k.cpt.vmState.mutateStateDB:
if not db.inAccessList(beneficiary):
db.accessList(beneficiary)
gasCost = gasCost + ColdAccountAccessCost
k.cpt.gasMeter.consumeGas(
gasCost, reason = "SELFDESTRUCT EIP161")
k.cpt.selfDestruct(beneficiary)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public, op exec table entries # Public, op exec table entries
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
const const
vm2OpExecSysOP*: seq[Vm2OpExec] = @[ vm2OpExecSysOp*: seq[Vm2OpExec] = @[
(opCode: Return, ## 0xf3, Halt execution returning output data.
forks: Vm2OpAllForks,
info: "Halt execution returning output data",
exec: (prep: vm2OpIgnore,
run: returnOp,
post: vm2OpIgnore)),
(opCode: Revert, ## 0xfd, Halt and revert state changes
forks: Vm2OpAllForks,
info: "Halt execution reverting state changes but returning data " &
"and remaining gas",
exec: (prep: vm2OpIgnore,
run: revertOp,
post: vm2OpIgnore)),
(opCode: Invalid, ## 0xfe, invalid instruction. (opCode: Invalid, ## 0xfe, invalid instruction.
forks: Vm2OpAllForks, forks: Vm2OpAllForks,
info: "Designated invalid instruction", info: "Designated invalid instruction",
exec: (prep: vm2OpIgnore, exec: (prep: vm2OpIgnore,
run: invalidOp, run: invalidOp,
post: vm2OpIgnore)),
(opCode: SelfDestruct, ## 0xff, Halt execution, prep for later deletion
forks: Vm2OpAllForks - Vm2OpTangerineAndLater,
info: "Halt execution and register account for later deletion",
exec: (prep: vm2OpIgnore,
run: selfDestructOp,
post: vm2OpIgnore)),
(opCode: SelfDestruct, ## 0xff, EIP150: self destruct, Tangerine
forks: Vm2OpTangerineAndLater - Vm2OpSpuriousAndLater,
info: "EIP150: Halt execution and register account for later deletion",
exec: (prep: vm2OpIgnore,
run: selfDestructEIP150Op,
post: vm2OpIgnore)),
(opCode: SelfDestruct, ## 0xff, EIP161: self destruct, Spurious and later
forks: Vm2OpSpuriousAndLater - Vm2OpBerlinAndLater,
info: "EIP161: Halt execution and register account for later deletion",
exec: (prep: vm2OpIgnore,
run: selfDestructEIP161Op,
post: vm2OpIgnore)),
(opCode: SelfDestruct, ## 0xff, EIP2929: self destruct, Berlin and later
forks: Vm2OpBerlinAndLater,
info: "EIP2929: Halt execution and register account for later deletion",
exec: (prep: vm2OpIgnore,
run: selfDestructEIP2929Op,
post: vm2OpIgnore))] post: vm2OpIgnore))]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
strformat, times, sets, sequtils, options, strformat, times, sets, options,
chronicles, stint, nimcrypto, eth/common, chronicles, stint, nimcrypto, eth/common,
./utils/[macros_procs_opcodes, v2utils_numeric], ./utils/[macros_procs_opcodes, v2utils_numeric],
./gas_meter, ./v2gas_costs, ./v2opcode_values, ./v2forks, ./gas_meter, ./v2gas_costs, ./v2opcode_values, ./v2forks,
@ -24,56 +24,10 @@ logScope:
# ################################## # ##################################
# Syntactic sugar # Syntactic sugar
proc gasEip2929AccountCheck(c: Computation, address: EthAddress, prevCost = 0.GasInt) =
c.vmState.mutateStateDB:
let gasCost = if not db.inAccessList(address):
db.accessList(address)
ColdAccountAccessCost
else:
WarmStorageReadCost
c.gasMeter.consumeGas(gasCost - prevCost, reason = "gasEIP2929AccountCheck")
template push(x: typed) {.dirty.} = template push(x: typed) {.dirty.} =
## Push an expression on the computation stack ## Push an expression on the computation stack
c.stack.push x c.stack.push x
proc writePaddedResult(mem: var Memory,
data: openarray[byte],
memPos, dataPos, len: Natural,
paddingValue = 0.byte) =
mem.extend(memPos, len)
let dataEndPosition = dataPos.int64 + len - 1
let sourceBytes = data[min(dataPos, data.len) .. min(data.len - 1, dataEndPosition)]
mem.write(memPos, sourceBytes)
# Don't duplicate zero-padding of mem.extend
let paddingOffset = min(memPos + sourceBytes.len, mem.len)
let numPaddingBytes = min(mem.len - paddingOffset, len - sourceBytes.len)
if numPaddingBytes > 0:
# TODO: avoid unnecessary memory allocation
mem.write(paddingOffset, repeat(paddingValue, numPaddingBytes))
template sstoreNetGasMeteringImpl(c: Computation, slot, newValue: Uint256) =
let stateDB = c.vmState.readOnlyStateDB
let currentValue {.inject.} = c.getStorage(slot)
let
gasParam = GasParams(
kind: Op.Sstore,
s_currentValue: currentValue,
s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot))
(gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(newValue, gasParam)
c.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}] -> {newValue} ({currentValue})")
if gasRefund != 0:
c.gasMeter.refundGas(gasRefund)
c.vmState.mutateStateDB:
db.setStorage(c.msg.contractAddress, slot, newValue)
# ################################## # ##################################
# re-implemented OP handlers # re-implemented OP handlers
@ -248,6 +202,14 @@ opHandler log1, Op.Log1
opHandler log2, Op.Log2 opHandler log2, Op.Log2
opHandler log3, Op.Log3 opHandler log3, Op.Log3
opHandler log4, Op.Log4 opHandler log4, Op.Log4
opHandler returnOp, Op.Return
opHandler revert, Op.Revert
opHandler invalidOp, Op.Invalid
opHandler selfDestruct, Op.SelfDestruct, FkFrontier
opHandler selfDestructEIP150, Op.SelfDestruct, FkTangerine
opHandler selfDestructEIP161, Op.SelfDestruct, FkSpurious
opHandler selfDestructEIP2929, Op.SelfDestruct
# ########################################## # ##########################################
# f0s: System operations. # f0s: System operations.
@ -490,84 +452,3 @@ genCall(call, Call)
genCall(callCode, CallCode) genCall(callCode, CallCode)
genCall(delegateCall, DelegateCall) genCall(delegateCall, DelegateCall)
genCall(staticCall, StaticCall) genCall(staticCall, StaticCall)
op returnOp, inline = false, startPos, size:
## 0xf3, Halt execution returning output data.
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
c.gasMeter.consumeGas(
c.gasCosts[Return].m_handler(c.memory.len, pos, len),
reason = "RETURN"
)
c.memory.extend(pos, len)
c.output = c.memory.read(pos, len)
op revert, inline = false, startPos, size:
## 0xfd, Halt execution reverting state changes but returning data and remaining gas.
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
c.gasMeter.consumeGas(
c.gasCosts[Revert].m_handler(c.memory.len, pos, len),
reason = "REVERT"
)
c.memory.extend(pos, len)
c.output = c.memory.read(pos, len)
# setError(msg, false) will signal cheap revert
c.setError("REVERT opcode executed", false)
op selfDestruct, inline = false:
## 0xff Halt execution and register account for later deletion.
let beneficiary = c.stack.popAddress()
c.selfDestruct(beneficiary)
op selfDestructEip150, inline = false:
let beneficiary = c.stack.popAddress()
let gasParams = GasParams(kind: SelfDestruct,
sd_condition: not c.accountExists(beneficiary)
)
let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP150")
c.selfDestruct(beneficiary)
op selfDestructEip161, 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
)
let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161")
c.selfDestruct(beneficiary)
# Constantinople's new opcodes
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)