From 26620eb67286190eb6308b04f89765fe2b7aac44 Mon Sep 17 00:00:00 2001 From: jangko Date: Mon, 28 Aug 2023 19:10:31 +0700 Subject: [PATCH] EVM embrace more EVMC types Also embed evmc_status_code to computation.error, and make the tracer produce cleaner output. No more "Revert opcode executed" error message. We can distinguish error code between REVERT and FAILURE in a more cleaner way. --- nimbus/evm/computation.nim | 34 +++- nimbus/evm/evmc_api.nim | 2 +- .../evm/interpreter/op_handlers/oph_call.nim | 28 +-- .../interpreter/op_handlers/oph_create.nim | 8 +- .../interpreter/op_handlers/oph_helpers.nim | 3 +- .../interpreter/op_handlers/oph_memory.nim | 2 +- .../interpreter/op_handlers/oph_sysops.nim | 2 +- nimbus/evm/interpreter_dispatch.nim | 13 +- nimbus/evm/message.nim | 2 +- nimbus/evm/precompiles.nim | 167 +++++++++--------- nimbus/evm/state.nim | 12 -- nimbus/evm/tracer/legacy_tracer.nim | 10 -- nimbus/evm/types.nim | 61 +++---- nimbus/transaction/call_common.nim | 13 +- nimbus/transaction/evmc_host_glue.nim | 4 +- nimbus/transaction/evmc_vm_glue.nim | 6 +- nimbus/transaction/host_call_nested.nim | 6 +- nimbus/transaction/host_services.nim | 2 +- nimbus/transaction/host_types.nim | 3 - nimbus/vm_computation.nim | 7 +- 20 files changed, 182 insertions(+), 203 deletions(-) diff --git a/nimbus/evm/computation.nim b/nimbus/evm/computation.nim index 8500f1ba4..242147331 100644 --- a/nimbus/evm/computation.nim +++ b/nimbus/evm/computation.nim @@ -51,7 +51,7 @@ const # ------------------------------------------------------------------------------ proc generateContractAddress(c: Computation, salt: ContractSalt): EthAddress = - if c.msg.kind == evmcCreate: + if c.msg.kind == EVMC_CREATE: let creationNonce = c.vmState.readOnlyStateDB().getNonce(c.msg.sender) result = generateAddress(c.msg.sender, creationNonce) else: @@ -273,7 +273,26 @@ proc rollback*(c: Computation) = c.vmState.stateDB.rollback(c.savePoint) proc setError*(c: Computation, msg: string, burnsGas = false) = - c.error = Error(info: msg, burnsGas: burnsGas) + c.error = Error(statusCode: EVMC_FAILURE, info: msg, burnsGas: burnsGas) + +proc setError*(c: Computation, code: evmc_status_code, burnsGas = false) = + c.error = Error(statusCode: code, info: $code, burnsGas: burnsGas) + +proc setError*(c: Computation, code: evmc_status_code, msg: string, burnsGas = false) = + c.error = Error(statusCode: code, info: msg, burnsGas: burnsGas) + +func statusCode*(c: Computation): evmc_status_code = + if c.isSuccess: + EVMC_SUCCESS + else: + c.error.statusCode + +func errorOpt*(c: Computation): Option[string] = + if c.isSuccess: + return none(string) + if c.error.statusCode == EVMC_REVERT: + return none(string) + some(c.error.info) proc writeContract*(c: Computation) {.gcsafe, raises: [CatchableError].} = @@ -292,16 +311,14 @@ proc writeContract*(c: Computation) # EIP-3541 constraint (https://eips.ethereum.org/EIPS/eip-3541). if fork >= FkLondon and c.output[0] == 0xEF.byte: withExtra trace, "New contract code starts with 0xEF byte, not allowed by EIP-3541" - # TODO: Return `EVMC_CONTRACT_VALIDATION_FAILURE` (like Silkworm). - c.setError("EVMC_CONTRACT_VALIDATION_FAILURE", true) + c.setError(EVMC_CONTRACT_VALIDATION_FAILURE, true) return # EIP-170 constraint (https://eips.ethereum.org/EIPS/eip-3541). if fork >= FkSpurious and len > EIP170_MAX_CODE_SIZE: withExtra trace, "New contract code exceeds EIP-170 limit", codeSize=len, maxSize=EIP170_MAX_CODE_SIZE - # TODO: Return `EVMC_OUT_OF_GAS` (like Silkworm). - c.setError("EVMC_OUT_OF_GAS", true) + c.setError(EVMC_OUT_OF_GAS, true) return # Charge gas and write the code even if the code address is self-destructed. @@ -322,8 +339,7 @@ proc writeContract*(c: Computation) if fork >= FkHomestead: # EIP-2 (https://eips.ethereum.org/EIPS/eip-2). - # TODO: Return `EVMC_OUT_OF_GAS` (like Silkworm). - c.setError("EVMC_OUT_OF_GAS", true) + c.setError(EVMC_OUT_OF_GAS, true) else: # Before EIP-2, when out of gas for code storage, the account ends up with # zero-length code and no error. No gas is charged. Code cited in EIP-2: @@ -416,7 +432,7 @@ proc traceError*(c: Computation) {.gcsafe, raises: [].} = c.gasMeter.gasRefunded, c.returnData, c.msg.depth + 1, - some(c.error.info)) + c.errorOpt) proc prepareTracer*(c: Computation) = c.vmState.capturePrepare(c, c.msg.depth) diff --git a/nimbus/evm/evmc_api.nim b/nimbus/evm/evmc_api.nim index 7e583ef3b..a3ce55297 100644 --- a/nimbus/evm/evmc_api.nim +++ b/nimbus/evm/evmc_api.nim @@ -33,7 +33,7 @@ type nimbus_message* = object kind* : evmc_call_kind - flags* : uint32 + flags* : evmc_flags depth* : int32 gas* : int64 recipient* : EthAddress diff --git a/nimbus/evm/interpreter/op_handlers/oph_call.nim b/nimbus/evm/interpreter/op_handlers/oph_call.nim index 0d93a8086..517019f05 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_call.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_call.nim @@ -142,7 +142,7 @@ proc staticCallParams(c: Computation): LocalParams = result.value = 0.u256 result.sender = c.msg.contractAddress - result.flags = emvcStatic + result.flags.incl EVMC_STATIC result.contractAddress = result.codeAddress result.updateStackAndParams(c) @@ -196,7 +196,7 @@ const ## 0xf1, Message-Call into an account let cpt = k.cpt - if emvcStatic == cpt.msg.flags and cpt.stack[^3, UInt256] > 0.u256: + if EVMC_STATIC in cpt.msg.flags and cpt.stack[^3, UInt256] > 0.u256: raise newException( StaticContextError, "Cannot modify state while inside of a STATICCALL context") @@ -248,7 +248,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind : evmcCall.ord.evmc_call_kind, + kind : EVMC_CALL, depth : (cpt.msg.depth + 1).int32, gas : childGasLimit, sender : p.sender, @@ -257,7 +257,7 @@ const input_data : cpt.memory.readPtr(p.memInPos), input_size : p.memInLen.uint, value : toEvmc(p.value), - flags : p.flags.uint32 + flags : p.flags ) c.execSubCall(msg, p) else: @@ -265,7 +265,7 @@ const memPos = p.memOutPos, memLen = p.memOutLen, childMsg = Message( - kind: evmcCall, + kind: EVMC_CALL, depth: cpt.msg.depth + 1, gas: childGasLimit, sender: p.sender, @@ -327,7 +327,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind : evmcCallCode.ord.evmc_call_kind, + kind : EVMC_CALLCODE, depth : (cpt.msg.depth + 1).int32, gas : childGasLimit, sender : p.sender, @@ -336,7 +336,7 @@ const input_data : cpt.memory.readPtr(p.memInPos), input_size : p.memInLen.uint, value : toEvmc(p.value), - flags : p.flags.uint32 + flags : p.flags ) c.execSubCall(msg, p) else: @@ -344,7 +344,7 @@ const memPos = p.memOutPos, memLen = p.memOutLen, childMsg = Message( - kind: evmcCallCode, + kind: EVMC_CALLCODE, depth: cpt.msg.depth + 1, gas: childGasLimit, sender: p.sender, @@ -401,7 +401,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind : evmcDelegateCall.ord.evmc_call_kind, + kind : EVMC_DELEGATECALL, depth : (cpt.msg.depth + 1).int32, gas : childGasLimit, sender : p.sender, @@ -410,7 +410,7 @@ const input_data : cpt.memory.readPtr(p.memInPos), input_size : p.memInLen.uint, value : toEvmc(p.value), - flags : p.flags.uint32 + flags : p.flags ) c.execSubCall(msg, p) else: @@ -418,7 +418,7 @@ const memPos = p.memOutPos, memLen = p.memOutLen, childMsg = Message( - kind: evmcDelegateCall, + kind: EVMC_DELEGATECALL, depth: cpt.msg.depth + 1, gas: childGasLimit, sender: p.sender, @@ -476,7 +476,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind : evmcCall.ord.evmc_call_kind, + kind : EVMC_CALL, depth : (cpt.msg.depth + 1).int32, gas : childGasLimit, sender : p.sender, @@ -485,7 +485,7 @@ const input_data : cpt.memory.readPtr(p.memInPos), input_size : p.memInLen.uint, value : toEvmc(p.value), - flags : p.flags.uint32 + flags : p.flags ) c.execSubCall(msg, p) else: @@ -493,7 +493,7 @@ const memPos = p.memOutPos, memLen = p.memOutLen, childMsg = Message( - kind: evmcCall, + kind: EVMC_CALL, depth: cpt.msg.depth + 1, gas: childGasLimit, sender: p.sender, diff --git a/nimbus/evm/interpreter/op_handlers/oph_create.nim b/nimbus/evm/interpreter/op_handlers/oph_create.nim index b300cc7ba..a7749abc9 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_create.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_create.nim @@ -140,7 +140,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind: evmcCreate.ord.evmc_call_kind, + kind: EVMC_CREATE, depth: (cpt.msg.depth + 1).int32, gas: createMsgGas, sender: cpt.msg.contractAddress, @@ -153,7 +153,7 @@ const else: cpt.execSubCreate( childMsg = Message( - kind: evmcCreate, + kind: EVMC_CREATE, depth: cpt.msg.depth + 1, gas: createMsgGas, sender: cpt.msg.contractAddress, @@ -222,7 +222,7 @@ const msg = new(nimbus_message) c = cpt msg[] = nimbus_message( - kind: evmcCreate2.ord.evmc_call_kind, + kind: EVMC_CREATE2, depth: (cpt.msg.depth + 1).int32, gas: createMsgGas, sender: cpt.msg.contractAddress, @@ -236,7 +236,7 @@ const cpt.execSubCreate( salt = salt, childMsg = Message( - kind: evmcCreate2, + kind: EVMC_CREATE2, depth: cpt.msg.depth + 1, gas: createMsgGas, sender: cpt.msg.contractAddress, diff --git a/nimbus/evm/interpreter/op_handlers/oph_helpers.nim b/nimbus/evm/interpreter/op_handlers/oph_helpers.nim index e2e56df0f..a65b4b4c5 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_helpers.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_helpers.nim @@ -20,7 +20,6 @@ import ../gas_costs, eth/common, eth/common/eth_types, - macros, stint when defined(evmc_enabled): @@ -64,7 +63,7 @@ proc gasEip2929AccountCheck*(c: Computation; address: EthAddress, slot: UInt256) template checkInStaticContext*(c: Computation) = ## Verify static context in handler function, raise an error otherwise - if emvcStatic == c.msg.flags: + if EVMC_STATIC in c.msg.flags: # TODO: if possible, this check only appear # when fork >= FkByzantium raise newException( diff --git a/nimbus/evm/interpreter/op_handlers/oph_memory.nim b/nimbus/evm/interpreter/op_handlers/oph_memory.nim index dc2806502..ef42d6a37 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_memory.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_memory.nim @@ -21,7 +21,6 @@ import ../../stack, ../../types, ../gas_costs, - ../gas_meter, ../op_codes, ../utils/utils_numeric, ./oph_defs, @@ -33,6 +32,7 @@ import when not defined(evmc_enabled): import + ../gas_meter, ../../state, ../../../db/accounts_cache diff --git a/nimbus/evm/interpreter/op_handlers/oph_sysops.nim b/nimbus/evm/interpreter/op_handlers/oph_sysops.nim index 68125dbe7..9870bc9a4 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_sysops.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_sysops.nim @@ -64,7 +64,7 @@ const 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) + k.cpt.setError(EVMC_REVERT, "REVERT opcode executed", false) invalidOp: Vm2OpFn = proc(k: var Vm2Ctx) = diff --git a/nimbus/evm/interpreter_dispatch.nim b/nimbus/evm/interpreter_dispatch.nim index 3324e6339..82d8cef6c 100644 --- a/nimbus/evm/interpreter_dispatch.nim +++ b/nimbus/evm/interpreter_dispatch.nim @@ -102,7 +102,7 @@ proc selectVM(c: Computation, fork: EVMFork, shouldPrepareTracer: bool) proc beforeExecCall(c: Computation) = c.snapshot() - if c.msg.kind == evmcCall: + if c.msg.kind == EVMC_CALL: c.vmState.mutateStateDB: db.subBalance(c.msg.sender, c.msg.value) db.addBalance(c.msg.contractAddress, c.msg.value) @@ -182,7 +182,7 @@ const ] func msgToOp(msg: Message): Op = - if emvcStatic == msg.flags: + if EVMC_STATIC in msg.flags: return STATICCALL MsgKindToOp[msg.kind] @@ -212,18 +212,14 @@ proc afterExec(c: Computation) if c.msg.depth > 0: let gasUsed = c.msg.gas - c.gasMeter.gasRemaining - let error = if c.isError: - some(c.error.info) - else: - none(string) - c.vmState.captureExit(c, c.output, gasUsed, error) + c.vmState.captureExit(c, c.output, gasUsed, c.errorOpt) # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true) - {.gcsafe, raises: [CatchableError].} = + {.gcsafe, raises: [].} = let fork = c.fork block: @@ -263,7 +259,6 @@ proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true) if c.isError() and c.continuation.isNil: if c.tracingEnabled: c.traceError() - #trace "executeOpcodes error", msg=c.error.info when vm_use_recursion: # Recursion with tiny stack frame per level. diff --git a/nimbus/evm/message.nim b/nimbus/evm/message.nim index 4e5464038..7d9cc0a2b 100644 --- a/nimbus/evm/message.nim +++ b/nimbus/evm/message.nim @@ -11,4 +11,4 @@ import ./types proc isCreate*(message: Message): bool = - message.kind in {evmcCreate, evmcCreate2} + message.kind in {EVMC_CREATE, EVMC_CREATE2} diff --git a/nimbus/evm/precompiles.nim b/nimbus/evm/precompiles.nim index 817baabad..bdc662980 100644 --- a/nimbus/evm/precompiles.nim +++ b/nimbus/evm/precompiles.nim @@ -17,7 +17,8 @@ import nimcrypto/[ripemd, sha2, utils], bncurve/[fields, groups], ../common/evmforks, ../core/eip4844, - ./modexp + ./modexp, + ./computation type @@ -48,7 +49,7 @@ type # paBlsMapG1 # paBlsMapG2 # Cancun - + proc getMaxPrecompileAddr(fork: EVMFork): PrecompileAddresses = if fork < FkByzantium: paIdentity @@ -74,9 +75,9 @@ iterator activePrecompiles*(fork: EVMFork): EthAddress = res[^1] = c.byte yield res -proc getSignature(computation: Computation): (array[32, byte], Signature) = +proc getSignature(c: Computation): (array[32, byte], Signature) = # input is Hash, V, R, S - template data: untyped = computation.msg.data + template data: untyped = c.msg.data var bytes: array[65, byte] # will hold R[32], S[32], V[1], in that order let maxPos = min(data.high, 127) @@ -100,7 +101,7 @@ proc getSignature(computation: Computation): (array[32, byte], Signature) = let sig = Signature.fromRaw(bytes) if sig.isErr: - raise newException(ValidationError, "Could not recover signature computation") + raise newException(ValidationError, "Could not recover signature c") result[1] = sig[] # extract message hash, only need to copy when there is a valid signature @@ -143,49 +144,49 @@ proc getFR(data: openArray[byte]): FR = if not result.fromBytes2(data): raise newException(ValidationError, "Could not get FR value") -proc ecRecover*(computation: Computation) = - computation.gasMeter.consumeGas( +proc ecRecover*(c: Computation) = + c.gasMeter.consumeGas( GasECRecover, reason="ECRecover Precompile") var - (msgHash, sig) = computation.getSignature() + (msgHash, sig) = c.getSignature() var pubkey = recover(sig, SkMessage(msgHash)) if pubkey.isErr: - raise newException(ValidationError, "Could not derive public key from computation") + raise newException(ValidationError, "Could not derive public key from c") - computation.output.setLen(32) - computation.output[12..31] = pubkey[].toCanonicalAddress() + c.output.setLen(32) + c.output[12..31] = pubkey[].toCanonicalAddress() #trace "ECRecover precompile", derivedKey = pubkey[].toCanonicalAddress() -proc sha256*(computation: Computation) = +proc sha256*(c: Computation) = let - wordCount = wordCount(computation.msg.data.len) + wordCount = wordCount(c.msg.data.len) gasFee = GasSHA256 + wordCount * GasSHA256Word - computation.gasMeter.consumeGas(gasFee, reason="SHA256 Precompile") - computation.output = @(sha2.sha256.digest(computation.msg.data).data) - #trace "SHA256 precompile", output = computation.output.toHex + c.gasMeter.consumeGas(gasFee, reason="SHA256 Precompile") + c.output = @(sha2.sha256.digest(c.msg.data).data) + #trace "SHA256 precompile", output = c.output.toHex -proc ripemd160*(computation: Computation) = +proc ripemd160*(c: Computation) = let - wordCount = wordCount(computation.msg.data.len) + wordCount = wordCount(c.msg.data.len) gasFee = GasRIPEMD160 + wordCount * GasRIPEMD160Word - computation.gasMeter.consumeGas(gasFee, reason="RIPEMD160 Precompile") - computation.output.setLen(32) - computation.output[12..31] = @(ripemd.ripemd160.digest(computation.msg.data).data) - #trace "RIPEMD160 precompile", output = computation.output.toHex + c.gasMeter.consumeGas(gasFee, reason="RIPEMD160 Precompile") + c.output.setLen(32) + c.output[12..31] = @(ripemd.ripemd160.digest(c.msg.data).data) + #trace "RIPEMD160 precompile", output = c.output.toHex -proc identity*(computation: Computation) = +proc identity*(c: Computation) = let - wordCount = wordCount(computation.msg.data.len) + wordCount = wordCount(c.msg.data.len) gasFee = GasIdentity + wordCount * GasIdentityWord - computation.gasMeter.consumeGas(gasFee, reason="Identity Precompile") - computation.output = computation.msg.data - #trace "Identity precompile", output = computation.output.toHex + c.gasMeter.consumeGas(gasFee, reason="Identity Precompile") + c.output = c.msg.data + #trace "Identity precompile", output = c.output.toHex proc modExpFee(c: Computation, baseLen, expLen, modLen: UInt256, fork: EVMFork): GasInt = template data: untyped {.dirty.} = @@ -287,16 +288,16 @@ proc modExp*(c: Computation, fork: EVMFork = FkByzantium) = c.output = newSeq[byte](modLen) c.output[^output.len..^1] = output[0..^1] -proc bn256ecAdd*(computation: Computation, fork: EVMFork = FkByzantium) = +proc bn256ecAdd*(c: Computation, fork: EVMFork = FkByzantium) = let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul - computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile") + c.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile") var input: array[128, byte] output: array[64, byte] # Padding data - let len = min(computation.msg.data.len, 128) - 1 - input[0..len] = computation.msg.data[0..len] + let len = min(c.msg.data.len, 128) - 1 + input[0..len] = c.msg.data[0..len] var p1 = G1.getPoint(input.toOpenArray(0, 63)) var p2 = G1.getPoint(input.toOpenArray(64, 127)) var apo = (p1 + p2).toAffine() @@ -304,19 +305,19 @@ proc bn256ecAdd*(computation: Computation, fork: EVMFork = FkByzantium) = # we can discard here because we supply proper buffer discard apo.get().toBytes(output) - computation.output = @output + c.output = @output -proc bn256ecMul*(computation: Computation, fork: EVMFork = FkByzantium) = +proc bn256ecMul*(c: Computation, fork: EVMFork = FkByzantium) = let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul - computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile") + c.gasMeter.consumeGas(gasFee, reason="ecMul Precompile") var input: array[96, byte] output: array[64, byte] # Padding data - let len = min(computation.msg.data.len, 96) - 1 - input[0..len] = computation.msg.data[0..len] + let len = min(c.msg.data.len, 96) - 1 + input[0..len] = c.msg.data[0..len] var p1 = G1.getPoint(input.toOpenArray(0, 63)) var fr = getFR(input.toOpenArray(64, 95)) var apo = (p1 * fr).toAffine() @@ -324,10 +325,10 @@ proc bn256ecMul*(computation: Computation, fork: EVMFork = FkByzantium) = # we can discard here because we supply buffer of proper size discard apo.get().toBytes(output) - computation.output = @output + c.output = @output -proc bn256ecPairing*(computation: Computation, fork: EVMFork = FkByzantium) = - let msglen = len(computation.msg.data) +proc bn256ecPairing*(c: Computation, fork: EVMFork = FkByzantium) = + let msglen = len(c.msg.data) if msglen mod 192 != 0: raise newException(ValidationError, "Invalid input length") @@ -336,7 +337,7 @@ proc bn256ecPairing*(computation: Computation, fork: EVMFork = FkByzantium) = GasECPairingBase + numPoints * GasECPairingPerPoint else: GasECPairingBaseIstanbul + numPoints * GasECPairingPerPointIstanbul - computation.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile") + c.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile") var output: array[32, byte] if msglen == 0: @@ -351,9 +352,9 @@ proc bn256ecPairing*(computation: Computation, fork: EVMFork = FkByzantium) = for i in 0..= FkByzantium and precompile > paIdentity: - computation.error = Error(info: e.msg, burnsGas: true) - else: - # swallow any other precompiles errors - debug "execPrecompiles validation error", msg=e.msg + let lb = c.msg.codeAddress[19] + if not validPrecompileAddr(lb, fork): + return + + let precompile = PrecompileAddresses(lb) + try: + case precompile + of paEcRecover: ecRecover(c) + of paSha256: sha256(c) + of paRipeMd160: ripemd160(c) + of paIdentity: identity(c) + of paModExp: modExp(c, fork) + of paEcAdd: bn256ecAdd(c, fork) + of paEcMul: bn256ecMul(c, fork) + of paPairing: bn256ecPairing(c, fork) + of paBlake2bf: blake2bf(c) + of paPointEvaluation: pointEvaluation(c) + #else: discard + # EIP 2537: disabled + # reason: not included in berlin + # of paBlsG1Add: blsG1Add(c) + # of paBlsG1Mul: blsG1Mul(c) + # of paBlsG1MultiExp: blsG1MultiExp(c) + # of paBlsG2Add: blsG2Add(c) + # of paBlsG2Mul: blsG2Mul(c) + # of paBlsG2MultiExp: blsG2MultiExp(c) + # of paBlsPairing: blsPairing(c) + # of paBlsMapG1: blsMapG1(c) + # of paBlsMapG2: blsMapG2(c) + except OutOfGas as e: + c.setError(EVMC_OUT_OF_GAS, e.msg, true) + except CatchableError as e: + if fork >= FkByzantium and precompile > paIdentity: + c.setError(EVMC_PRECOMPILE_FAILURE, e.msg, true) + else: + # swallow any other precompiles errors + debug "execPrecompiles validation error", msg=e.msg + true diff --git a/nimbus/evm/state.nim b/nimbus/evm/state.nim index f45620eb6..62bd52f23 100644 --- a/nimbus/evm/state.nim +++ b/nimbus/evm/state.nim @@ -35,7 +35,6 @@ proc init( asyncFactory: AsyncOperationFactory = AsyncOperationFactory(maybeDataSource: none[AsyncDataSource]())) {.gcsafe.} = ## Initialisation helper - self.prevHeaders = @[] self.parent = parent self.timestamp = timestamp self.gasLimit = gasLimit @@ -297,17 +296,6 @@ method getAncestorHash*( else: result = db.getBlockHash(blockNumber) - #TODO: should we use deque here? - # someday we may revive this code when - # we already have working miner - when false: - let idx = ancestorDepth.toInt - if idx >= vmState.prevHeaders.len: - return - - var header = vmState.prevHeaders[idx] - result = header.hash - proc readOnlyStateDB*(vmState: BaseVMState): ReadOnlyStateDB {.inline.} = ReadOnlyStateDB(vmState.stateDB) diff --git a/nimbus/evm/tracer/legacy_tracer.nim b/nimbus/evm/tracer/legacy_tracer.nim index b203aa179..8b720313e 100644 --- a/nimbus/evm/tracer/legacy_tracer.nim +++ b/nimbus/evm/tracer/legacy_tracer.nim @@ -37,16 +37,6 @@ iterator storage(ctx: LegacyTracer, compDepth: int): UInt256 = for key in ctx.storageKeys[compDepth]: yield key -template stripLeadingZeros(value: string): string = - var cidx = 0 - # ignore the last character so we retain '0' on zero value - while cidx < value.len - 1 and value[cidx] == '0': - cidx.inc - value[cidx .. ^1] - -proc encodeHexInt(x: SomeInteger): JsonNode = - %("0x" & x.toHex.stripLeadingZeros.toLowerAscii) - proc newLegacyTracer*(flags: set[TracerFlags]): LegacyTracer = let trace = newJObject() diff --git a/nimbus/evm/types.nim b/nimbus/evm/types.nim index 24890caca..e0d01fa8d 100644 --- a/nimbus/evm/types.nim +++ b/nimbus/evm/types.nim @@ -17,6 +17,14 @@ import ../db/accounts_cache, ../common/[common, evmforks] +# this import not guarded by `when defined(evmc_enabled)` +# because we want to use evmc types such as evmc_call_kind +# and evmc_flags +import + evmc/evmc + +export evmc + {.push raises: [].} when defined(evmc_enabled): @@ -35,27 +43,26 @@ type ClearCache BaseVMState* = ref object of RootObj - prevHeaders* : seq[BlockHeader] - com* : CommonRef - gasPool* : GasInt - parent* : BlockHeader - timestamp* : EthTime - gasLimit* : GasInt - fee* : Option[UInt256] - prevRandao* : Hash256 - blockDifficulty*: UInt256 - flags* : set[VMFlag] - tracer* : TracerRef - receipts* : seq[Receipt] - stateDB* : AccountsCache + com* : CommonRef + gasPool* : GasInt + parent* : BlockHeader + timestamp* : EthTime + gasLimit* : GasInt + fee* : Option[UInt256] + prevRandao* : Hash256 + blockDifficulty* : UInt256 + flags* : set[VMFlag] + tracer* : TracerRef + receipts* : seq[Receipt] + stateDB* : AccountsCache cumulativeGasUsed*: GasInt - txOrigin* : EthAddress - txGasPrice* : GasInt + txOrigin* : EthAddress + txGasPrice* : GasInt txVersionedHashes*: VersionedHashes - gasCosts* : GasCosts - fork* : EVMFork - minerAddress* : EthAddress - asyncFactory* : AsyncOperationFactory + gasCosts* : GasCosts + fork* : EVMFork + minerAddress* : EthAddress + asyncFactory* : AsyncOperationFactory Computation* = ref object # The execution computation @@ -82,23 +89,17 @@ type continuation*: proc() {.gcsafe, raises: [CatchableError].} Error* = ref object - info*: string - burnsGas*: bool + statusCode*: evmc_status_code + info* : string + burnsGas* : bool GasMeter* = object gasRefunded*: GasInt gasRemaining*: GasInt - CallKind* = enum - evmcCall = 0, # CALL - evmcDelegateCall = 1, # DELEGATECALL - evmcCallCode = 2, # CALLCODE - evmcCreate = 3, # CREATE - evmcCreate2 = 4 # CREATE2 + CallKind* = evmc_call_kind - MsgFlags* = enum - emvcNoFlags = 0 - emvcStatic = 1 + MsgFlags* = evmc_flags Message* = ref object kind*: CallKind diff --git a/nimbus/transaction/call_common.nim b/nimbus/transaction/call_common.nim index 90e830ea1..fb42f90d5 100644 --- a/nimbus/transaction/call_common.nim +++ b/nimbus/transaction/call_common.nim @@ -66,7 +66,7 @@ proc hostToComputationMessage*(msg: EvmcMessage): Message = # When input size is zero, input data pointer may be null. data: if msg.input_size <= 0: @[] else: @(makeOpenArray(msg.input_data, msg.input_size.int)), - flags: if msg.isStatic: emvcStatic else: emvcNoFlags + flags: msg.flags ) func intrinsicGas*(call: CallParams, vmState: BaseVMState): GasInt {.inline.} = @@ -206,9 +206,9 @@ when defined(evmc_enabled): if callResult.status_code == EVMC_SUCCESS: c.error = nil elif callResult.status_code == EVMC_REVERT: - c.setError("EVMC_REVERT", false) + c.setError(EVMC_REVERT, false) else: - c.setError($callResult.status_code, true) + c.setError(callResult.status_code, true) c.gasMeter.gasRemaining = callResult.gas_left c.msg.contractAddress = callResult.create_address.fromEvmc @@ -278,12 +278,7 @@ proc finishRunningComputation(host: TransactionHost, call: CallParams): CallResu let gasRemaining = calculateAndPossiblyRefundGas(host, call) # evm gas used without intrinsic gas let gasUsed = host.msg.gas - gasRemaining - let error = if c.isError: - some(c.error.info) - else: - none(string) - - host.vmState.captureEnd(c, c.output, gasUsed, error) + host.vmState.captureEnd(c, c.output, gasUsed, c.errorOpt) result.isError = c.isError result.gasUsed = call.gasLimit - gasRemaining diff --git a/nimbus/transaction/evmc_host_glue.nim b/nimbus/transaction/evmc_host_glue.nim index dca0e2560..841e3ad14 100644 --- a/nimbus/transaction/evmc_host_glue.nim +++ b/nimbus/transaction/evmc_host_glue.nim @@ -83,11 +83,11 @@ proc accessStorage(p: evmc_host_context, address: var evmc_address, proc getTransientStorage(p: evmc_host_context, address: var evmc_address, key: var evmc_bytes32): evmc_bytes32 - {.cdecl, raises: [RlpError].} = + {.cdecl, raises: [].} = toHost(p).getTransientStorage(address.fromEvmc, key.flip256.fromEvmc).toEvmc.flip256 proc setTransientStorage(p: evmc_host_context, address: var evmc_address, - key, value: var evmc_bytes32) {.cdecl, raises: [RlpError].} = + key, value: var evmc_bytes32) {.cdecl, raises: [].} = toHost(p).setTransientStorage(address.fromEvmc, key.flip256.fromEvmc, value.flip256.fromEvmc) let hostInterface = evmc_host_interface( diff --git a/nimbus/transaction/evmc_vm_glue.nim b/nimbus/transaction/evmc_vm_glue.nim index c8fab8f35..1c0bdd124 100644 --- a/nimbus/transaction/evmc_vm_glue.nim +++ b/nimbus/transaction/evmc_vm_glue.nim @@ -9,7 +9,7 @@ {.push raises: [].} import - ./host_types, evmc/evmc, stew/ranges/ptr_arith, + ./host_types, evmc/evmc, ".."/[vm_types, vm_computation, vm_state_transactions] proc evmcReleaseResult(result: var evmc_result) {.cdecl.} = @@ -56,9 +56,7 @@ proc evmcExecute(vm: ptr evmc_vm, hostInterface: ptr evmc_host_interface, return evmc_result( # Standard EVMC result, if a bit generic. - status_code: if c.isSuccess: EVMC_SUCCESS - elif not c.error.burnsGas: EVMC_REVERT - else: EVMC_FAILURE, + status_code: c.statusCode, # Gas left is required to be zero when not `EVMC_SUCCESS` or `EVMC_REVERT`. gas_left: if result.status_code notin {EVMC_SUCCESS, EVMC_REVERT}: 0'i64 else: c.gasMeter.gasRemaining.int64, diff --git a/nimbus/transaction/host_call_nested.nim b/nimbus/transaction/host_call_nested.nim index a40dbc2bd..9f65c67b8 100644 --- a/nimbus/transaction/host_call_nested.nim +++ b/nimbus/transaction/host_call_nested.nim @@ -43,7 +43,7 @@ proc afterExecCreateEvmcNested(host: TransactionHost, child: Computation, res.status_code = EVMC_SUCCESS res.create_address = child.msg.contractAddress.toEvmc else: - res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT + res.status_code = child.statusCode if child.output.len > 0: # TODO: can we move the ownership of seq to raw pointer? res.output_size = child.output.len.uint @@ -65,7 +65,7 @@ proc beforeExecCallEvmcNested(host: TransactionHost, host.computation.msg.contractAddress, value: m.value.fromEvmc, data: @(makeOpenArray(m.inputData, m.inputSize.int)), - flags: if m.isStatic: emvcStatic else: emvcNoFlags, + flags: m.flags, ) return newComputation(host.vmState, childMsg) @@ -78,7 +78,7 @@ proc afterExecCallEvmcNested(host: TransactionHost, child: Computation, host.computation.merge(child) res.status_code = EVMC_SUCCESS else: - res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT + res.status_code = child.statusCode if child.output.len > 0: # TODO: can we move the ownership of seq to raw pointer? diff --git a/nimbus/transaction/host_services.nim b/nimbus/transaction/host_services.nim index 1827e6769..a33eb6e9d 100644 --- a/nimbus/transaction/host_services.nim +++ b/nimbus/transaction/host_services.nim @@ -9,7 +9,7 @@ #{.push raises: [].} import - sets, times, stint, chronicles, + times, stint, chronicles, eth/common/eth_types, ../db/accounts_cache, ../common/[evmforks, common], ".."/[vm_state, vm_computation, vm_internals, vm_gas_costs], diff --git a/nimbus/transaction/host_types.nim b/nimbus/transaction/host_types.nim index 83723b3ac..e696d6284 100644 --- a/nimbus/transaction/host_types.nim +++ b/nimbus/transaction/host_types.nim @@ -88,9 +88,6 @@ template flip256*(word256: evmc_uint256be): evmc_uint256be = template isCreate*(kind: EvmcCallKind): bool = kind in {EVMC_CREATE, EVMC_CREATE2} -template isStatic*(msg: EvmcMessage): bool = - EVMC_STATIC in msg.flags - template isZero*(n: evmc_bytes32): bool = n == default(evmc_bytes32) diff --git a/nimbus/vm_computation.nim b/nimbus/vm_computation.nim index f030d9108..67667fc53 100644 --- a/nimbus/vm_computation.nim +++ b/nimbus/vm_computation.nim @@ -8,9 +8,6 @@ # at your option. This file may not be copied, modified, or distributed except # according to those terms. - -# The VM computation module suffers from a circular include/import dependency. -# After fixing this wrapper should be re-factored. import ./evm/computation as vmc, ./evm/interpreter_dispatch as vmi @@ -57,6 +54,8 @@ export vmc.traceOpCodeEnded, vmc.traceOpCodeStarted, vmc.tracingEnabled, - vmc.writeContract + vmc.writeContract, + vmc.statusCode, + vmc.errorOpt # End