nimbus-eth1/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim

191 lines
5.9 KiB
Nim

# 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.
## EVM Opcode Handlers: Helper Functions & Macros
## ==============================================
##
const
kludge {.intdefine.}: int = 0
breakCircularDependency {.used.} = kludge > 0
import
../../../errors,
./oph_defs,
macros,
stint,
strutils
type
OphNumToTextFn* = proc(n: int): string
OpHanldlerImplFn* = proc(k: var Vm2Ctx; n: int)
const
recForkSet = "Vm2OpAllForks"
# ------------------------------------------------------------------------------
# Kludge BEGIN
# ------------------------------------------------------------------------------
when not breakCircularDependency:
import
../../../db/accounts_cache,
../../v2state,
../../v2types,
../gas_meter,
../v2gas_costs,
eth/common
else:
const
emvcStatic = 1
ColdAccountAccessCost = 2
WarmStorageReadCost = 3
# function stubs from v2state.nim
template mutateStateDB(vmState: BaseVMState, body: untyped) =
block:
var db {.inject.} = vmState.accountDb
body
# 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
# function stubs from gas_meter.nim
proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard
# ------------------------------------------------------------------------------
# Kludge END
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc asIdent(id, name: string): NimNode {.compileTime.} =
result = nnkExprColonExpr.newTree(
newIdentNode(id),
newIdentNode(name))
proc asText(id, name: string): NimNode {.compileTime.} =
result = nnkExprColonExpr.newTree(
newIdentNode(id),
newLit(name))
# ------------------------------------------------------------------------------
# Public
# ------------------------------------------------------------------------------
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 checkInStaticContext*(c: Computation) =
## Verify static context in handler function, raise an error otherwise
if emvcStatic == c.msg.flags:
# TODO: if possible, this check only appear
# when fork >= FkByzantium
raise newException(
StaticContextError,
"Cannot modify state while inside of STATICCALL context")
macro genOphHandlers*(runHandler: static[OphNumToTextFn];
itemInfo: static[OphNumToTextFn];
inxList: static[openArray[int]];
body: static[OpHanldlerImplFn]): untyped =
## Generate the equivalent of
## ::
## const <runHandler>: Vm2OpFn = proc (k: var Vm2Ctx) =
## ## <itemInfo(n)>,
## <body(k,n)>
##
## for all `n` in `inxList`
##
result = newStmtList()
for n in inxList:
let
fnName = ident(n.runHandler)
comment = newCommentStmtNode(n.itemInfo)
# => push##Op: Vm2OpFn = proc (k: var Vm2Ctx) = ...
result.add quote do:
const `fnName`: Vm2OpFn = proc(k: var Vm2Ctx) =
`comment`
`body`(k,`n`)
# echo ">>>", result.repr
macro genOphList*(runHandler: static[OphNumToTextFn];
handlerInfo: static[OphNumToTextFn];
inxList: static[openArray[int]];
varName: static[string];
opCode: static[OphNumToTextFn]): untyped =
## Generate
## ::
## const <varName>*: seq[Vm2OpExec] = @[ <records> ]
##
## where <records> is a sequence of <record(n)> items like
## ::
## (opCode: <opCode(n)>,
## forks: Vm2OpAllForks,
## info: <handlerInfo(n)>,
## exec: (prep: vm2OpIgnore,
## run: <runHandler(n)>,
## post: vm2OpIgnore))
##
## for all `n` in `inxList`
##
var records = nnkBracket.newTree()
for n in inxList:
var handlerName = n.runHandler.multiReplace(("Op",""),("OP",""))
records.add nnkPar.newTree(
"opCode".asIdent(n.opCode),
"forks".asIdent(recForkSet),
"name".asText(handlerName),
"info".asText(n.handlerInfo),
nnkExprColonExpr.newTree(
newIdentNode("exec"),
nnkPar.newTree(
"prep".asIdent("vm2OpIgnore"),
"run".asIdent(n.runHandler),
"post".asIdent("vm2OpIgnore"))))
# => const <varName>*: seq[Vm2OpExec] = @[ <records> ]
result = nnkStmtList.newTree(
nnkConstSection.newTree(
nnkConstDef.newTree(
nnkPostfix.newTree(
newIdentNode("*"),
newIdentNode(varName)),
nnkBracketExpr.newTree(
newIdentNode("seq"),
newIdentNode("Vm2OpExec")),
nnkPrefix.newTree(
newIdentNode("@"), records))))
# echo ">>> ", result.repr
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------