diff --git a/nimbus/vm2/computation.nim b/nimbus/vm2/computation.nim index c8af4dee2..b17675e52 100644 --- a/nimbus/vm2/computation.nim +++ b/nimbus/vm2/computation.nim @@ -5,144 +5,8 @@ # * 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. -when defined(evmc_enabled): - {.fatal: "Flags \"evmc_enabled\" and \"vm2_enabled\" are mutually exclusive"} - import - chronicles, strformat, macros, options, - sets, eth/[common, keys], - ../constants, - ./compu_helper, - ./interpreter/forks_list, - ./interpreter_dispatch, - ./message, ./types, ./state, - ../db/accounts_cache, - ./precompiles + ./interpreter_dispatch -logScope: - topics = "vm computation" - -const - ripemdAddr = block: - proc initAddress(x: int): EthAddress {.compileTime.} = - result[19] = x.byte - initAddress(3) - - -proc beforeExecCall(c: Computation) = - c.snapshot() - if c.msg.kind == evmcCall: - c.vmState.mutateStateDb: - db.subBalance(c.msg.sender, c.msg.value) - db.addBalance(c.msg.contractAddress, c.msg.value) - -proc afterExecCall(c: Computation) = - ## Collect all of the accounts that *may* need to be deleted based on EIP161 - ## https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md - ## also see: https://github.com/ethereum/EIPs/issues/716 - - if c.isError or c.fork >= FKByzantium: - if c.msg.contractAddress == ripemdAddr: - # Special case to account for geth+parity bug - c.vmState.touchedAccounts.incl c.msg.contractAddress - - if c.isSuccess: - c.commit() - c.touchedAccounts.incl c.msg.contractAddress - else: - c.rollback() - -proc beforeExecCreate(c: Computation): bool = - 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() - - if c.vmState.readOnlyStateDb().hasCodeOrNonce(c.msg.contractAddress): - c.setError("Address collision when creating contract address={c.msg.contractAddress.toHex}", true) - c.rollback() - return true - - c.vmState.mutateStateDb: - db.subBalance(c.msg.sender, c.msg.value) - db.addBalance(c.msg.contractAddress, c.msg.value) - db.clearStorage(c.msg.contractAddress) - if c.fork >= FkSpurious: - # EIP161 nonce incrementation - db.incNonce(c.msg.contractAddress) - - return false - -proc afterExecCreate(c: Computation) = - if c.isSuccess: - let fork = c.fork - let contractFailed = not c.writeContract(fork) - if contractFailed and fork >= FkHomestead: - c.setError(&"writeContract failed, depth={c.msg.depth}", true) - - if c.isSuccess: - c.commit() - else: - c.rollback() - -proc beforeExec(c: Computation): bool = - if not c.msg.isCreate: - c.beforeExecCall() - false - else: - c.beforeExecCreate() - -proc afterExec(c: Computation) = - if not c.msg.isCreate: - c.afterExecCall() - else: - c.afterExecCreate() - -proc executeOpcodes*(c: Computation) = - let fork = c.fork - - block: - if not c.continuation.isNil: - c.continuation = nil - elif c.execPrecompiles(fork): - break - - try: - c.selectVM(fork) - except CatchableError as e: - c.setError( - &"Opcode Dispatch Error msg={e.msg}, depth={c.msg.depth}", true) - - if c.isError() and c.continuation.isNil: - if c.tracingEnabled: c.traceError() - debug "executeOpcodes error", msg=c.error.info - - -proc execCallOrCreate*(cParam: Computation) = - var (c, before) = (cParam, true) - defer: - while not c.isNil: - c.dispose() - c = c.parent - - # No actual recursion, but simulate recursion including before/after/dispose. - while true: - while true: - if before and c.beforeExec(): - break - c.executeOpcodes() - if c.continuation.isNil: - c.afterExec() - break - (before, c.child, c, c.parent) = (true, nil.Computation, c.child, c) - if c.parent.isNil: - break - c.dispose() - (before, c.parent, c) = (false, nil.Computation, c.parent) - (c.continuation)() +export + interpreter_dispatch diff --git a/nimbus/vm2/interpreter_dispatch.nim b/nimbus/vm2/interpreter_dispatch.nim index 10c395edd..9033e655d 100644 --- a/nimbus/vm2/interpreter_dispatch.nim +++ b/nimbus/vm2/interpreter_dispatch.nim @@ -9,25 +9,42 @@ # according to those terms. const - # help with low memory when compiling + # help with low memory when compiling selectVM() function lowmem {.intdefine.}: int = 0 lowMemoryCompileTime {.used.} = lowmem > 0 import + ../constants, + ../db/accounts_cache, ./code_stream, ./compu_helper, ./interpreter/op_dispatcher, + ./message, + ./precompiles, + ./state, ./types, - chronicles + chronicles, + eth/[common, keys], + macros, + options, + sets, + stew/byteutils, + strformat logScope: topics = "vm opcode" +const + ripemdAddr = block: + proc initAddress(x: int): EthAddress {.compileTime.} = + result[19] = x.byte + initAddress(3) + # ------------------------------------------------------------------------------ -# Public functions +# Private functions # ------------------------------------------------------------------------------ -proc selectVM*(c: Computation, fork: Fork) {.gcsafe.} = +proc selectVM(c: Computation, fork: Fork) {.gcsafe.} = ## Op code execution handler main loop. var desc: Vm2Ctx desc.cpt = c @@ -87,6 +104,132 @@ proc selectVM*(c: Computation, fork: Fork) {.gcsafe.} = genLowMemDispatcher(fork, c.instr, desc) + +proc beforeExecCall(c: Computation) = + c.snapshot() + if c.msg.kind == evmcCall: + c.vmState.mutateStateDb: + db.subBalance(c.msg.sender, c.msg.value) + db.addBalance(c.msg.contractAddress, c.msg.value) + +proc afterExecCall(c: Computation) = + ## Collect all of the accounts that *may* need to be deleted based on EIP161 + ## https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md + ## also see: https://github.com/ethereum/EIPs/issues/716 + + if c.isError or c.fork >= FKByzantium: + if c.msg.contractAddress == ripemdAddr: + # Special case to account for geth+parity bug + c.vmState.touchedAccounts.incl c.msg.contractAddress + + if c.isSuccess: + c.commit() + c.touchedAccounts.incl c.msg.contractAddress + else: + c.rollback() + + +proc beforeExecCreate(c: Computation): bool = + 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() + + if c.vmState.readOnlyStateDb().hasCodeOrNonce(c.msg.contractAddress): + var blurb =c.msg.contractAddress.toHex + c.setError("Address collision when creating contract address={blurb}", true) + c.rollback() + return true + + c.vmState.mutateStateDb: + db.subBalance(c.msg.sender, c.msg.value) + db.addBalance(c.msg.contractAddress, c.msg.value) + db.clearStorage(c.msg.contractAddress) + if c.fork >= FkSpurious: + # EIP161 nonce incrementation + db.incNonce(c.msg.contractAddress) + + return false + +proc afterExecCreate(c: Computation) = + if c.isSuccess: + let fork = c.fork + let contractFailed = not c.writeContract(fork) + if contractFailed and fork >= FkHomestead: + c.setError(&"writeContract failed, depth={c.msg.depth}", true) + + if c.isSuccess: + c.commit() + else: + c.rollback() + + +proc beforeExec(c: Computation): bool = + if not c.msg.isCreate: + c.beforeExecCall() + false + else: + c.beforeExecCreate() + +proc afterExec(c: Computation) = + if not c.msg.isCreate: + c.afterExecCall() + else: + c.afterExecCreate() + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc executeOpcodes*(c: Computation) = + let fork = c.fork + + block: + if not c.continuation.isNil: + c.continuation = nil + elif c.execPrecompiles(fork): + break + + try: + c.selectVM(fork) + except CatchableError as e: + c.setError( + &"Opcode Dispatch Error msg={e.msg}, depth={c.msg.depth}", true) + + if c.isError() and c.continuation.isNil: + if c.tracingEnabled: c.traceError() + debug "executeOpcodes error", msg=c.error.info + + +proc execCallOrCreate*(cParam: Computation) = + var (c, before) = (cParam, true) + defer: + while not c.isNil: + c.dispose() + c = c.parent + + # No actual recursion, but simulate recursion including before/after/dispose. + while true: + while true: + if before and c.beforeExec(): + break + c.executeOpcodes() + if c.continuation.isNil: + c.afterExec() + break + (before, c.child, c, c.parent) = (true, nil.Computation, c.child, c) + if c.parent.isNil: + break + c.dispose() + (before, c.parent, c) = (false, nil.Computation, c.parent) + (c.continuation)() + # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------