From 1bdbfda37f0467e637aeb741553a969ce9512b48 Mon Sep 17 00:00:00 2001 From: Jordan Hrycaj Date: Fri, 16 Apr 2021 11:53:05 +0100 Subject: [PATCH] re-integrated/added Create and Create2 handlers --- nimbus/vm2/interpreter/op_codes.nim | 3 +- nimbus/vm2/interpreter/op_handlers.nim | 4 +- .../op_handlers/oph_arithmetic.nim | 8 +- .../interpreter/op_handlers/oph_create.nim | 255 ++++++++++++++++++ .../vm2/interpreter/op_handlers/oph_defs.nim | 9 +- .../interpreter/op_handlers/oph_helpers.nim | 2 - .../interpreter/op_handlers/oph_sysops.nim | 23 +- nimbus/vm2/interpreter/opcodes_impl.nim | 72 +---- 8 files changed, 280 insertions(+), 96 deletions(-) create mode 100644 nimbus/vm2/interpreter/op_handlers/oph_create.nim diff --git a/nimbus/vm2/interpreter/op_codes.nim b/nimbus/vm2/interpreter/op_codes.nim index eb3f4420f..819122f1d 100644 --- a/nimbus/vm2/interpreter/op_codes.nim +++ b/nimbus/vm2/interpreter/op_codes.nim @@ -194,8 +194,7 @@ type ## alternative account's code, but persisting the ## current values for sender and value. Create2 = 0xf5, ## Behaves identically to CREATE, except using - ## keccak256( 0xff ++ address ++ salt ++ - ## keccak256(init_code))[12:] + ## keccak256 Nop0xF6, Nop0xF7, Nop0xF8, Nop0xF9, ## .. diff --git a/nimbus/vm2/interpreter/op_handlers.nim b/nimbus/vm2/interpreter/op_handlers.nim index 61663bbd3..313c7f639 100644 --- a/nimbus/vm2/interpreter/op_handlers.nim +++ b/nimbus/vm2/interpreter/op_handlers.nim @@ -22,7 +22,7 @@ import ./op_handlers/[oph_defs, oph_arithmetic, oph_hash, oph_envinfo, oph_blockdata, oph_memory, oph_push, oph_dup, oph_swap, oph_log, - oph_sysops] + oph_create, oph_sysops] # ------------------------------------------------------------------------------ # Helper @@ -51,7 +51,7 @@ proc mkOpTable(select: Fork): array[Op,Vm2OpExec] {.compileTime.} = result.importList(select, vm2OpExecDup, "Dup") result.importList(select, vm2OpExecSwap, "Swap") result.importList(select, vm2OpExecLog, "Log") - #result.importList(select, vm2OpExecCreate, "Create") + result.importList(select, vm2OpExecCreate, "Create") #result.importList(select, vm2OpExecCall, "Call") result.importList(select, vm2OpExecSysOp, "SysOp") diff --git a/nimbus/vm2/interpreter/op_handlers/oph_arithmetic.nim b/nimbus/vm2/interpreter/op_handlers/oph_arithmetic.nim index 8a3847822..078449f59 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_arithmetic.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_arithmetic.nim @@ -41,8 +41,6 @@ when not breakCircularDependency: else: import macros - var blindGasCosts: array[Op,int] - # copied from stack.nim macro genTupleType(len: static[int], elemType: untyped): untyped = result = nnkTupleConstr.newNimNode() @@ -50,13 +48,13 @@ else: # function stubs from stack.nim (to satisfy compiler logic) proc push[T](x: Stack; n: T) = discard - proc popInt(x: var Stack): UInt256 = discard + proc popInt(x: var Stack): UInt256 = result 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 gasCosts(c: Computation): array[Op,int] = result # function stubs from v2utils_numeric.nim proc extractSign(v: var UInt256, sign: var bool) = discard @@ -67,7 +65,7 @@ else: proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard # stubs from v2gas_costs.nim - proc d_handler(x: int; value: Uint256): int = 0 + proc d_handler(x: int; value: Uint256): int = result # ------------------------------------------------------------------------------ # Kludge END diff --git a/nimbus/vm2/interpreter/op_handlers/oph_create.nim b/nimbus/vm2/interpreter/op_handlers/oph_create.nim new file mode 100644 index 000000000..9f426fd48 --- /dev/null +++ b/nimbus/vm2/interpreter/op_handlers/oph_create.nim @@ -0,0 +1,255 @@ +# 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: Create Operations +## ====================================== +## + +const + kludge {.intdefine.}: int = 0 + breakCircularDependency {.used.} = kludge > 0 + +import + ../../../errors, + ./oph_defs, + ./oph_helpers, + chronicles, + strformat, + stint + +# ------------------------------------------------------------------------------ +# Kludge BEGIN +# ------------------------------------------------------------------------------ + +when not breakCircularDependency: + import + ../../../constants, + ../../stack, + ../../v2computation, + ../../v2memory, + ../../v2state, + ../../v2types, + ../gas_meter, + ../utils/v2utils_numeric, + ../v2gas_costs, + eth/common + +else: + import macros + + type + GasResult = tuple[gasCost, gasRefund: GasInt] + const + evmcCreate = 42 + evmcCreate2 = 43 + MaxCallDepth = 45 + + # function stubs from stack.nim (to satisfy compiler logic) + proc top[T](x: Stack, value: T) = discard + proc peekInt(x: Stack): UInt256 = result + proc popInt(x: var Stack): UInt256 = result + + # function stubs from v2computation.nim (to satisfy compiler logic) + proc gasCosts(c: Computation): array[Op,int] = result + proc getBalance[T](c: Computation, address: T): Uint256 = result + proc newComputation[A,B](v:A, m:B, salt = 0.u256): Computation = new result + func shouldBurnGas(c: Computation): bool = result + proc isSuccess(c: Computation): bool = result + proc merge(c, child: Computation) = discard + template chainTo(c, d: Computation, e: untyped) = + c.child = d; c.continuation = proc() = e + + # function stubs from v2utils_numeric.nim + func safeInt(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 gas_meter.nim + proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard + proc returnGas(gasMeter: var GasMeter; amount: GasInt) = discard + + # stubs from v2gas_costs.nim + type GasParams = object + case kind*: Op + of Create: + cr_currentMemSize, cr_memOffset, cr_memLength: int64 + else: + discard + proc c_handler(x: int; y: Uint256, z: GasParams): GasResult = result + proc m_handler(x: int; curMemSize, memOffset, memLen: int64): int = result + +# ------------------------------------------------------------------------------ +# Kludge END +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# Private, op handlers implementation +# ------------------------------------------------------------------------------ + +const + createOp: Vm2OpFn = proc(k: Vm2Ctx) = + ## 0xf0, Create a new account with associated code + checkInStaticContext(k.cpt) + + let + endowment = k.cpt.stack.popInt() + memPos = k.cpt.stack.popInt().safeInt + memLen = k.cpt.stack.peekInt().safeInt + salt = 0.u256 + + k.cpt.stack.top(0) + + let gasParams = GasParams( + kind: Create, + cr_currentMemSize: k.cpt.memory.len, + cr_memOffset: memPos, + cr_memLength: memLen) + + var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost + k.cpt.gasMeter.consumeGas( + gasCost, reason = &"CREATE: GasCreate + {memLen} * memory expansion") + k.cpt.memory.extend(memPos, memLen) + k.cpt.returnData.setLen(0) + + if k.cpt.msg.depth >= MaxCallDepth: + debug "Computation Failure", + reason = "Stack too deep", + maxDepth = MaxCallDepth, + depth = k.cpt.msg.depth + return + + if endowment != 0: + let senderBalance = k.cpt.getBalance(k.cpt.msg.contractAddress) + if senderBalance < endowment: + debug "Computation Failure", + reason = "Insufficient funds available to transfer", + required = endowment, + balance = senderBalance + return + + var createMsgGas = k.cpt.gasMeter.gasRemaining + if k.cpt.fork >= FkTangerine: + createMsgGas -= createMsgGas div 64 + k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE") + + let childMsg = Message( + kind: evmcCreate, + depth: k.cpt.msg.depth + 1, + gas: createMsgGas, + sender: k.cpt.msg.contractAddress, + value: endowment, + data: k.cpt.memory.read(memPos, memLen)) + + var child = newComputation(k.cpt.vmState, childMsg, salt) + k.cpt.chainTo(child): + if not child.shouldBurnGas: + k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining) + + if child.isSuccess: + k.cpt.merge(child) + k.cpt.stack.top child.msg.contractAddress + else: + k.cpt.returnData = child.output + + # --------------------- + + create2Op: Vm2OpFn = proc(k: Vm2Ctx) = + ## 0xf5, Behaves identically to CREATE, except using keccak256 + checkInStaticContext(k.cpt) + + let + endowment = k.cpt.stack.popInt() + memPos = k.cpt.stack.popInt().safeInt + memLen = k.cpt.stack.popInt().safeInt + salt = k.cpt.stack.peekInt() + + k.cpt.stack.top(0) + + let gasParams = GasParams( + kind: Create, + cr_currentMemSize: k.cpt.memory.len, + cr_memOffset: memPos, + cr_memLength: memLen) + + var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost + gasCost = gasCost + k.cpt.gasCosts[Create2].m_handler(0, 0, memLen) + + k.cpt.gasMeter.consumeGas( + gasCost, reason = &"CREATE: GasCreate + {memLen} * memory expansion") + k.cpt.memory.extend(memPos, memLen) + k.cpt.returnData.setLen(0) + + if k.cpt.msg.depth >= MaxCallDepth: + debug "Computation Failure", + reason = "Stack too deep", + maxDepth = MaxCallDepth, + depth = k.cpt.msg.depth + return + + if endowment != 0: + let senderBalance = k.cpt.getBalance(k.cpt.msg.contractAddress) + if senderBalance < endowment: + debug "Computation Failure", + reason = "Insufficient funds available to transfer", + required = endowment, + balance = senderBalance + return + + var createMsgGas = k.cpt.gasMeter.gasRemaining + if k.cpt.fork >= FkTangerine: + createMsgGas -= createMsgGas div 64 + k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE") + + let childMsg = Message( + kind: evmcCreate2, + depth: k.cpt.msg.depth + 1, + gas: createMsgGas, + sender: k.cpt.msg.contractAddress, + value: endowment, + data: k.cpt.memory.read(memPos, memLen)) + + var child = newComputation(k.cpt.vmState, childMsg, salt) + k.cpt.chainTo(child): + if not child.shouldBurnGas: + k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining) + + if child.isSuccess: + k.cpt.merge(child) + k.cpt.stack.top child.msg.contractAddress + else: + k.cpt.returnData = child.output + +# ------------------------------------------------------------------------------ +# Public, op exec table entries +# ------------------------------------------------------------------------------ + +const + vm2OpExecCreate*: seq[Vm2OpExec] = @[ + + (opCode: Create, ## 0xf0, Create a new account with associated code + forks: Vm2OpAllForks, + info: "Create a new account with associated code", + exec: (prep: vm2OpIgnore, + run: createOp, + post: vm2OpIgnore)), + + (opCode: Create2, ## 0xf5, Create using keccak256 + forks: Vm2OpAllForks, + info: "Behaves identically to CREATE, except using keccak256", + exec: (prep: vm2OpIgnore, + run: create2Op, + post: vm2OpIgnore))] + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/vm2/interpreter/op_handlers/oph_defs.nim b/nimbus/vm2/interpreter/op_handlers/oph_defs.nim index 14adcbf3b..8f2176204 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_defs.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_defs.nim @@ -37,6 +37,8 @@ else: {.fatal: "Flag \"vm2_enabled\" must be unset "& "while circular dependency breaker kludge is activated".} type + GasInt* = int + ReadOnlyStateDB* = seq[byte] @@ -51,8 +53,11 @@ else: accountDb*: ReadOnlyStateDB Message* = ref object + kind*: int + depth*: int + gas*: GasInt contractAddress*: EthAddress - sender*: UInt256 + sender*: EthAddress value*: UInt256 data*: seq[byte] flags*: int @@ -68,6 +73,8 @@ else: code*: CodeStream returnData*: seq[byte] fork*: Fork + parent*, child*: Computation + continuation*: proc() {.gcsafe.} # ------------------------------------------------------------------------------ # Kludge END diff --git a/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim b/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim index 1b1b3dc5e..8b974f153 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim @@ -47,8 +47,6 @@ else: emvcStatic = 1 ColdAccountAccessCost = 2 WarmStorageReadCost = 3 - type - GasInt = int # function stubs from v2state.nim template mutateStateDB(vmState: BaseVMState, body: untyped) = diff --git a/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim b/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim index 379acf079..a72ca33c5 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim @@ -43,14 +43,9 @@ 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 = @@ -58,23 +53,23 @@ else: 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 popAddress(x: var Stack): EthAddress = result 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 gasCosts(c: Computation): array[Op,int] = result 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 + proc accountExists(c: Computation, address: EthAddress): bool = result + proc getBalance[T](c: Computation, address: T): Uint256 = result # function stubs from v2utils_numeric.nim - func cleanMemRef(x: UInt256): int = 0 + func cleanMemRef(x: UInt256): int = result # function stubs from v2memory.nim - proc len(mem: Memory): int = 0 + proc len(mem: Memory): int = result proc extend(mem: var Memory; startPos: Natural; size: Natural) = discard proc read(mem: var Memory, startPos: Natural, size: Natural): seq[byte] = @[] @@ -94,11 +89,11 @@ else: 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 + proc c_handler(x: int; y: Uint256, z: GasParams): GasResult = result + proc m_handler(x: int; curMemSize, memOffset, memLen: int64): int = result # function stubs from accounts_cache.nim: - func inAccessList[A,B](ac: A; address: B): bool = false + func inAccessList[A,B](ac: A; address: B): bool = result proc accessList[A,B](ac: var A; address: B) = discard # ------------------------------------------------------------------------------ diff --git a/nimbus/vm2/interpreter/opcodes_impl.nim b/nimbus/vm2/interpreter/opcodes_impl.nim index 5f62b9375..3eb1ff8b7 100644 --- a/nimbus/vm2/interpreter/opcodes_impl.nim +++ b/nimbus/vm2/interpreter/opcodes_impl.nim @@ -202,6 +202,8 @@ opHandler log1, Op.Log1 opHandler log2, Op.Log2 opHandler log3, Op.Log3 opHandler log4, Op.Log4 +opHandler create, Op.Create +opHandler create2, Op.Create2 opHandler returnOp, Op.Return opHandler revert, Op.Revert opHandler invalidOp, Op.Invalid @@ -213,76 +215,6 @@ opHandler selfDestructEIP2929, Op.SelfDestruct # ########################################## # f0s: System operations. -template genCreate(callName: untyped, opCode: Op): untyped = - op callName, inline = false: - checkInStaticContext(c) - let - endowment = c.stack.popInt() - memPos = c.stack.popInt().safeInt - - when opCode == Create: - const callKind = evmcCreate - let memLen {.inject.} = c.stack.peekInt().safeInt - let salt = 0.u256 - else: - const callKind = evmcCreate2 - let memLen {.inject.} = c.stack.popInt().safeInt - let salt = c.stack.peekInt() - - c.stack.top(0) - - let gasParams = GasParams(kind: Create, - cr_currentMemSize: c.memory.len, - cr_memOffset: memPos, - cr_memLength: memLen - ) - var gasCost = c.gasCosts[Create].c_handler(1.u256, gasParams).gasCost - when opCode == Create2: - gasCost = gasCost + c.gasCosts[Create2].m_handler(0, 0, memLen) - - let reason = &"CREATE: GasCreate + {memLen} * memory expansion" - c.gasMeter.consumeGas(gasCost, reason = reason) - c.memory.extend(memPos, memLen) - c.returnData.setLen(0) - - if c.msg.depth >= MaxCallDepth: - debug "Computation Failure", reason = "Stack too deep", maxDepth = MaxCallDepth, depth = c.msg.depth - return - - if endowment != 0: - let senderBalance = c.getBalance(c.msg.contractAddress) - if senderBalance < endowment: - debug "Computation Failure", reason = "Insufficient funds available to transfer", required = endowment, balance = senderBalance - return - - var createMsgGas = c.gasMeter.gasRemaining - if c.fork >= FkTangerine: - createMsgGas -= createMsgGas div 64 - c.gasMeter.consumeGas(createMsgGas, reason="CREATE") - - block: - let childMsg = Message( - kind: callKind, - depth: c.msg.depth + 1, - gas: createMsgGas, - sender: c.msg.contractAddress, - value: endowment, - data: c.memory.read(memPos, memLen) - ) - - var child = newComputation(c.vmState, childMsg, salt) - c.chainTo(child): - if not child.shouldBurnGas: - c.gasMeter.returnGas(child.gasMeter.gasRemaining) - - if child.isSuccess: - c.merge(child) - c.stack.top child.msg.contractAddress - else: - c.returnData = child.output - -genCreate(create, Create) -genCreate(create2, Create2) proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) = let gas = c.stack.popInt()