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 <jamie@shareable.org>
This commit is contained in:
parent
24ce283498
commit
7c3b7ab7a8
|
@ -15,7 +15,8 @@ import
|
||||||
graphql/common/types, graphql/httpserver,
|
graphql/common/types, graphql/httpserver,
|
||||||
../db/[db_chain, state_db], ../errors, ../utils,
|
../db/[db_chain, state_db], ../errors, ../utils,
|
||||||
../transaction, ../rpc/rpc_utils, ../vm_state, ../config,
|
../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
|
from eth/p2p import EthereumNode
|
||||||
export httpserver
|
export httpserver
|
||||||
|
@ -716,9 +717,9 @@ proc blockAccount(ud: RootRef, params: Args, parent: Node): RespResult {.apiPrag
|
||||||
let address = hexToByteArray[20](params[0].val.stringVal)
|
let address = hexToByteArray[20](params[0].val.stringVal)
|
||||||
ctx.accountNode(h.header, address)
|
ctx.accountNode(h.header, address)
|
||||||
|
|
||||||
proc toCallData(n: Node): (CallData, bool) =
|
proc toCallData(n: Node): (RpcCallData, bool) =
|
||||||
# phew, probably need to use macro here :)
|
# phew, probably need to use macro here :)
|
||||||
var cd: CallData
|
var cd: RpcCallData
|
||||||
var gasLimit = false
|
var gasLimit = false
|
||||||
if n[0][1].kind != nkEmpty:
|
if n[0][1].kind != nkEmpty:
|
||||||
cd.source = hextoByteArray[20](n[0][1].stringVal)
|
cd.source = hextoByteArray[20](n[0][1].stringVal)
|
||||||
|
@ -744,14 +745,14 @@ proc toCallData(n: Node): (CallData, bool) =
|
||||||
|
|
||||||
(cd, gasLimit)
|
(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
|
# TODO: handle revert
|
||||||
var
|
var
|
||||||
# we use current header stateRoot, unlike block validation
|
# we use current header stateRoot, unlike block validation
|
||||||
# which use previous block stateRoot
|
# which use previous block stateRoot
|
||||||
vmState = newBaseVMState(header.stateRoot, header, chainDB)
|
vmState = newBaseVMState(header.stateRoot, header, chainDB)
|
||||||
fork = toFork(chainDB.config, header.blockNumber)
|
fork = toFork(chainDB.config, header.blockNumber)
|
||||||
comp = setupComputation(vmState, callData, fork)
|
comp = rpcSetupComputation(vmState, callData, fork)
|
||||||
|
|
||||||
let gas = comp.gasMeter.gasRemaining
|
let gas = comp.gasMeter.gasRemaining
|
||||||
comp.execComputation()
|
comp.execComputation()
|
||||||
|
|
|
@ -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,
|
../db/[db_chain, accounts_cache], strutils, algorithm, options, times, json,
|
||||||
../constants, stint, hexstrings, rpc_types, ../config,
|
../constants, stint, hexstrings, rpc_types, ../config,
|
||||||
../vm_state_transactions, ../vm_state, ../vm_types, ../vm_types2,
|
../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
|
type
|
||||||
UnsignedTx* = object
|
UnsignedTx* = object
|
||||||
|
@ -23,15 +24,6 @@ type
|
||||||
payload* : Blob
|
payload* : Blob
|
||||||
contractCreation* {.rlpIgnore.}: bool
|
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.} =
|
proc read(rlp: var Rlp, t: var UnsignedTx, _: type EthAddress): EthAddress {.inline.} =
|
||||||
if rlp.blobLen != 0:
|
if rlp.blobLen != 0:
|
||||||
result = rlp.read(EthAddress)
|
result = rlp.read(EthAddress)
|
||||||
|
@ -161,7 +153,7 @@ proc signTransaction*(tx: UnsignedTx, chain: BaseChainDB, privateKey: PrivateKey
|
||||||
S: Uint256.fromBytesBE(sig[32..63])
|
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:
|
if call.source.isSome:
|
||||||
result.source = toAddress(call.source.get)
|
result.source = toAddress(call.source.get)
|
||||||
|
|
||||||
|
@ -188,40 +180,20 @@ proc callData*(call: EthCall, callMode: bool = true, chain: BaseChainDB): CallDa
|
||||||
if call.data.isSome:
|
if call.data.isSome:
|
||||||
result.data = hexToSeqByte(call.data.get.string)
|
result.data = hexToSeqByte(call.data.get.string)
|
||||||
|
|
||||||
proc setupComputation*(vmState: BaseVMState, call: CallData, fork: Fork) : Computation =
|
proc doCall*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB): HexDataStr =
|
||||||
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 =
|
|
||||||
var
|
var
|
||||||
# we use current header stateRoot, unlike block validation
|
# we use current header stateRoot, unlike block validation
|
||||||
# which use previous block stateRoot
|
# which use previous block stateRoot
|
||||||
vmState = newBaseVMState(header.stateRoot, header, chain)
|
vmState = newBaseVMState(header.stateRoot, header, chain)
|
||||||
fork = toFork(chain.config, header.blockNumber)
|
fork = toFork(chain.config, header.blockNumber)
|
||||||
comp = setupComputation(vmState, call, fork)
|
comp = rpcSetupComputation(vmState, call, fork)
|
||||||
|
|
||||||
comp.execComputation()
|
comp.execComputation()
|
||||||
result = hexDataStr(comp.output)
|
result = hexDataStr(comp.output)
|
||||||
# TODO: handle revert and error
|
# TODO: handle revert and error
|
||||||
# TODO: handle contract ABI
|
# 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
|
var
|
||||||
# we use current header stateRoot, unlike block validation
|
# we use current header stateRoot, unlike block validation
|
||||||
# which use previous block stateRoot
|
# which use previous block stateRoot
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue