Merge branch 'eth_estimateGas'
This commit is contained in:
commit
6d234b70d7
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -14,7 +14,7 @@ import
|
||||||
../utils/header, ../transaction, ../config, ../vm_state, ../constants, ../vm_types,
|
../utils/header, ../transaction, ../config, ../vm_state, ../constants, ../vm_types,
|
||||||
../vm_state_transactions, ../utils/addresses,
|
../vm_state_transactions, ../utils/addresses,
|
||||||
../db/[db_chain, state_db, storage_types],
|
../db/[db_chain, state_db, storage_types],
|
||||||
rpc_types, rpc_utils, ../vm/[message, computation]
|
rpc_types, rpc_utils, ../vm/[message, computation, interpreter_dispatch]
|
||||||
|
|
||||||
#[
|
#[
|
||||||
Note:
|
Note:
|
||||||
|
@ -33,6 +33,51 @@ template balance(addressDb: ReadOnlyStateDb, address: EthAddress): GasInt =
|
||||||
# TODO: Account balance u256 but GasInt is int64?
|
# TODO: Account balance u256 but GasInt is int64?
|
||||||
addressDb.getBalance(address).truncate(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,
|
||||||
|
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) =
|
proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||||
|
|
||||||
func getAccountDb(header: BlockHeader): ReadOnlyStateDB =
|
func getAccountDb(header: BlockHeader): ReadOnlyStateDB =
|
||||||
|
@ -100,7 +145,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||||
let
|
let
|
||||||
accountDb = accountDbFromTag(quantityTag)
|
accountDb = accountDbFromTag(quantityTag)
|
||||||
addrBytes = data.toAddress
|
addrBytes = data.toAddress
|
||||||
balance = accountDb.get_balance(addrBytes)
|
balance = accountDb.getBalance(addrBytes)
|
||||||
|
|
||||||
result = balance
|
result = balance
|
||||||
|
|
||||||
|
@ -283,7 +328,37 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||||
## call: the transaction call object.
|
## call: the transaction call object.
|
||||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||||
## Returns the amount of gas used.
|
## Returns the amount of gas used.
|
||||||
discard
|
var
|
||||||
|
header = chain.headerFromTag(quantityTag)
|
||||||
|
vmState = newBaseVMState(header, chain)
|
||||||
|
let
|
||||||
|
gasLimit = if
|
||||||
|
call.gas.isSome and call.gas.get > 0.GasInt: call.gas.get
|
||||||
|
else: header.gasLimit
|
||||||
|
gasPrice = if
|
||||||
|
call.gasPrice.isSome and call.gasPrice.get > 0: call.gasPrice.get
|
||||||
|
else: 0.GasInt
|
||||||
|
sender = if
|
||||||
|
call.source.isSome: call.source.get.toAddress
|
||||||
|
else: ZERO_ADDRESS
|
||||||
|
destination = if
|
||||||
|
call.to.isSome: call.to.get.toAddress
|
||||||
|
else: ZERO_ADDRESS
|
||||||
|
curState = vmState.readOnlyStateDb()
|
||||||
|
nonce = curState.getNonce(sender)
|
||||||
|
value = if
|
||||||
|
call.value.isSome: call.value.get
|
||||||
|
else: 0.u256
|
||||||
|
|
||||||
|
transaction = Transaction(
|
||||||
|
accountNonce: nonce,
|
||||||
|
gasPrice: gasPrice,
|
||||||
|
gasLimit: gasLimit,
|
||||||
|
to: destination,
|
||||||
|
value: value,
|
||||||
|
payload: @[]
|
||||||
|
)
|
||||||
|
result = vmState.binarySearchGas(transaction, sender, gasPrice)
|
||||||
|
|
||||||
func populateBlockObject(header: BlockHeader, blockBody: BlockBody): BlockObject =
|
func populateBlockObject(header: BlockHeader, blockBody: BlockBody): BlockObject =
|
||||||
result.number = some(header.blockNumber)
|
result.number = some(header.blockNumber)
|
||||||
|
|
|
@ -20,11 +20,19 @@ proc initTransaction*(nonce: AccountNonce, gasPrice, gasLimit: GasInt, to: EthAd
|
||||||
result.S = S
|
result.S = S
|
||||||
result.isContractCreation = isContractCreation
|
result.isContractCreation = isContractCreation
|
||||||
|
|
||||||
|
func intrinsicGas*(data: openarray[byte]): GasInt =
|
||||||
|
result = 21_000 # GasTransaction
|
||||||
|
for i in data:
|
||||||
|
if i == 0:
|
||||||
|
result += 4 # GasTXDataZero
|
||||||
|
else:
|
||||||
|
result += 68 # GasTXDataNonZero
|
||||||
|
|
||||||
proc intrinsicGas*(t: Transaction): GasInt =
|
proc intrinsicGas*(t: Transaction): GasInt =
|
||||||
# Compute the baseline gas cost for this transaction. This is the amount
|
# Compute the baseline gas cost for this transaction. This is the amount
|
||||||
# of gas needed to send this transaction (but that is not actually used
|
# of gas needed to send this transaction (but that is not actually used
|
||||||
# for computation)
|
# for computation)
|
||||||
raise newException(ValueError, "not implemented intrinsicGas")
|
result = t.payload.intrinsicGas
|
||||||
|
|
||||||
proc validate*(t: Transaction) =
|
proc validate*(t: Transaction) =
|
||||||
# Hook called during instantiation to ensure that all transaction
|
# Hook called during instantiation to ensure that all transaction
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
# * 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)
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
@ -781,8 +781,8 @@ op selfDestruct, inline = false:
|
||||||
|
|
||||||
computation.vmState.mutateStateDB:
|
computation.vmState.mutateStateDB:
|
||||||
let
|
let
|
||||||
local_balance = db.get_balance(computation.msg.storage_address)
|
local_balance = db.getBalance(computation.msg.storage_address)
|
||||||
beneficiary_balance = db.get_balance(beneficiary)
|
beneficiary_balance = db.getBalance(beneficiary)
|
||||||
|
|
||||||
# Transfer to beneficiary
|
# Transfer to beneficiary
|
||||||
db.setBalance(beneficiary, local_balance + beneficiary_balance)
|
db.setBalance(beneficiary, local_balance + beneficiary_balance)
|
||||||
|
|
|
@ -12,14 +12,6 @@ import
|
||||||
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/[db_chain, state_db], ./utils/header,
|
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/[db_chain, state_db], ./utils/header,
|
||||||
./vm/interpreter, ./vm/interpreter/gas_costs, ./utils/addresses
|
./vm/interpreter, ./vm/interpreter/gas_costs, ./utils/addresses
|
||||||
|
|
||||||
func intrinsicGas*(data: openarray[byte]): GasInt =
|
|
||||||
result = 21_000
|
|
||||||
for i in data:
|
|
||||||
if i == 0:
|
|
||||||
result += 4
|
|
||||||
else:
|
|
||||||
result += 68
|
|
||||||
|
|
||||||
proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender: EthAddress): bool =
|
proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender: EthAddress): bool =
|
||||||
# XXX: https://github.com/status-im/nimbus/issues/35#issuecomment-391726518
|
# XXX: https://github.com/status-im/nimbus/issues/35#issuecomment-391726518
|
||||||
# XXX: lots of avoidable u256 construction
|
# XXX: lots of avoidable u256 construction
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
# * 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)
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
@ -17,4 +17,5 @@ import ./test_code_stream,
|
||||||
./test_precompiles,
|
./test_precompiles,
|
||||||
./test_generalstate_json,
|
./test_generalstate_json,
|
||||||
./test_tracer_json,
|
./test_tracer_json,
|
||||||
./test_persistblock_json
|
./test_persistblock_json,
|
||||||
|
./test_rpc
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018-2019 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.
|
||||||
|
|
||||||
## This module contains signatures for the Ethereum client RPCs.
|
## This module contains signatures for the Ethereum client RPCs.
|
||||||
## The signatures are not imported directly, but read and processed with parseStmt,
|
## The signatures are not imported directly, but read and processed with parseStmt,
|
||||||
## then a procedure body is generated to marshal native Nim parameters to json and visa versa.
|
## then a procedure body is generated to marshal native Nim parameters to json and visa versa.
|
||||||
import json, stint, eth_common, ../../nimbus/rpc/hexstrings
|
import
|
||||||
|
json,
|
||||||
|
stint, eth_common,
|
||||||
|
../../nimbus/rpc/hexstrings, ../../nimbus/rpc/rpc_types
|
||||||
|
|
||||||
proc web3_clientVersion(): string
|
proc web3_clientVersion(): string
|
||||||
proc web3_sha3(data: string): string
|
proc web3_sha3(data: string): string
|
||||||
|
@ -27,11 +37,11 @@ proc eth_getCode(data: EthAddressStr, quantityTag: string): HexDataStr
|
||||||
proc eth_sign(data:EthAddressStr, message: HexDataStr): HexDataStr
|
proc eth_sign(data:EthAddressStr, message: HexDataStr): HexDataStr
|
||||||
#proc eth_sendRawTransaction(data: string, quantityTag: int): UInt256
|
#proc eth_sendRawTransaction(data: string, quantityTag: int): UInt256
|
||||||
proc eth_call(call: EthCall, quantityTag: string): string
|
proc eth_call(call: EthCall, quantityTag: string): string
|
||||||
|
proc eth_estimateGas(call: EthCall, quantityTag: string): GasInt
|
||||||
|
|
||||||
# TODO: Use eth_common types
|
# TODO: Use eth_common types
|
||||||
|
|
||||||
#[proc eth_sendTransaction(obj: EthSend): UInt256
|
#[proc eth_sendTransaction(obj: EthSend): UInt256
|
||||||
proc eth_estimateGas(call: EthCall, quantityTag: string): UInt256
|
|
||||||
proc eth_getBlockByHash(data: array[32, byte], fullTransactions: bool): BlockObject
|
proc eth_getBlockByHash(data: array[32, byte], fullTransactions: bool): BlockObject
|
||||||
proc eth_getBlockByNumber(quantityTag: string, fullTransactions: bool): BlockObject
|
proc eth_getBlockByNumber(quantityTag: string, fullTransactions: bool): BlockObject
|
||||||
proc eth_getTransactionByHash(data: Uint256): TransactionObject
|
proc eth_getTransactionByHash(data: Uint256): TransactionObject
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018-2019 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.
|
||||||
|
|
||||||
# Separated from main tests for brevity
|
# Separated from main tests for brevity
|
||||||
|
|
||||||
import unittest, ../../nimbus/rpc/hexstrings, json
|
import unittest, ../../nimbus/rpc/hexstrings, json
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018-2019 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
|
import
|
||||||
unittest, json, strformat, nimcrypto, rlp, options,
|
unittest, json, strformat, options,
|
||||||
|
nimcrypto, rlp, eth_trie/db, eth_p2p, eth_keys,
|
||||||
json_rpc/[rpcserver, rpcclient],
|
json_rpc/[rpcserver, rpcclient],
|
||||||
../nimbus/rpc/[common, p2p, hexstrings, rpc_types],
|
../nimbus/rpc/[common, p2p, hexstrings, rpc_types],
|
||||||
../nimbus/constants,
|
../nimbus/constants,
|
||||||
../nimbus/nimbus/[vm_state, config],
|
../nimbus/[vm_state, config],
|
||||||
../nimbus/db/[state_db, db_chain], eth_common, byteutils,
|
../nimbus/db/[state_db, db_chain, storage_types], eth_common, byteutils,
|
||||||
../nimbus/p2p/chain,
|
../nimbus/p2p/chain,
|
||||||
../nimbus/genesis,
|
../nimbus/genesis,
|
||||||
eth_trie/db,
|
./rpcclient/test_hexstrings
|
||||||
eth_p2p, eth_keys
|
|
||||||
import rpcclient/test_hexstrings
|
|
||||||
|
|
||||||
# Perform checks for hex string validation
|
# Perform checks for hex string validation
|
||||||
doHexStrTests()
|
doHexStrTests()
|
||||||
|
@ -58,34 +64,41 @@ proc doTests =
|
||||||
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
|
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
|
||||||
state.mutateStateDB:
|
state.mutateStateDB:
|
||||||
db.setBalance(address, balance)
|
db.setBalance(address, balance)
|
||||||
|
doAssert(canonicalHeadHashKey().toOpenArray in state.chainDb.db)
|
||||||
|
|
||||||
# Create Ethereum RPCs
|
# Create Ethereum RPCs
|
||||||
|
let RPC_PORT = 8545
|
||||||
var
|
var
|
||||||
rpcServer = newRpcSocketServer(["localhost:8545"])
|
rpcServer = newRpcSocketServer(["localhost:" & $RPC_PORT])
|
||||||
client = newRpcSocketClient()
|
client = newRpcSocketClient()
|
||||||
setupCommonRpc(rpcServer)
|
setupCommonRpc(rpcServer)
|
||||||
setupEthRpc(ethNode, chain, rpcServer)
|
setupEthRpc(ethNode, chain, rpcServer)
|
||||||
|
|
||||||
# Begin tests
|
# Begin tests
|
||||||
rpcServer.start()
|
rpcServer.start()
|
||||||
waitFor client.connect("localhost", Port(8545))
|
waitFor client.connect("localhost", Port(RPC_PORT))
|
||||||
|
|
||||||
|
# TODO: add more tests here
|
||||||
suite "Remote Procedure Calls":
|
suite "Remote Procedure Calls":
|
||||||
# TODO: Currently returning 'block not found' when fetching header in p2p, so cannot perform tests
|
|
||||||
test "eth_call":
|
test "eth_call":
|
||||||
let
|
let
|
||||||
blockNum = state.blockheader.blockNumber
|
blockNum = state.blockheader.blockNumber
|
||||||
callParams = EthCall(value: some(100.u256))
|
callParams = EthCall(value: some(100.u256))
|
||||||
var r = waitFor client.eth_call(callParams, "0x" & blockNum.toHex)
|
r1 = waitFor client.eth_call(callParams, "0x" & blockNum.toHex)
|
||||||
echo r
|
check r1 == "0x"
|
||||||
test "eth_getBalance":
|
test "eth_getBalance":
|
||||||
expect ValueError:
|
let r2 = waitFor client.eth_getBalance(ZERO_ADDRESS.toEthAddressStr, "0x0")
|
||||||
# check error is raised on null address
|
check r2 == 0
|
||||||
var r = waitFor client.eth_getBalance(ZERO_ADDRESS.toEthAddressStr, "0x0")
|
|
||||||
|
|
||||||
let blockNum = state.blockheader.blockNumber
|
let blockNum = state.blockheader.blockNumber
|
||||||
var r = waitFor client.eth_getBalance(address.toEthAddressStr, "0x" & blockNum.toHex)
|
let r3 = waitFor client.eth_getBalance(address.toEthAddressStr, "0x" & blockNum.toHex)
|
||||||
echo r
|
check r3 == 0
|
||||||
|
test "eth_estimateGas":
|
||||||
|
let
|
||||||
|
call = EthCall()
|
||||||
|
blockNum = state.blockheader.blockNumber
|
||||||
|
r4 = waitFor client.eth_estimateGas(call, "0x" & blockNum.toHex)
|
||||||
|
check r4 == 21_000
|
||||||
|
|
||||||
rpcServer.stop()
|
rpcServer.stop()
|
||||||
rpcServer.close()
|
rpcServer.close()
|
||||||
|
|
Loading…
Reference in New Issue