From 1bb6ef43a1a25357cf714455ce84da31287dc543 Mon Sep 17 00:00:00 2001 From: Jordan Hrycaj Date: Thu, 15 Apr 2021 18:42:43 +0100 Subject: [PATCH] re-integrated handlers with op codes 0xf2/return ..0xff/selfdestruct --- nimbus/vm2/interpreter/op_handlers.nim | 4 +- .../vm2/interpreter/op_handlers/oph_defs.nim | 1 + .../interpreter/op_handlers/oph_helpers.nim | 1 - .../interpreter/op_handlers/oph_memory.nim | 4 +- .../interpreter/op_handlers/oph_sysops.nim | 244 +++++++++++++++++- nimbus/vm2/interpreter/opcodes_impl.nim | 137 +--------- 6 files changed, 251 insertions(+), 140 deletions(-) diff --git a/nimbus/vm2/interpreter/op_handlers.nim b/nimbus/vm2/interpreter/op_handlers.nim index 9be5f5f4e..61663bbd3 100644 --- a/nimbus/vm2/interpreter/op_handlers.nim +++ b/nimbus/vm2/interpreter/op_handlers.nim @@ -51,7 +51,9 @@ 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, vm2OpExecSysOP, "SysOp") + #result.importList(select, vm2OpExecCreate, "Create") + #result.importList(select, vm2OpExecCall, "Call") + result.importList(select, vm2OpExecSysOp, "SysOp") for op in Op: if select notin result[op].forks: diff --git a/nimbus/vm2/interpreter/op_handlers/oph_defs.nim b/nimbus/vm2/interpreter/op_handlers/oph_defs.nim index e47599591..14adcbf3b 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_defs.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_defs.nim @@ -59,6 +59,7 @@ else: Computation* = ref object returnStack*: seq[int] + output*: seq[byte] vmState*: BaseVMState gasMeter*: GasMeter stack*: Stack diff --git a/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim b/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim index eb0f93777..1b1b3dc5e 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_helpers.nim @@ -47,7 +47,6 @@ else: emvcStatic = 1 ColdAccountAccessCost = 2 WarmStorageReadCost = 3 - type GasInt = int diff --git a/nimbus/vm2/interpreter/op_handlers/oph_memory.nim b/nimbus/vm2/interpreter/op_handlers/oph_memory.nim index a8ed8e94e..f4c16cf1e 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_memory.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_memory.nim @@ -47,8 +47,8 @@ else: const ColdSloadCost = 42 WarmStorageReadCost = 43 - - var blindGasCosts: array[Op,int] + var + blindGasCosts: array[Op,int] # copied from stack.nim macro genTupleType(len: static[int], elemType: untyped): untyped = diff --git a/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim b/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim index bf3514cbe..379acf079 100644 --- a/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim +++ b/nimbus/vm2/interpreter/op_handlers/oph_sysops.nim @@ -12,32 +12,260 @@ ## ====================================== ## +const + kludge {.intdefine.}: int = 0 + breakCircularDependency {.used.} = kludge > 0 + import - ../op_codes, ./oph_defs, ../../../errors.nim + ../../../errors, + ./oph_defs, + ./oph_helpers, + stint + +# ------------------------------------------------------------------------------ +# Kludge BEGIN +# ------------------------------------------------------------------------------ + +when not breakCircularDependency: + import + ../../../db/accounts_cache, + ../../stack, + ../../v2computation, + ../../v2memory, + ../../v2state, + ../../v2types, + ../gas_meter, + ../utils/v2utils_numeric, + ../v2gas_costs, + eth/common + +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 = + result = nnkTupleConstr.newNimNode() + 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 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 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 + + # function stubs from v2utils_numeric.nim + func cleanMemRef(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 v2state.nim + template mutateStateDB(vmState: BaseVMState, body: untyped) = + block: + var db {.inject.} = vmState.accountDb + body + + # function stubs from gas_meter.nim + proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard + + # stubs from v2gas_costs.nim + type GasParams = object + case kind*: Op + of SelfDestruct: + 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 + + # 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 + +# ------------------------------------------------------------------------------ +# Kludge END +# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Private # ------------------------------------------------------------------------------ const - invalidOp: Vm2OpFn = - proc(k: Vm2Ctx) {.gcsafe.} = - raise newException(InvalidInstruction, - "Invalid instruction, received an opcode " & - "not implemented in the current fork.") + returnOp: Vm2OpFn = proc(k: Vm2Ctx) = + ## 0xf3, Halt execution returning output data. + let (startPos, size) = k.cpt.stack.popInt(2) + + let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) + k.cpt.gasMeter.consumeGas( + k.cpt.gasCosts[Return].m_handler(k.cpt.memory.len, pos, len), + reason = "RETURN") + k.cpt.memory.extend(pos, len) + k.cpt.output = k.cpt.memory.read(pos, len) + + + revertOp: Vm2OpFn = proc(k: Vm2Ctx) = + ## 0xfd, Halt execution reverting state changes but returning data + ## and remaining gas. + let (startPos, size) = k.cpt.stack.popInt(2) + + let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) + k.cpt.gasMeter.consumeGas( + k.cpt.gasCosts[Revert].m_handler(k.cpt.memory.len, pos, len), + reason = "REVERT") + + k.cpt.memory.extend(pos, len) + k.cpt.output = k.cpt.memory.read(pos, len) + # setError(msg, false) will signal cheap revert + k.cpt.setError("REVERT opcode executed", false) + + + invalidOp: Vm2OpFn = proc(k: Vm2Ctx) = + raise newException(InvalidInstruction, + "Invalid instruction, received an opcode " & + "not implemented in the current fork.") + + # ----------- + + selfDestructOp: Vm2OpFn = proc(k: Vm2Ctx) = + ## 0xff, Halt execution and register account for later deletion. + let beneficiary = k.cpt.stack.popAddress() + k.cpt.selfDestruct(beneficiary) + + + selfDestructEIP150Op: Vm2OpFn = proc(k: Vm2Ctx) = + ## selfDestructEip150 (auto generated comment) + let beneficiary = k.cpt.stack.popAddress() + + let gasParams = GasParams( + kind: SelfDestruct, + sd_condition: not k.cpt.accountExists(beneficiary)) + + let gasCost = + k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost + k.cpt.gasMeter.consumeGas( + gasCost, reason = "SELFDESTRUCT EIP150") + k.cpt.selfDestruct(beneficiary) + + + selfDestructEip161Op: Vm2OpFn = proc(k: Vm2Ctx) = + ## selfDestructEip161 (auto generated comment) + checkInStaticContext(k.cpt) + + let + beneficiary = k.cpt.stack.popAddress() + isDead = not k.cpt.accountExists(beneficiary) + balance = k.cpt.getBalance(k.cpt.msg.contractAddress) + + let gasParams = GasParams( + kind: SelfDestruct, + sd_condition: isDead and not balance.isZero) + + let gasCost = + k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost + k.cpt.gasMeter.consumeGas( + gasCost, reason = "SELFDESTRUCT EIP161") + k.cpt.selfDestruct(beneficiary) + + + selfDestructEIP2929Op: Vm2OpFn = proc(k: Vm2Ctx) = + ## selfDestructEIP2929 (auto generated comment) + checkInStaticContext(k.cpt) + + let + beneficiary = k.cpt.stack.popAddress() + isDead = not k.cpt.accountExists(beneficiary) + balance = k.cpt.getBalance(k.cpt.msg.contractAddress) + + let gasParams = GasParams( + kind: SelfDestruct, + sd_condition: isDead and not balance.isZero) + + var gasCost = + k.cpt.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost + + k.cpt.vmState.mutateStateDB: + if not db.inAccessList(beneficiary): + db.accessList(beneficiary) + gasCost = gasCost + ColdAccountAccessCost + + k.cpt.gasMeter.consumeGas( + gasCost, reason = "SELFDESTRUCT EIP161") + k.cpt.selfDestruct(beneficiary) # ------------------------------------------------------------------------------ # Public, op exec table entries # ------------------------------------------------------------------------------ const - vm2OpExecSysOP*: seq[Vm2OpExec] = @[ + vm2OpExecSysOp*: seq[Vm2OpExec] = @[ - (opCode: Invalid, ## 0xfe, invalid instruction. + (opCode: Return, ## 0xf3, Halt execution returning output data. + forks: Vm2OpAllForks, + info: "Halt execution returning output data", + exec: (prep: vm2OpIgnore, + run: returnOp, + post: vm2OpIgnore)), + + (opCode: Revert, ## 0xfd, Halt and revert state changes + forks: Vm2OpAllForks, + info: "Halt execution reverting state changes but returning data " & + "and remaining gas", + exec: (prep: vm2OpIgnore, + run: revertOp, + post: vm2OpIgnore)), + + (opCode: Invalid, ## 0xfe, invalid instruction. forks: Vm2OpAllForks, info: "Designated invalid instruction", exec: (prep: vm2OpIgnore, run: invalidOp, + post: vm2OpIgnore)), + + (opCode: SelfDestruct, ## 0xff, Halt execution, prep for later deletion + forks: Vm2OpAllForks - Vm2OpTangerineAndLater, + info: "Halt execution and register account for later deletion", + exec: (prep: vm2OpIgnore, + run: selfDestructOp, + post: vm2OpIgnore)), + + (opCode: SelfDestruct, ## 0xff, EIP150: self destruct, Tangerine + forks: Vm2OpTangerineAndLater - Vm2OpSpuriousAndLater, + info: "EIP150: Halt execution and register account for later deletion", + exec: (prep: vm2OpIgnore, + run: selfDestructEIP150Op, + post: vm2OpIgnore)), + + (opCode: SelfDestruct, ## 0xff, EIP161: self destruct, Spurious and later + forks: Vm2OpSpuriousAndLater - Vm2OpBerlinAndLater, + info: "EIP161: Halt execution and register account for later deletion", + exec: (prep: vm2OpIgnore, + run: selfDestructEIP161Op, + post: vm2OpIgnore)), + + (opCode: SelfDestruct, ## 0xff, EIP2929: self destruct, Berlin and later + forks: Vm2OpBerlinAndLater, + info: "EIP2929: Halt execution and register account for later deletion", + exec: (prep: vm2OpIgnore, + run: selfDestructEIP2929Op, post: vm2OpIgnore))] # ------------------------------------------------------------------------------ diff --git a/nimbus/vm2/interpreter/opcodes_impl.nim b/nimbus/vm2/interpreter/opcodes_impl.nim index 4dcd4e9c4..5f62b9375 100644 --- a/nimbus/vm2/interpreter/opcodes_impl.nim +++ b/nimbus/vm2/interpreter/opcodes_impl.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - strformat, times, sets, sequtils, options, + strformat, times, sets, options, chronicles, stint, nimcrypto, eth/common, ./utils/[macros_procs_opcodes, v2utils_numeric], ./gas_meter, ./v2gas_costs, ./v2opcode_values, ./v2forks, @@ -24,56 +24,10 @@ logScope: # ################################## # Syntactic sugar -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 push(x: typed) {.dirty.} = ## Push an expression on the computation stack c.stack.push x -proc writePaddedResult(mem: var Memory, - data: openarray[byte], - memPos, dataPos, len: Natural, - paddingValue = 0.byte) = - - mem.extend(memPos, len) - let dataEndPosition = dataPos.int64 + len - 1 - let sourceBytes = data[min(dataPos, data.len) .. min(data.len - 1, dataEndPosition)] - mem.write(memPos, sourceBytes) - - # Don't duplicate zero-padding of mem.extend - let paddingOffset = min(memPos + sourceBytes.len, mem.len) - let numPaddingBytes = min(mem.len - paddingOffset, len - sourceBytes.len) - if numPaddingBytes > 0: - # TODO: avoid unnecessary memory allocation - mem.write(paddingOffset, repeat(paddingValue, numPaddingBytes)) - -template sstoreNetGasMeteringImpl(c: Computation, slot, newValue: Uint256) = - let stateDB = c.vmState.readOnlyStateDB - let currentValue {.inject.} = c.getStorage(slot) - - let - gasParam = GasParams( - kind: Op.Sstore, - s_currentValue: currentValue, - s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot)) - (gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(newValue, gasParam) - - c.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}] -> {newValue} ({currentValue})") - - if gasRefund != 0: - c.gasMeter.refundGas(gasRefund) - - c.vmState.mutateStateDB: - db.setStorage(c.msg.contractAddress, slot, newValue) - # ################################## # re-implemented OP handlers @@ -248,6 +202,14 @@ opHandler log1, Op.Log1 opHandler log2, Op.Log2 opHandler log3, Op.Log3 opHandler log4, Op.Log4 +opHandler returnOp, Op.Return +opHandler revert, Op.Revert +opHandler invalidOp, Op.Invalid + +opHandler selfDestruct, Op.SelfDestruct, FkFrontier +opHandler selfDestructEIP150, Op.SelfDestruct, FkTangerine +opHandler selfDestructEIP161, Op.SelfDestruct, FkSpurious +opHandler selfDestructEIP2929, Op.SelfDestruct # ########################################## # f0s: System operations. @@ -490,84 +452,3 @@ genCall(call, Call) genCall(callCode, CallCode) genCall(delegateCall, DelegateCall) genCall(staticCall, StaticCall) - -op returnOp, inline = false, startPos, size: - ## 0xf3, Halt execution returning output data. - let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) - - c.gasMeter.consumeGas( - c.gasCosts[Return].m_handler(c.memory.len, pos, len), - reason = "RETURN" - ) - - c.memory.extend(pos, len) - c.output = c.memory.read(pos, len) - -op revert, inline = false, startPos, size: - ## 0xfd, Halt execution reverting state changes but returning data and remaining gas. - let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) - c.gasMeter.consumeGas( - c.gasCosts[Revert].m_handler(c.memory.len, pos, len), - reason = "REVERT" - ) - - c.memory.extend(pos, len) - c.output = c.memory.read(pos, len) - # setError(msg, false) will signal cheap revert - c.setError("REVERT opcode executed", false) - -op selfDestruct, inline = false: - ## 0xff Halt execution and register account for later deletion. - let beneficiary = c.stack.popAddress() - c.selfDestruct(beneficiary) - -op selfDestructEip150, inline = false: - let beneficiary = c.stack.popAddress() - - let gasParams = GasParams(kind: SelfDestruct, - sd_condition: not c.accountExists(beneficiary) - ) - - let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost - c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP150") - c.selfDestruct(beneficiary) - -op selfDestructEip161, inline = false: - checkInStaticContext(c) - - let - beneficiary = c.stack.popAddress() - isDead = not c.accountExists(beneficiary) - balance = c.getBalance(c.msg.contractAddress) - - let gasParams = GasParams(kind: SelfDestruct, - sd_condition: isDead and not balance.isZero - ) - - let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost - c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161") - c.selfDestruct(beneficiary) - -# Constantinople's new opcodes - -op selfDestructEIP2929, inline = false: - checkInStaticContext(c) - - let - beneficiary = c.stack.popAddress() - isDead = not c.accountExists(beneficiary) - balance = c.getBalance(c.msg.contractAddress) - - let gasParams = GasParams(kind: SelfDestruct, - sd_condition: isDead and not balance.isZero - ) - - var gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost - - c.vmState.mutateStateDB: - if not db.inAccessList(beneficiary): - db.accessList(beneficiary) - gasCost = gasCost + ColdAccountAccessCost - - c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161") - c.selfDestruct(beneficiary)