mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-27 20:45:48 +00:00
Interpreter dispatch cleanups (#2913)
* `shouldPrepareTracer` always true * simple `pop` should not copy value (reading the memory shows up in a profiler) * continuation code simplified * remove some unnecessary EH
This commit is contained in:
parent
9b7e2960c2
commit
667897557a
@ -70,11 +70,8 @@ proc purgeOlderBlocksFromHistory(db: CoreDbRef, bn: BlockNumber) =
|
|||||||
if 0 < bn:
|
if 0 < bn:
|
||||||
var blkNum = bn - 1
|
var blkNum = bn - 1
|
||||||
while 0 < blkNum:
|
while 0 < blkNum:
|
||||||
try:
|
|
||||||
if not db.forgetHistory blkNum:
|
if not db.forgetHistory blkNum:
|
||||||
break
|
break
|
||||||
except RlpError as exc:
|
|
||||||
warn "Error forgetting history", err = exc.msg
|
|
||||||
blkNum = blkNum - 1
|
blkNum = blkNum - 1
|
||||||
|
|
||||||
proc persistBlocksImpl(
|
proc persistBlocksImpl(
|
||||||
|
@ -22,11 +22,8 @@ proc validateWithdrawals*(
|
|||||||
elif withdrawals.isNone:
|
elif withdrawals.isNone:
|
||||||
return err("Post-Shanghai block body must have withdrawals")
|
return err("Post-Shanghai block body must have withdrawals")
|
||||||
else:
|
else:
|
||||||
try:
|
|
||||||
if withdrawals.get.calcWithdrawalsRoot != header.withdrawalsRoot.get:
|
if withdrawals.get.calcWithdrawalsRoot != header.withdrawalsRoot.get:
|
||||||
return err("Mismatched withdrawalsRoot blockNumber =" & $header.number)
|
return err("Mismatched withdrawalsRoot blockNumber =" & $header.number)
|
||||||
except RlpError as ex:
|
|
||||||
return err(ex.msg)
|
|
||||||
else:
|
else:
|
||||||
if header.withdrawalsRoot.isSome:
|
if header.withdrawalsRoot.isSome:
|
||||||
return err("Pre-Shanghai block header must not have withdrawalsRoot")
|
return err("Pre-Shanghai block header must not have withdrawalsRoot")
|
||||||
|
@ -122,9 +122,7 @@ func jumpImpl(c: Computation; jumpTarget: UInt256): EvmResultVoid =
|
|||||||
|
|
||||||
proc popOp(cpt: VmCpt): EvmResultVoid =
|
proc popOp(cpt: VmCpt): EvmResultVoid =
|
||||||
## 0x50, Remove item from stack.
|
## 0x50, Remove item from stack.
|
||||||
cpt.stack.popInt.isOkOr:
|
cpt.stack.pop()
|
||||||
return err(error)
|
|
||||||
ok()
|
|
||||||
|
|
||||||
proc mloadOp(cpt: VmCpt): EvmResultVoid =
|
proc mloadOp(cpt: VmCpt): EvmResultVoid =
|
||||||
## 0x51, Load word from memory
|
## 0x51, Load word from memory
|
||||||
|
@ -27,7 +27,6 @@ logScope:
|
|||||||
|
|
||||||
proc runVM(
|
proc runVM(
|
||||||
c: VmCpt,
|
c: VmCpt,
|
||||||
shouldPrepareTracer: bool,
|
|
||||||
fork: static EVMFork,
|
fork: static EVMFork,
|
||||||
tracingEnabled: static bool,
|
tracingEnabled: static bool,
|
||||||
): EvmResultVoid =
|
): EvmResultVoid =
|
||||||
@ -35,16 +34,7 @@ proc runVM(
|
|||||||
## this function is instantiated so that selection of fork-specific
|
## this function is instantiated so that selection of fork-specific
|
||||||
## versions of functions happens only once
|
## versions of functions happens only once
|
||||||
|
|
||||||
# It's important not to re-prepare the tracer after
|
|
||||||
# an async operation, only after a call/create.
|
|
||||||
#
|
|
||||||
# That is, tracingEnabled is checked in many places, and
|
|
||||||
# indicates something like, "Do we want tracing to be
|
|
||||||
# enabled?", whereas shouldPrepareTracer is more like,
|
|
||||||
# "Are we at a spot right now where we want to re-initialize
|
|
||||||
# the tracer?"
|
|
||||||
when tracingEnabled:
|
when tracingEnabled:
|
||||||
if shouldPrepareTracer:
|
|
||||||
c.prepareTracer()
|
c.prepareTracer()
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
@ -55,7 +45,7 @@ proc runVM(
|
|||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
macro selectVM(v: VmCpt, shouldPrepareTracer: bool, fork: EVMFork, tracingEnabled: bool): EvmResultVoid =
|
macro selectVM(v: VmCpt, fork: EVMFork, tracingEnabled: bool): EvmResultVoid =
|
||||||
# Generate opcode dispatcher that calls selectVM with a literal for each fork:
|
# Generate opcode dispatcher that calls selectVM with a literal for each fork:
|
||||||
#
|
#
|
||||||
# case fork
|
# case fork
|
||||||
@ -69,8 +59,8 @@ macro selectVM(v: VmCpt, shouldPrepareTracer: bool, fork: EVMFork, tracingEnable
|
|||||||
`fork`
|
`fork`
|
||||||
call = quote:
|
call = quote:
|
||||||
case `tracingEnabled`
|
case `tracingEnabled`
|
||||||
of false: runVM(`v`, `shouldPrepareTracer`, `forkVal`, `false`)
|
of false: runVM(`v`, `fork`, false)
|
||||||
of true: runVM(`v`, `shouldPrepareTracer`, `forkVal`, `true`)
|
of true: runVM(`v`, `fork`, true)
|
||||||
|
|
||||||
caseStmt.add nnkOfBranch.newTree(forkVal, call)
|
caseStmt.add nnkOfBranch.newTree(forkVal, call)
|
||||||
caseStmt
|
caseStmt
|
||||||
@ -193,18 +183,17 @@ template handleEvmError(x: EvmErrorObj) =
|
|||||||
depth = $(c.msg.depth + 1) # plus one to match tracer depth, and avoid confusion
|
depth = $(c.msg.depth + 1) # plus one to match tracer depth, and avoid confusion
|
||||||
c.setError("Opcode Dispatch Error: " & msg & ", depth=" & depth, true)
|
c.setError("Opcode Dispatch Error: " & msg & ", depth=" & depth, true)
|
||||||
|
|
||||||
proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true) =
|
proc executeOpcodes*(c: Computation) =
|
||||||
let fork = c.fork
|
let fork = c.fork
|
||||||
|
|
||||||
block blockOne:
|
block blockOne:
|
||||||
if c.continuation.isNil:
|
let cont = c.continuation
|
||||||
|
if cont.isNil:
|
||||||
let precompile = c.fork.getPrecompile(c.msg.codeAddress)
|
let precompile = c.fork.getPrecompile(c.msg.codeAddress)
|
||||||
if precompile.isSome:
|
if precompile.isSome:
|
||||||
c.execPrecompile(precompile[])
|
c.execPrecompile(precompile[])
|
||||||
break blockOne
|
break blockOne
|
||||||
|
else:
|
||||||
let cont = c.continuation
|
|
||||||
if not cont.isNil:
|
|
||||||
c.continuation = nil
|
c.continuation = nil
|
||||||
cont().isOkOr:
|
cont().isOkOr:
|
||||||
handleEvmError(error)
|
handleEvmError(error)
|
||||||
@ -216,20 +205,16 @@ proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true) =
|
|||||||
# and then call this proc again.
|
# and then call this proc again.
|
||||||
break blockOne
|
break blockOne
|
||||||
|
|
||||||
# FIXME-Adam: I hate how convoluted this is. See also the comment in
|
# traceOpCodeEnded is normally called directly after opcode execution
|
||||||
# op_dispatcher.nim. The idea here is that we need to call
|
# but in the case that a continuation is created, it must run after that
|
||||||
# traceOpCodeEnded at the end of the opcode (and only if there
|
# continuation has finished
|
||||||
# hasn't been an exception thrown); otherwise we run into problems
|
if c.tracingEnabled:
|
||||||
# if an exception (e.g. out of gas) is thrown during a continuation.
|
|
||||||
# So this code says, "If we've just run a continuation, but there's
|
|
||||||
# no *subsequent* continuation, then the opcode is done."
|
|
||||||
if c.tracingEnabled and not (cont.isNil) and nextCont.isNil:
|
|
||||||
c.traceOpCodeEnded(c.instr, c.opIndex)
|
c.traceOpCodeEnded(c.instr, c.opIndex)
|
||||||
|
|
||||||
if c.instr == Return or c.instr == Revert or c.instr == SelfDestruct:
|
if c.instr == Return or c.instr == Revert or c.instr == SelfDestruct:
|
||||||
break blockOne
|
break blockOne
|
||||||
|
|
||||||
c.selectVM(shouldPrepareTracer, fork, c.tracingEnabled).isOkOr:
|
c.selectVM(fork, c.tracingEnabled).isOkOr:
|
||||||
handleEvmError(error)
|
handleEvmError(error)
|
||||||
break blockOne # this break is not needed but make the flow clear
|
break blockOne # this break is not needed but make the flow clear
|
||||||
|
|
||||||
@ -256,24 +241,24 @@ when vm_use_recursion:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
proc execCallOrCreate*(cParam: Computation) =
|
proc execCallOrCreate*(cParam: Computation) =
|
||||||
var (c, before, shouldPrepareTracer) = (cParam, true, true)
|
var (c, before) = (cParam, true)
|
||||||
|
|
||||||
# No actual recursion, but simulate recursion including before/after/dispose.
|
# No actual recursion, but simulate recursion including before/after/dispose.
|
||||||
while true:
|
while true:
|
||||||
while true:
|
while true:
|
||||||
if before and c.beforeExec():
|
if before and c.beforeExec():
|
||||||
break
|
break
|
||||||
c.executeOpcodes(shouldPrepareTracer)
|
c.executeOpcodes()
|
||||||
if c.continuation.isNil:
|
if c.continuation.isNil:
|
||||||
c.afterExec()
|
c.afterExec()
|
||||||
break
|
break
|
||||||
(before, shouldPrepareTracer, c.child, c, c.parent) =
|
(before, c.child, c, c.parent) =
|
||||||
(true, true, nil.Computation, c.child, c)
|
(true, nil.Computation, c.child, c)
|
||||||
if c.parent.isNil:
|
if c.parent.isNil:
|
||||||
break
|
break
|
||||||
c.dispose()
|
c.dispose()
|
||||||
(before, shouldPrepareTracer, c.parent, c) =
|
(before, c.parent, c) =
|
||||||
(false, true, nil.Computation, c.parent)
|
(false, nil.Computation, c.parent)
|
||||||
|
|
||||||
while not c.isNil:
|
while not c.isNil:
|
||||||
c.dispose()
|
c.dispose()
|
||||||
|
@ -132,6 +132,11 @@ func popInt*(stack: EvmStack): EvmResult[UInt256] =
|
|||||||
func popAddress*(stack: EvmStack): EvmResult[Address] =
|
func popAddress*(stack: EvmStack): EvmResult[Address] =
|
||||||
popAux(stack, Address)
|
popAux(stack, Address)
|
||||||
|
|
||||||
|
func pop*(stack: EvmStack): EvmResult[void] =
|
||||||
|
? ensurePop(stack, 1)
|
||||||
|
stack.len -= 1
|
||||||
|
ok()
|
||||||
|
|
||||||
proc init*(_: type EvmStack): EvmStack =
|
proc init*(_: type EvmStack): EvmStack =
|
||||||
let memory = c_malloc(evmStackSize * sizeof(EvmStackElement) + 31)
|
let memory = c_malloc(evmStackSize * sizeof(EvmStackElement) + 31)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except
|
# at your option. This file may not be copied, modified, or distributed except
|
||||||
# according to those terms.
|
# according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[json, sets, streams, strutils],
|
std/[json, sets, streams, strutils],
|
||||||
eth/common/eth_types,
|
eth/common/eth_types,
|
||||||
@ -164,17 +166,11 @@ method captureOpStart*(ctx: JsonTracer, c: Computation,
|
|||||||
ctx.stack.add(%(v.encodeHex))
|
ctx.stack.add(%(v.encodeHex))
|
||||||
|
|
||||||
if TracerFlags.DisableStorage notin ctx.flags and op == Sstore:
|
if TracerFlags.DisableStorage notin ctx.flags and op == Sstore:
|
||||||
try:
|
|
||||||
if c.stack.len > 1:
|
if c.stack.len > 1:
|
||||||
ctx.rememberStorageKey(c.msg.depth,
|
ctx.rememberStorageKey(c.msg.depth,
|
||||||
c.stack[^1, UInt256].expect("stack constains more than 2 elements"))
|
c.stack[^1, UInt256].expect("stack constains more than 2 elements"))
|
||||||
except ValueError as ex:
|
|
||||||
error "JsonTracer captureOpStart", msg=ex.msg
|
|
||||||
|
|
||||||
try:
|
|
||||||
ctx.captureOpImpl(c, pc, op, 0, 0, [], depth, Opt.none(string))
|
ctx.captureOpImpl(c, pc, op, 0, 0, [], depth, Opt.none(string))
|
||||||
except RlpError as ex:
|
|
||||||
error "JsonTracer captureOpStart", msg=ex.msg
|
|
||||||
|
|
||||||
# make sure captureOpEnd get the right opIndex
|
# make sure captureOpEnd get the right opIndex
|
||||||
result = ctx.index
|
result = ctx.index
|
||||||
@ -229,13 +225,10 @@ method captureFault*(ctx: JsonTracer, comp: Computation,
|
|||||||
ctx.node = nil
|
ctx.node = nil
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
ctx.captureOpImpl(comp, pc, op, gas, refund, rData, depth, error)
|
ctx.captureOpImpl(comp, pc, op, gas, refund, rData, depth, error)
|
||||||
doAssert(ctx.node.isNil.not)
|
doAssert(ctx.node.isNil.not)
|
||||||
ctx.writeJson(ctx.node)
|
ctx.writeJson(ctx.node)
|
||||||
ctx.node = nil
|
ctx.node = nil
|
||||||
except RlpError as ex:
|
|
||||||
error "JsonTracer captureOpFault", msg=ex.msg
|
|
||||||
|
|
||||||
proc close*(ctx: JsonTracer) =
|
proc close*(ctx: JsonTracer) {.raises: [IOError, OSError].} =
|
||||||
ctx.stream.close()
|
ctx.stream.close()
|
||||||
|
@ -61,6 +61,14 @@ proc runStackTests() =
|
|||||||
check stack.push(element).isOk
|
check stack.push(element).isOk
|
||||||
check(stack.popInt.get == 3.u256)
|
check(stack.popInt.get == 3.u256)
|
||||||
|
|
||||||
|
test "pop requires stack item":
|
||||||
|
var stack = EvmStack.init()
|
||||||
|
defer: stack.dispose()
|
||||||
|
check:
|
||||||
|
stack.pop().isErr()
|
||||||
|
stack.push(1'u).isOk()
|
||||||
|
stack.pop().isOk()
|
||||||
|
|
||||||
test "swap correct":
|
test "swap correct":
|
||||||
privateAccess(EvmStack)
|
privateAccess(EvmStack)
|
||||||
var stack = EvmStack.init()
|
var stack = EvmStack.init()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user