Initial commit for eth_estimateGas

This commit is contained in:
coffeepots 2018-11-27 18:21:28 +00:00 committed by Ștefan Talpalaru
parent 03c1664c8a
commit ac9fb37465
No known key found for this signature in database
GPG Key ID: CBF7934204F1B6F9
1 changed files with 70 additions and 3 deletions

View File

@ -13,7 +13,7 @@ import
eth_common, eth_p2p, eth_keys, eth_trie/db, rlp,
../utils/header, ../transaction, ../config, ../vm_state, ../constants, ../vm_types,
../vm_state_transactions, ../utils/addresses,
../db/[db_chain, state_db, storage_types],
../vm/[interpreter_dispatch, computation],
rpc_types, rpc_utils, ../vm/[message, computation]
#[
@ -33,6 +33,52 @@ template balance(addressDb: ReadOnlyStateDb, address: EthAddress): GasInt =
# TODO: Account balance u256 but GasInt is int64?
addressDb.getBalance(address).truncate(int64)
proc binarySearchGas(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress, gasPrice: GasInt, tolerance = 1): GasInt =
proc dummyComputation(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress): BaseComputation =
# Note that vmState may be altered
setupComputation(
vmState.blockHeader,
vmState,
transaction,
sender)
proc dummyTransaction(gasLimit, gasPrice: GasInt, destination: EthAddress, value: UInt256): Transaction =
Transaction(
accountNonce: 0.AccountNonce,
gasPrice: gasPrice,
gasLimit: gasLimit,
to: destination,
value: value
)
var
hiGas = vmState.gasLimit
loGas = transaction.intrinsicGas
gasPrice = transaction.gasPrice # TODO: Or zero?
proc tryTransaction(vmState: var BaseVMState, gasLimit: GasInt): bool =
var
spoofTransaction = dummyTransaction(gasLimit, gasPrice, transaction.to, transaction.value)
computation = vmState.dummyComputation(spoofTransaction, sender)
computation.executeOpcodes
if not computation.isError:
return true
if vmState.tryTransaction(loGas):
return loGas
if not vmState.tryTransaction(hiGas):
return 0.GasInt # TODO: Reraise error from computation
var
minVal = vmState.gasLimit
maxVal = transaction.intrinsicGas
while loGas - hiGas > tolerance:
let midPoint = (loGas + hiGas) div 2
if vmState.tryTransaction(midPoint):
minVal = midPoint
else:
maxVal = midPoint
result = minVal
proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
func getAccountDb(header: BlockHeader): ReadOnlyStateDB =
@ -274,7 +320,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
discard comp.execComputation
result = ("0x" & nimcrypto.toHex(comp.output)).HexDataStr
rpcsrv.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> GasInt:
## Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
## The transaction will not be added to the blockchain. Note that the estimate may be significantly more than
@ -283,7 +329,28 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
## call: the transaction call object.
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
## Returns the amount of gas used.
discard
# TODO: Use optional fields with EthCall
var
header = chain.headerFromTag(quantityTag)
vmState = newBaseVMState(header, chain)
let
gasLimit = if call.gas > 0.GasInt: call.gas else: header.gasLimit
gasPrice = if call.gasPrice > 0: call.gasPrice else: 0.GasInt
curState = chain.getStateDb(header.stateRoot, true)
sender = call.source.string.strToAddress
destination = call.to.string.strToAddress
nonce = curState.getNonce(sender)
value = call.value
# TODO: Use initTransaction when merged
transaction = Transaction(
accountNonce: nonce,
gasPrice: gasPrice,
gasLimit: gasLimit,
to: destination,
value: value.u256,
payload: @[]
)
result = vmState.binarySearchGas(transaction, sender, gasPrice)
func populateBlockObject(header: BlockHeader, blockBody: BlockBody): BlockObject =
result.number = some(header.blockNumber)