From 8bda81496a387538d019eaa0a0cda1ddbf4b4cac Mon Sep 17 00:00:00 2001 From: Jamie Lokier Date: Mon, 3 May 2021 09:08:11 +0100 Subject: [PATCH] RPC: Move EVM-calling function estimateGas to rpcEstimateGas `estimateGas` used by JSON-RPC is another way to setup and call the EVM, also used by GraphQL. Move it to `transaction/call_evm`. This function has too much direct knowledge of details that shouldn't be used outside transaction handling code, details we need to change when changing the db and transaction memory layer. Moving this one exposed quite a bit of abstraction leakage, as it calls directly to the hexary trie db around `processTransaction`. It looks like the _intended_ functionality of `estimateGas` is similar to `rpcDoCall` with the only real difference being to not store the final state. It looks like the extra stuff in `estimateGas` compared with `doCall` is a messy workaround for computation not exposing the right API ("don't save final state") for RPC to use. Signed-off-by: Jamie Lokier --- nimbus/graphql/ethapi.nim | 2 +- nimbus/rpc/p2p.nim | 2 +- nimbus/rpc/rpc_utils.nim | 22 ---------------------- nimbus/transaction/call_evm.nim | 25 ++++++++++++++++++++++++- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index 2337ea90a..f38969158 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -778,7 +778,7 @@ proc blockEstimateGas(ud: RootRef, params: Args, parent: Node): RespResult {.api let param = params[0].val try: let (callData, gasLimit) = toCallData(param) - let gasUsed = estimateGas(callData, h.header, ctx.chainDB, gasLimit) + let gasUsed = rpcEstimateGas(callData, h.header, ctx.chainDB, gasLimit) longNode(gasUsed) except Exception as em: err("estimateGas error: " & em.msg) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 7663a92ba..f7ab6a403 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -275,7 +275,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) = let header = chain.headerFromTag(quantityTag) callData = callData(call, false, chain) - gasUsed = estimateGas(callData, header, chain, call.gas.isSome) + gasUsed = rpcEstimateGas(callData, header, chain, call.gas.isSome) result = encodeQuantity(gasUsed.uint64) server.rpc("eth_getBlockByHash") do(data: EthHashStr, fullTransactions: bool) -> Option[BlockObject]: diff --git a/nimbus/rpc/rpc_utils.nim b/nimbus/rpc/rpc_utils.nim index b137541c8..15c59a470 100644 --- a/nimbus/rpc/rpc_utils.nim +++ b/nimbus/rpc/rpc_utils.nim @@ -180,28 +180,6 @@ proc callData*(call: EthCall, callMode: bool = true, chain: BaseChainDB): RpcCal if call.data.isSome: result.data = hexToSeqByte(call.data.get.string) -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 - vmState = newBaseVMState(header.stateRoot, header, chain) - fork = toFork(chain.config, header.blockNumber) - tx = Transaction( - accountNonce: vmState.accountdb.getNonce(call.source), - gasPrice: call.gasPrice, - gasLimit: if haveGasLimit: call.gas else: header.gasLimit - vmState.cumulativeGasUsed, - to : call.to, - value : call.value, - payload : call.data, - isContractCreation: call.contractCreation - ) - - var dbTx = chain.db.beginTransaction() - defer: dbTx.dispose() - result = processTransaction(tx, call.source, vmState, fork) - dbTx.dispose() - # TODO: handle revert and error - proc populateTransactionObject*(tx: Transaction, header: BlockHeader, txIndex: int): TransactionObject = result.blockHash = some(header.hash) result.blockNumber = some(encodeQuantity(header.blockNumber)) diff --git a/nimbus/transaction/call_evm.nim b/nimbus/transaction/call_evm.nim index d9deb687f..ae67767b6 100644 --- a/nimbus/transaction/call_evm.nim +++ b/nimbus/transaction/call_evm.nim @@ -9,7 +9,8 @@ import eth/common/eth_types, stint, options, ".."/[vm_types, vm_types2, vm_state, vm_computation], - ".."/[db/db_chain, config, vm_state_transactions, rpc/hexstrings] + ".."/[db/db_chain, config, vm_state_transactions, rpc/hexstrings], + ".."/[db/accounts_cache, p2p/executor], eth/trie/db type RpcCallData* = object @@ -53,3 +54,25 @@ proc rpcDoCall*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB): Hex comp.execComputation() result = hexDataStr(comp.output) + +proc rpcEstimateGas*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB, haveGasLimit: bool): GasInt = + # TODO: handle revert and error + 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) + tx = Transaction( + accountNonce: vmState.accountdb.getNonce(call.source), + gasPrice: call.gasPrice, + gasLimit: if haveGasLimit: call.gas else: header.gasLimit - vmState.cumulativeGasUsed, + to : call.to, + value : call.value, + payload : call.data, + isContractCreation: call.contractCreation + ) + + var dbTx = chain.db.beginTransaction() + defer: dbTx.dispose() + result = processTransaction(tx, call.source, vmState, fork) + dbTx.dispose()