# 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. 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, ./message, ./types, ./state, ../db/accounts_cache, ./precompiles logScope: topics = "vm computation" proc initAddress(x: int): EthAddress {.compileTime.} = result[19] = x.byte const ripemdAddr = initAddress(3) proc executeOpcodes*(c: Computation) {.gcsafe.} 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 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)() import interpreter_dispatch 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