From 7c3b7ab7a8e2f39f8f2f59249002f404ecdd0561 Mon Sep 17 00:00:00 2001 From: Jamie Lokier Date: Fri, 30 Apr 2021 10:33:14 +0100 Subject: [PATCH] RPC: Gather EVM-calling functions to one place; rpcSetupComputation Start gathering the functions that call the EVM into one place, `transaction/call_evm.nim`. This is first of a series of changes to gather all ways the EVM is called to one place. Duplicate, slightly different setup functions have accumulated over time, each with some knowledge of EVM internals. When they are brought together, these methods will be changed to use a single entry point to the EVM, allowing the entry point to be refactored, EVMC to be completed, and async concurrency to be implemented on top. This also simplifies the callers. First, a helper function used by RPC and GraphQL to make EVM calls without permanently modifying the account state. `setupComputation` -> `rpcSetupComputation`. Signed-off-by: Jamie Lokier --- nimbus/graphql/ethapi.nim | 11 +++++---- nimbus/rpc/rpc_utils.nim | 40 +++++--------------------------- nimbus/transaction/call_evm.nim | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 nimbus/transaction/call_evm.nim diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index 2781af8fa..2337ea90a 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -15,7 +15,8 @@ import graphql/common/types, graphql/httpserver, ../db/[db_chain, state_db], ../errors, ../utils, ../transaction, ../rpc/rpc_utils, ../vm_state, ../config, - ../vm_computation, ../vm_state_transactions + ../vm_computation, ../vm_state_transactions, + ../transaction/call_evm from eth/p2p import EthereumNode export httpserver @@ -716,9 +717,9 @@ proc blockAccount(ud: RootRef, params: Args, parent: Node): RespResult {.apiPrag let address = hexToByteArray[20](params[0].val.stringVal) ctx.accountNode(h.header, address) -proc toCallData(n: Node): (CallData, bool) = +proc toCallData(n: Node): (RpcCallData, bool) = # phew, probably need to use macro here :) - var cd: CallData + var cd: RpcCallData var gasLimit = false if n[0][1].kind != nkEmpty: cd.source = hextoByteArray[20](n[0][1].stringVal) @@ -744,14 +745,14 @@ proc toCallData(n: Node): (CallData, bool) = (cd, gasLimit) -proc makeCall(ctx: GraphqlContextRef, callData: CallData, header: BlockHeader, chainDB: BaseChainDB): RespResult = +proc makeCall(ctx: GraphqlContextRef, callData: RpcCallData, header: BlockHeader, chainDB: BaseChainDB): RespResult = # TODO: handle revert var # we use current header stateRoot, unlike block validation # which use previous block stateRoot vmState = newBaseVMState(header.stateRoot, header, chainDB) fork = toFork(chainDB.config, header.blockNumber) - comp = setupComputation(vmState, callData, fork) + comp = rpcSetupComputation(vmState, callData, fork) let gas = comp.gasMeter.gasRemaining comp.execComputation() diff --git a/nimbus/rpc/rpc_utils.nim b/nimbus/rpc/rpc_utils.nim index bac7a71bc..0eec165c9 100644 --- a/nimbus/rpc/rpc_utils.nim +++ b/nimbus/rpc/rpc_utils.nim @@ -11,7 +11,8 @@ import hexstrings, eth/[common, rlp, keys, trie/db], stew/byteutils, nimcrypto, ../db/[db_chain, accounts_cache], strutils, algorithm, options, times, json, ../constants, stint, hexstrings, rpc_types, ../config, ../vm_state_transactions, ../vm_state, ../vm_types, ../vm_types2, - ../vm_computation, ../p2p/executor, ../utils, ../transaction + ../vm_computation, ../p2p/executor, ../utils, ../transaction, + ../transaction/call_evm type UnsignedTx* = object @@ -23,15 +24,6 @@ type payload* : Blob contractCreation* {.rlpIgnore.}: bool - CallData* = object - source*: EthAddress - to*: EthAddress - gas*: GasInt - gasPrice*: GasInt - value*: UInt256 - data*: seq[byte] - contractCreation*: bool - proc read(rlp: var Rlp, t: var UnsignedTx, _: type EthAddress): EthAddress {.inline.} = if rlp.blobLen != 0: result = rlp.read(EthAddress) @@ -161,7 +153,7 @@ proc signTransaction*(tx: UnsignedTx, chain: BaseChainDB, privateKey: PrivateKey S: Uint256.fromBytesBE(sig[32..63]) ) -proc callData*(call: EthCall, callMode: bool = true, chain: BaseChainDB): CallData = +proc callData*(call: EthCall, callMode: bool = true, chain: BaseChainDB): RpcCallData = if call.source.isSome: result.source = toAddress(call.source.get) @@ -188,40 +180,20 @@ proc callData*(call: EthCall, callMode: bool = true, chain: BaseChainDB): CallDa if call.data.isSome: result.data = hexToSeqByte(call.data.get.string) -proc setupComputation*(vmState: BaseVMState, call: CallData, fork: Fork) : Computation = - vmState.setupTxContext( - origin = call.source, - gasPrice = call.gasPrice, - forkOverride = some(fork) - ) - - let msg = Message( - kind: evmcCall, - depth: 0, - gas: call.gas, - sender: call.source, - contractAddress: call.to, - codeAddress: call.to, - value: call.value, - data: call.data - ) - - result = newComputation(vmState, msg) - -proc doCall*(call: CallData, header: BlockHeader, chain: BaseChainDB): HexDataStr = +proc doCall*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB): HexDataStr = var # we use current header stateRoot, unlike block validation # which use previous block stateRoot vmState = newBaseVMState(header.stateRoot, header, chain) fork = toFork(chain.config, header.blockNumber) - comp = setupComputation(vmState, call, fork) + comp = rpcSetupComputation(vmState, call, fork) comp.execComputation() result = hexDataStr(comp.output) # TODO: handle revert and error # TODO: handle contract ABI -proc estimateGas*(call: CallData, header: BlockHeader, chain: BaseChainDB, haveGasLimit: bool): GasInt = +proc estimateGas*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB, haveGasLimit: bool): GasInt = var # we use current header stateRoot, unlike block validation # which use previous block stateRoot diff --git a/nimbus/transaction/call_evm.nim b/nimbus/transaction/call_evm.nim new file mode 100644 index 000000000..9ba5b4133 --- /dev/null +++ b/nimbus/transaction/call_evm.nim @@ -0,0 +1,41 @@ +# Nimbus - Various ways of calling the EVM +# +# Copyright (c) 2018-2021 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. + +import + eth/common/eth_types, stint, options, + ".."/[vm_types, vm_types2, vm_state, vm_computation] + +type + RpcCallData* = object + source*: EthAddress + to*: EthAddress + gas*: GasInt + gasPrice*: GasInt + value*: UInt256 + data*: seq[byte] + contractCreation*: bool + +proc rpcSetupComputation*(vmState: BaseVMState, call: RpcCallData, fork: Fork): Computation = + vmState.setupTxContext( + origin = call.source, + gasPrice = call.gasPrice, + forkOverride = some(fork) + ) + + let msg = Message( + kind: evmcCall, + depth: 0, + gas: call.gas, + sender: call.source, + contractAddress: call.to, + codeAddress: call.to, + value: call.value, + data: call.data + ) + + return newComputation(vmState, msg)