diff --git a/nimbus/vm2/compu_helper.nim b/nimbus/vm2/compu_helper.nim index 43875b42d..96274c15c 100644 --- a/nimbus/vm2/compu_helper.nim +++ b/nimbus/vm2/compu_helper.nim @@ -11,7 +11,12 @@ import ../constants, ../db/accounts_cache, - ./interpreter/op_codes, + ../utils, + ./code_stream, + ./interpreter/[forks_list, gas_meter, gas_costs, op_codes], + ./memory, + ./message, + ./stack, ./state, ./transaction_tracer, ./types, @@ -26,35 +31,62 @@ logScope: when defined(chronicles_log_level): import stew/byteutils +# ------------------------------------------------------------------------------ +# Helpers +# ------------------------------------------------------------------------------ + +proc generateContractAddress(c: Computation, salt: Uint256): EthAddress = + if c.msg.kind == evmcCreate: + let creationNonce = c.vmState.readOnlyStateDb().getNonce(c.msg.sender) + result = generateAddress(c.msg.sender, creationNonce) + else: + result = generateSafeAddress(c.msg.sender, salt, c.msg.data) + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +template getCoinbase*(c: Computation): EthAddress = + c.vmState.coinbase + +template getTimestamp*(c: Computation): int64 = + c.vmState.timestamp.toUnix + +template getBlockNumber*(c: Computation): Uint256 = + c.vmState.blockNumber.blockNumberToVmWord + +template getDifficulty*(c: Computation): DifficultyInt = + c.vmState.difficulty + +template getGasLimit*(c: Computation): GasInt = + c.vmState.gasLimit + +template getChainId*(c: Computation): uint = + c.vmState.chaindb.config.chainId.uint + +template getOrigin*(c: Computation): EthAddress = + c.vmState.txOrigin + +template getGasPrice*(c: Computation): GasInt = + c.vmState.txGasPrice + +template getBlockHash*(c: Computation, blockNumber: Uint256): Hash256 = + c.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber) + template accountExists*(c: Computation, address: EthAddress): bool = if c.fork >= FkSpurious: not c.vmState.readOnlyStateDB.isDeadAccount(address) else: c.vmState.readOnlyStateDB.accountExists(address) -proc addLogEntry*(c: Computation, log: Log) {.inline.} = - c.logEntries.add(log) - -template fork*(c: Computation): untyped = - c.vmState.fork - -template gasCosts*(c: Computation): untyped = - c.vmState.gasCosts +template getStorage*(c: Computation, slot: Uint256): Uint256 = + c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot) template getBalance*(c: Computation, address: EthAddress): Uint256 = c.vmState.readOnlyStateDB.getBalance(address) -template getBlockHash*(c: Computation, blockNumber: Uint256): Hash256 = - c.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber) - -template getBlockNumber*(c: Computation): Uint256 = - c.vmState.blockNumber.blockNumberToVmWord - -template getChainId*(c: Computation): uint = - c.vmState.chaindb.config.chainId.uint - -template getCode*(c: Computation, address: EthAddress): seq[byte] = - c.vmState.readOnlyStateDB.getCode(address) +template getCodeSize*(c: Computation, address: EthAddress): uint = + uint(c.vmState.readOnlyStateDB.getCodeSize(address)) template getCodeHash*(c: Computation, address: EthAddress): Hash256 = let @@ -64,34 +96,109 @@ template getCodeHash*(c: Computation, address: EthAddress): Hash256 = else: db.getCodeHash(address) -template getCodeSize*(c: Computation, address: EthAddress): uint = - uint(c.vmState.readOnlyStateDB.getCodeSize(address)) +template selfDestruct*(c: Computation, address: EthAddress) = + c.execSelfDestruct(address) -template getCoinbase*(c: Computation): EthAddress = - c.vmState.coinbase +template getCode*(c: Computation, address: EthAddress): seq[byte] = + c.vmState.readOnlyStateDB.getCode(address) -template getDifficulty*(c: Computation): DifficultyInt = - c.vmState.difficulty +proc newComputation*(vmState: BaseVMState, + message: Message, salt= 0.u256): Computation = + new result + result.vmState = vmState + result.msg = message + result.memory = Memory() + result.stack = newStack() + result.returnStack = @[] + result.gasMeter.init(message.gas) + result.touchedAccounts = initHashSet[EthAddress]() + result.suicides = initHashSet[EthAddress]() -template getGasLimit*(c: Computation): GasInt = - c.vmState.gasLimit + if result.msg.isCreate(): + result.msg.contractAddress = result.generateContractAddress(salt) + result.code = newCodeStream(message.data) + message.data = @[] + else: + result.code = newCodeStream( + vmState.readOnlyStateDb.getCode(message.codeAddress)) -template getGasPrice*(c: Computation): GasInt = - c.vmState.txGasPrice +template gasCosts*(c: Computation): untyped = + c.vmState.gasCosts -template getOrigin*(c: Computation): EthAddress = - c.vmState.txOrigin +template fork*(c: Computation): untyped = + c.vmState.fork -template getStorage*(c: Computation, slot: Uint256): Uint256 = - c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot) +proc isOriginComputation*(c: Computation): bool = + # Is this computation the computation initiated by a transaction + c.msg.sender == c.vmState.txOrigin -template getTimestamp*(c: Computation): int64 = - c.vmState.timestamp.toUnix +template isSuccess*(c: Computation): bool = + c.error.isNil -proc prepareTracer*(c: Computation) {.inline.} = - c.vmState.tracer.prepare(c.msg.depth) +template isError*(c: Computation): bool = + not c.isSuccess -proc selfDestruct*(c: Computation, beneficiary: EthAddress) = +func shouldBurnGas*(c: Computation): bool = + c.isError and c.error.burnsGas + +proc isSuicided*(c: Computation, address: EthAddress): bool = + result = address in c.suicides + +proc snapshot*(c: Computation) = + c.savePoint = c.vmState.accountDb.beginSavePoint() + +proc commit*(c: Computation) = + c.vmState.accountDb.commit(c.savePoint) + +proc dispose*(c: Computation) {.inline.} = + c.vmState.accountDb.safeDispose(c.savePoint) + c.savePoint = nil + +proc rollback*(c: Computation) = + c.vmState.accountDb.rollback(c.savePoint) + +proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} = + c.error = Error(info: msg, burnsGas: burnsGas) + +proc writeContract*(c: Computation, fork: Fork): bool {.gcsafe.} = + result = true + + let contractCode = c.output + if contractCode.len == 0: return + + if fork >= FkSpurious and contractCode.len >= EIP170_CODE_SIZE_LIMIT: + debug "Contract code size exceeds EIP170", + limit = EIP170_CODE_SIZE_LIMIT, + actual = contractCode.len + return false + + let storageAddr = c.msg.contractAddress + if c.isSuicided(storageAddr): return + + let gasParams = GasParams(kind: Create, cr_memLength: contractCode.len) + let codeCost = c.gasCosts[Create].c_handler(0.u256, gasParams).gasCost + if c.gasMeter.gasRemaining >= codeCost: + c.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE") + c.vmState.mutateStateDb: + db.setCode(storageAddr, contractCode) + result = true + else: + if fork < FkHomestead or FkByzantium <= fork: + c.output = @[] + result = false + +template chainTo*(c, toChild: Computation, after: untyped) = + c.child = toChild + c.continuation = proc() = + after + +proc merge*(c, child: Computation) = + c.logEntries.add child.logEntries + c.gasMeter.refundGas(child.gasMeter.gasRefunded) + c.suicides.incl child.suicides + c.touchedAccounts.incl child.touchedAccounts + +proc execSelfDestruct*(c: Computation, beneficiary: EthAddress) = c.vmState.mutateStateDB: let localBalance = c.getBalance(c.msg.contractAddress) @@ -114,8 +221,19 @@ proc selfDestruct*(c: Computation, beneficiary: EthAddress) = # Register the account to be deleted c.suicides.incl(c.msg.contractAddress) -proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} = - c.error = Error(info: msg, burnsGas: burnsGas) +proc addLogEntry*(c: Computation, log: Log) {.inline.} = + c.logEntries.add(log) + +proc getGasRefund*(c: Computation): GasInt = + if c.isSuccess: + result = c.gasMeter.gasRefunded + +proc refundSelfDestruct*(c: Computation) = + let cost = gasFees[c.fork][RefundSelfDestruct] + c.gasMeter.refundGas(cost * c.suicides.len) + +proc tracingEnabled*(c: Computation): bool {.inline.} = + TracerFlags.EnableTracing in c.vmState.tracer.flags proc traceOpCodeStarted*(c: Computation, op: Op): int {.inline.} = c.vmState.tracer.traceOpCodeStarted(c, op) @@ -123,10 +241,12 @@ proc traceOpCodeStarted*(c: Computation, op: Op): int {.inline.} = proc traceOpCodeEnded*(c: Computation, op: Op, lastIndex: int) {.inline.} = c.vmState.tracer.traceOpCodeEnded(c, op, lastIndex) -proc tracingEnabled*(c: Computation): bool {.inline.} = - TracerFlags.EnableTracing in c.vmState.tracer.flags +proc traceError*(c: Computation) {.inline.} = + c.vmState.tracer.traceError(c) +proc prepareTracer*(c: Computation) {.inline.} = + c.vmState.tracer.prepare(c.msg.depth) -# deprecated, related to nimvm/evmc implementation -proc execSelfDestruct*(c: Computation, address: EthAddress) {.deprecated.} = - c.selfDestruct(address) +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/vm2/computation.nim b/nimbus/vm2/computation.nim index 5ab8790e5..4bd56dc0d 100644 --- a/nimbus/vm2/computation.nim +++ b/nimbus/vm2/computation.nim @@ -13,94 +13,14 @@ import sets, eth/[common, keys], ../constants, ./compu_helper, - ./interpreter/[op_codes, gas_meter, gas_costs, forks_list], - ./code_stream, ./memory, ./message, ./stack, ./types, ./state, + ./interpreter/forks_list, + ./message, ./types, ./state, ../db/accounts_cache, - ./precompiles, - ./transaction_tracer, ../utils + ./precompiles logScope: topics = "vm computation" -proc generateContractAddress(c: Computation, salt: Uint256): EthAddress = - if c.msg.kind == evmcCreate: - let creationNonce = c.vmState.readOnlyStateDb().getNonce(c.msg.sender) - result = generateAddress(c.msg.sender, creationNonce) - else: - result = generateSafeAddress(c.msg.sender, salt, c.msg.data) - - -proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Computation = - new result - result.vmState = vmState - result.msg = message - result.memory = Memory() - result.stack = newStack() - result.returnStack = @[] - result.gasMeter.init(message.gas) - result.touchedAccounts = initHashSet[EthAddress]() - result.suicides = initHashSet[EthAddress]() - - if result.msg.isCreate(): - result.msg.contractAddress = result.generateContractAddress(salt) - result.code = newCodeStream(message.data) - message.data = @[] - else: - result.code = newCodeStream(vmState.readOnlyStateDb.getCode(message.codeAddress)) - -proc isOriginComputation*(c: Computation): bool = - # Is this computation the computation initiated by a transaction - c.msg.sender == c.vmState.txOrigin - -template isSuccess*(c: Computation): bool = - c.error.isNil - -template isError*(c: Computation): bool = - not c.isSuccess - -func shouldBurnGas*(c: Computation): bool = - c.isError and c.error.burnsGas - -proc isSuicided*(c: Computation, address: EthAddress): bool = - result = address in c.suicides - -proc snapshot*(c: Computation) = - c.savePoint = c.vmState.accountDb.beginSavePoint() - -proc commit*(c: Computation) = - c.vmState.accountDb.commit(c.savePoint) - -proc dispose*(c: Computation) {.inline.} = - c.vmState.accountDb.safeDispose(c.savePoint) - c.savePoint = nil - -proc rollback*(c: Computation) = - c.vmState.accountDb.rollback(c.savePoint) - -proc writeContract*(c: Computation, fork: Fork): bool {.gcsafe.} = - result = true - - let contractCode = c.output - if contractCode.len == 0: return - - if fork >= FkSpurious and contractCode.len >= EIP170_CODE_SIZE_LIMIT: - debug "Contract code size exceeds EIP170", limit=EIP170_CODE_SIZE_LIMIT, actual=contractCode.len - return false - - let storageAddr = c.msg.contractAddress - if c.isSuicided(storageAddr): return - - let gasParams = GasParams(kind: Create, cr_memLength: contractCode.len) - let codeCost = c.gasCosts[Create].c_handler(0.u256, gasParams).gasCost - if c.gasMeter.gasRemaining >= codeCost: - c.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE") - c.vmState.mutateStateDb: - db.setCode(storageAddr, contractCode) - result = true - else: - if fork < FkHomestead or fork >= FkByzantium: c.output = @[] - result = false - proc initAddress(x: int): EthAddress {.compileTime.} = result[19] = x.byte const ripemdAddr = initAddress(3) proc executeOpcodes*(c: Computation) {.gcsafe.} @@ -180,11 +100,6 @@ proc afterExec(c: Computation) = else: c.afterExecCreate() -template chainTo*(c, toChild: Computation, after: untyped) = - c.child = toChild - c.continuation = proc() = - after - proc execCallOrCreate*(cParam: Computation) = var (c, before) = (cParam, true) defer: @@ -208,24 +123,6 @@ proc execCallOrCreate*(cParam: Computation) = (before, c.parent, c) = (false, nil.Computation, c.parent) (c.continuation)() -proc merge*(c, child: Computation) = - c.logEntries.add child.logEntries - c.gasMeter.refundGas(child.gasMeter.gasRefunded) - c.suicides.incl child.suicides - c.touchedAccounts.incl child.touchedAccounts - -proc getGasRefund*(c: Computation): GasInt = - if c.isSuccess: - result = c.gasMeter.gasRefunded - -proc refundSelfDestruct*(c: Computation) = - let cost = gasFees[c.fork][RefundSelfDestruct] - c.gasMeter.refundGas(cost * c.suicides.len) - - -proc traceError*(c: Computation) {.inline.} = - c.vmState.tracer.traceError(c) - import interpreter_dispatch diff --git a/nimbus/vm2/interpreter.nim b/nimbus/vm2/interpreter.nim index f94621f94..171a77cc0 100644 --- a/nimbus/vm2/interpreter.nim +++ b/nimbus/vm2/interpreter.nim @@ -35,6 +35,9 @@ import export xmc.accountExists, xmc.addLogEntry, + xmc.chainTo, + xmc.commit, + xmc.dispose, xmc.fork, xmc.getBalance, xmc.getBlockHash, @@ -47,37 +50,35 @@ export xmc.getDifficulty, xmc.getGasLimit, xmc.getGasPrice, + xmc.getGasRefund, xmc.getOrigin, xmc.getStorage, xmc.getTimestamp, + xmc.isError, + xmc.isOriginComputation, + xmc.isSuccess, + xmc.isSuicided, + xmc.merge, + xmc.newComputation, xmc.prepareTracer, + xmc.refundSelfDestruct, + xmc.rollback, xmc.selfDestruct, xmc.setError, + xmc.shouldBurnGas, + xmc.snapshot, + xmc.traceError, xmc.traceOpCodeEnded, xmc.traceOpCodeStarted, - xmc.tracingEnabled + xmc.tracingEnabled, + xmc.writeContract + import ./computation as vmc export - vmc.commit, - vmc.dispose, vmc.execCallOrCreate, - vmc.chainTo, - vmc.executeOpcodes, - vmc.getGasRefund, - vmc.isError, - vmc.isOriginComputation, - vmc.isSuccess, - vmc.isSuicided, - vmc.merge, - vmc.newComputation, - vmc.refundSelfDestruct, - vmc.rollback, - vmc.shouldBurnGas, - vmc.snapshot, - vmc.traceError, - vmc.writeContract + vmc.executeOpcodes import diff --git a/nimbus/vm_computation.nim b/nimbus/vm_computation.nim index 9e964daa8..16f257d62 100644 --- a/nimbus/vm_computation.nim +++ b/nimbus/vm_computation.nim @@ -18,6 +18,9 @@ when defined(evmc_enabled) or not defined(vm2_enabled): export vmc.accountExists, vmc.addLogEntry, + vmc.chainTo, + vmc.commit, + vmc.dispose, vmc.execSelfDestruct, vmc.fork, vmc.getBalance, @@ -31,15 +34,28 @@ when defined(evmc_enabled) or not defined(vm2_enabled): vmc.getDifficulty, vmc.getGasLimit, vmc.getGasPrice, + vmc.getGasRefund, vmc.getOrigin, vmc.getStorage, vmc.getTimestamp, + vmc.isError, + vmc.isOriginComputation, + vmc.isSuccess, + vmc.isSuicided, + vmc.merge, + vmc.newComputation, + vmc.prepareTracer, + vmc.refundSelfDestruct, + vmc.rollback, vmc.selfDestruct, vmc.setError, - vmc.prepareTracer, + vmc.shouldBurnGas, + vmc.snapshot, + vmc.traceError, vmc.traceOpCodeEnded, vmc.traceOpCodeStarted, - vmc.tracingEnabled + vmc.tracingEnabled, + vmc.writeContract else: import @@ -49,6 +65,9 @@ else: export xmc.accountExists, xmc.addLogEntry, + xmc.chainTo, + xmc.commit, + xmc.dispose, xmc.execSelfDestruct, xmc.fork, xmc.getBalance, @@ -62,34 +81,31 @@ else: xmc.getDifficulty, xmc.getGasLimit, xmc.getGasPrice, + xmc.getGasRefund, xmc.getOrigin, xmc.getStorage, xmc.getTimestamp, + xmc.isError, + xmc.isOriginComputation, + xmc.isSuccess, + xmc.isSuicided, + xmc.merge, + xmc.newComputation, xmc.prepareTracer, + xmc.refundSelfDestruct, + xmc.rollback, xmc.selfDestruct, xmc.setError, + xmc.shouldBurnGas, + xmc.snapshot, + xmc.traceError, xmc.traceOpCodeEnded, xmc.traceOpCodeStarted, - xmc.tracingEnabled + xmc.tracingEnabled, + xmc.writeContract export - vmc.commit, - vmc.dispose, vmc.execCallOrCreate, - vmc.chainTo, - vmc.executeOpcodes, - vmc.getGasRefund, - vmc.isError, - vmc.isOriginComputation, - vmc.isSuccess, - vmc.isSuicided, - vmc.merge, - vmc.newComputation, - vmc.refundSelfDestruct, - vmc.rollback, - vmc.shouldBurnGas, - vmc.snapshot, - vmc.traceError, - vmc.writeContract + vmc.executeOpcodes # End