2021-04-30 09:33:14 +00:00
|
|
|
# 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
|
2021-05-17 09:23:59 +00:00
|
|
|
eth/common/eth_types, stint, options, stew/byteutils, stew/ranges/ptr_arith,
|
2021-05-03 09:04:15 +00:00
|
|
|
".."/[vm_types, vm_types2, vm_state, vm_computation, utils],
|
2021-05-03 08:08:11 +00:00
|
|
|
".."/[db/db_chain, config, vm_state_transactions, rpc/hexstrings],
|
2021-05-03 18:19:34 +00:00
|
|
|
".."/[db/accounts_cache, transaction, vm_precompiles, vm_gas_costs], eth/trie/db,
|
2021-05-17 09:23:59 +00:00
|
|
|
".."/vm_internals,
|
|
|
|
./host_types
|
|
|
|
|
|
|
|
proc hostToComputationMessage(msg: EvmcMessage): Message =
|
|
|
|
Message(
|
|
|
|
kind: CallKind(msg.kind),
|
|
|
|
depth: msg.depth,
|
|
|
|
gas: msg.gas,
|
|
|
|
sender: msg.sender.fromEvmc,
|
|
|
|
contractAddress: msg.destination.fromEvmc,
|
|
|
|
codeAddress: msg.destination.fromEvmc,
|
|
|
|
value: msg.value.fromEvmc,
|
|
|
|
# When input size is zero, input data pointer may be null.
|
|
|
|
data: if msg.input_size <= 0: @[]
|
|
|
|
else: @(makeOpenArray(msg.input_data, msg.input_size.int)),
|
|
|
|
flags: if msg.isStatic: emvcStatic else: emvcNoFlags
|
|
|
|
)
|
|
|
|
|
|
|
|
type
|
|
|
|
CallParams = object
|
|
|
|
vmState: BaseVMState
|
|
|
|
forkOverride: Option[Fork]
|
|
|
|
origin: Option[HostAddress]
|
|
|
|
gasPrice: GasInt
|
|
|
|
gasLimit: GasInt
|
|
|
|
sender: HostAddress
|
|
|
|
to: HostAddress
|
|
|
|
isCreate: bool
|
|
|
|
value: HostValue
|
|
|
|
input: seq[byte]
|
|
|
|
|
|
|
|
proc setupCall(call: CallParams): TransactionHost =
|
|
|
|
let vmState = call.vmState
|
|
|
|
vmState.setupTxContext(
|
|
|
|
origin = call.origin.get(call.sender),
|
|
|
|
gasPrice = call.gasPrice,
|
|
|
|
forkOverride = call.forkOverride
|
|
|
|
)
|
|
|
|
|
|
|
|
let host = TransactionHost(
|
|
|
|
vmState: vmState,
|
|
|
|
msg: EvmcMessage(
|
|
|
|
kind: if call.isCreate: EVMC_CREATE else: EVMC_CALL,
|
|
|
|
# Default: flags: {},
|
|
|
|
# Default: depth: 0,
|
|
|
|
gas: call.gasLimit,
|
|
|
|
destination: call.to.toEvmc,
|
|
|
|
sender: call.sender.toEvmc,
|
|
|
|
value: call.value.toEvmc,
|
|
|
|
)
|
|
|
|
# All other defaults in `TransactionHost` are fine.
|
|
|
|
)
|
|
|
|
|
|
|
|
if call.input.len > 0:
|
|
|
|
host.msg.input_size = call.input.len.csize_t
|
|
|
|
# Must copy the data so the `host.msg.input_data` pointer
|
|
|
|
# remains valid after the end of `call` lifetime.
|
|
|
|
host.input = call.input
|
|
|
|
host.msg.input_data = host.input[0].addr
|
|
|
|
|
|
|
|
let cMsg = hostToComputationMessage(host.msg)
|
|
|
|
host.computation = newComputation(vmState, cMsg)
|
|
|
|
return host
|
|
|
|
|
|
|
|
proc setupComputation(call: CallParams): Computation =
|
|
|
|
return setupCall(call).computation
|
2021-04-30 09:33:14 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
RpcCallData* = object
|
|
|
|
source*: EthAddress
|
|
|
|
to*: EthAddress
|
|
|
|
gas*: GasInt
|
|
|
|
gasPrice*: GasInt
|
|
|
|
value*: UInt256
|
|
|
|
data*: seq[byte]
|
|
|
|
contractCreation*: bool
|
|
|
|
|
2021-05-17 10:03:39 +00:00
|
|
|
proc rpcSetupComputation(vmState: BaseVMState, rpc: RpcCallData,
|
2021-05-04 23:55:04 +00:00
|
|
|
gasLimit: GasInt, forkOverride = none(Fork)): Computation =
|
2021-05-17 10:03:39 +00:00
|
|
|
return setupComputation(CallParams(
|
|
|
|
vmState: vmState,
|
|
|
|
forkOverride: forkOverride,
|
|
|
|
gasPrice: rpc.gasPrice,
|
|
|
|
gasLimit: gasLimit,
|
|
|
|
sender: rpc.source,
|
|
|
|
to: rpc.to,
|
|
|
|
isCreate: rpc.contractCreation,
|
|
|
|
value: rpc.value,
|
|
|
|
input: rpc.data
|
|
|
|
))
|
2021-05-03 08:08:03 +00:00
|
|
|
|
|
|
|
proc rpcDoCall*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB): HexDataStr =
|
|
|
|
# TODO: handle revert and error
|
|
|
|
# TODO: handle contract ABI
|
|
|
|
var
|
|
|
|
# we use current header stateRoot, unlike block validation
|
|
|
|
# which use previous block stateRoot
|
|
|
|
vmState = newBaseVMState(header.stateRoot, header, chain)
|
2021-05-04 23:55:04 +00:00
|
|
|
comp = rpcSetupComputation(vmState, call, call.gas)
|
2021-05-03 08:08:03 +00:00
|
|
|
|
|
|
|
comp.execComputation()
|
|
|
|
result = hexDataStr(comp.output)
|
2021-05-03 08:08:11 +00:00
|
|
|
|
2021-05-03 08:07:56 +00:00
|
|
|
proc rpcMakeCall*(call: RpcCallData, header: BlockHeader, chain: BaseChainDB): (string, GasInt, bool) =
|
|
|
|
# TODO: handle revert
|
|
|
|
var
|
2021-05-04 06:56:10 +00:00
|
|
|
parent = chain.getBlockHeader(header.parentHash)
|
|
|
|
vmState = newBaseVMState(parent.stateRoot, header, chain)
|
|
|
|
fork = toFork(chain.config, header.blockNumber)
|
|
|
|
comp = rpcSetupComputation(vmState, call, call.gas, some(fork))
|
2021-05-03 08:07:56 +00:00
|
|
|
|
|
|
|
let gas = comp.gasMeter.gasRemaining
|
|
|
|
comp.execComputation()
|
|
|
|
return (comp.output.toHex, gas - comp.gasMeter.gasRemaining, comp.isError)
|
|
|
|
|
RPC: Change rpcEstimateGas to use the EVM without a fake transaction
Change `rpcEstimateGas` to setup and execute a computation directly, in a
similar way to `rpcDoCall` and `rpcMakeCall`, instead of constructing a fake
transaction and then validating it.
This patch does not (or should not) change any behaviour.
Although this looks a bit messy as it duplicates parts of `validateTransaction`
and `processTransaction`, proc names have been used to hopefully keep the
meanings clear, and it's just a stepping stone as those transaction functions
will be changed next. Also the behaviour of RPC `estimateGas` may not be
correct (although this patch is careful not to change it), so it's good to make
it explicit so we can see how it differs from other RPCs.
Doing this change exposed some interesting behaviour differences between the
`call` RPC and `estimateGas` RPC, which may be bugs, or may be intentional.
These differences are now obvious and explicit.
The unclear areas are not well documented by any of the clients, even Infura
which says a bit more than the others. So to find out if they are intended,
we'll have to run tests against other Ethereum services.
Guessing, on the face of it, it looks likely that RPC `call` should:
- Setup EIP2929 access lists
- Account for intrinsic gas (maybe not because zero-gas transactions are ok)
And it looks likely that RPC `estimateGas` should:
- Not return zero when an account has insufficient balance
- Maybe use a different gas cost estimate when one isn't supplied in the RPC
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-03 06:58:45 +00:00
|
|
|
func rpcIntrinsicGas(call: RpcCallData, fork: Fork): GasInt =
|
|
|
|
var intrinsicGas = call.data.intrinsicGas(fork)
|
|
|
|
if call.contractCreation:
|
|
|
|
intrinsicGas = intrinsicGas + gasFees[fork][GasTXCreate]
|
|
|
|
return intrinsicGas
|
|
|
|
|
|
|
|
func rpcValidateCall(call: RpcCallData, vmState: BaseVMState, gasLimit: GasInt,
|
|
|
|
fork: Fork, intrinsicGas: var GasInt, gasCost: var UInt256): bool =
|
|
|
|
# This behaviour matches `validateTransaction`, used by `processTransaction`.
|
|
|
|
if vmState.cumulativeGasUsed + gasLimit > vmState.blockHeader.gasLimit:
|
|
|
|
return false
|
|
|
|
let balance = vmState.readOnlyStateDB.getBalance(call.source)
|
|
|
|
gasCost = gasLimit.u256 * call.gasPrice.u256
|
|
|
|
if gasCost > balance or call.value > balance - gasCost:
|
|
|
|
return false
|
|
|
|
intrinsicGas = rpcIntrinsicGas(call, fork)
|
|
|
|
if intrinsicGas > gasLimit:
|
|
|
|
return false
|
|
|
|
return true
|
|
|
|
|
|
|
|
proc rpcInitialAccessListEIP2929(call: RpcCallData, vmState: BaseVMState, fork: Fork) =
|
|
|
|
# EIP2929 initial access list.
|
|
|
|
if fork >= FkBerlin:
|
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.accessList(call.source)
|
|
|
|
# For contract creations the EVM will add the contract address to the
|
|
|
|
# access list itself, after calculating the new contract address.
|
|
|
|
if not call.contractCreation:
|
|
|
|
db.accessList(call.to)
|
|
|
|
for c in activePrecompiles():
|
|
|
|
db.accessList(c)
|
|
|
|
|
2021-05-03 08:08:11 +00:00
|
|
|
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)
|
RPC: Change rpcEstimateGas to use the EVM without a fake transaction
Change `rpcEstimateGas` to setup and execute a computation directly, in a
similar way to `rpcDoCall` and `rpcMakeCall`, instead of constructing a fake
transaction and then validating it.
This patch does not (or should not) change any behaviour.
Although this looks a bit messy as it duplicates parts of `validateTransaction`
and `processTransaction`, proc names have been used to hopefully keep the
meanings clear, and it's just a stepping stone as those transaction functions
will be changed next. Also the behaviour of RPC `estimateGas` may not be
correct (although this patch is careful not to change it), so it's good to make
it explicit so we can see how it differs from other RPCs.
Doing this change exposed some interesting behaviour differences between the
`call` RPC and `estimateGas` RPC, which may be bugs, or may be intentional.
These differences are now obvious and explicit.
The unclear areas are not well documented by any of the clients, even Infura
which says a bit more than the others. So to find out if they are intended,
we'll have to run tests against other Ethereum services.
Guessing, on the face of it, it looks likely that RPC `call` should:
- Setup EIP2929 access lists
- Account for intrinsic gas (maybe not because zero-gas transactions are ok)
And it looks likely that RPC `estimateGas` should:
- Not return zero when an account has insufficient balance
- Maybe use a different gas cost estimate when one isn't supplied in the RPC
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-03 06:58:45 +00:00
|
|
|
gasLimit = if haveGasLimit: call.gas else: header.gasLimit - vmState.cumulativeGasUsed
|
|
|
|
intrinsicGas: GasInt
|
|
|
|
gasCost: UInt256
|
|
|
|
|
|
|
|
# Nimbus `estimateGas` has historically checked against remaining gas in the
|
|
|
|
# current block, balance in the sender account (even if the sender is default
|
|
|
|
# account 0x00), and other limits, and returned 0 as the gas estimate if any
|
|
|
|
# checks failed. This behaviour came from how it used `processTransaction`
|
|
|
|
# which calls `validateTransaction`. For now, keep this behaviour the same.
|
|
|
|
# Compare this code with `validateTransaction`.
|
|
|
|
#
|
|
|
|
# TODO: This historically differs from `rpcDoCall` and `rpcMakeCall`. There
|
|
|
|
# are other differences in rpc_utils.nim `callData` too. Are the different
|
|
|
|
# behaviours intended, and is 0 the correct return value to mean "not enough
|
|
|
|
# gas to start"? Probably not.
|
|
|
|
if not rpcValidateCall(call, vmState, gasLimit, fork, intrinsicGas, gasCost):
|
|
|
|
return 0
|
2021-05-03 08:08:11 +00:00
|
|
|
|
|
|
|
var dbTx = chain.db.beginTransaction()
|
|
|
|
defer: dbTx.dispose()
|
RPC: Change rpcEstimateGas to use the EVM without a fake transaction
Change `rpcEstimateGas` to setup and execute a computation directly, in a
similar way to `rpcDoCall` and `rpcMakeCall`, instead of constructing a fake
transaction and then validating it.
This patch does not (or should not) change any behaviour.
Although this looks a bit messy as it duplicates parts of `validateTransaction`
and `processTransaction`, proc names have been used to hopefully keep the
meanings clear, and it's just a stepping stone as those transaction functions
will be changed next. Also the behaviour of RPC `estimateGas` may not be
correct (although this patch is careful not to change it), so it's good to make
it explicit so we can see how it differs from other RPCs.
Doing this change exposed some interesting behaviour differences between the
`call` RPC and `estimateGas` RPC, which may be bugs, or may be intentional.
These differences are now obvious and explicit.
The unclear areas are not well documented by any of the clients, even Infura
which says a bit more than the others. So to find out if they are intended,
we'll have to run tests against other Ethereum services.
Guessing, on the face of it, it looks likely that RPC `call` should:
- Setup EIP2929 access lists
- Account for intrinsic gas (maybe not because zero-gas transactions are ok)
And it looks likely that RPC `estimateGas` should:
- Not return zero when an account has insufficient balance
- Maybe use a different gas cost estimate when one isn't supplied in the RPC
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-03 06:58:45 +00:00
|
|
|
|
|
|
|
# TODO: EIP2929 setup also historically differs from `rpcDoCall` and `rpcMakeCall`.
|
|
|
|
rpcInitialAccessListEIP2929(call, vmState, fork)
|
|
|
|
|
|
|
|
# TODO: Deduction of `intrinsicGas` also differs from `rpcDoCall` and `rpcMakeCall`.
|
2021-05-04 23:55:04 +00:00
|
|
|
var c = rpcSetupComputation(vmState, call, gasLimit - intrinsicGas, some(fork))
|
RPC: Change rpcEstimateGas to use the EVM without a fake transaction
Change `rpcEstimateGas` to setup and execute a computation directly, in a
similar way to `rpcDoCall` and `rpcMakeCall`, instead of constructing a fake
transaction and then validating it.
This patch does not (or should not) change any behaviour.
Although this looks a bit messy as it duplicates parts of `validateTransaction`
and `processTransaction`, proc names have been used to hopefully keep the
meanings clear, and it's just a stepping stone as those transaction functions
will be changed next. Also the behaviour of RPC `estimateGas` may not be
correct (although this patch is careful not to change it), so it's good to make
it explicit so we can see how it differs from other RPCs.
Doing this change exposed some interesting behaviour differences between the
`call` RPC and `estimateGas` RPC, which may be bugs, or may be intentional.
These differences are now obvious and explicit.
The unclear areas are not well documented by any of the clients, even Infura
which says a bit more than the others. So to find out if they are intended,
we'll have to run tests against other Ethereum services.
Guessing, on the face of it, it looks likely that RPC `call` should:
- Setup EIP2929 access lists
- Account for intrinsic gas (maybe not because zero-gas transactions are ok)
And it looks likely that RPC `estimateGas` should:
- Not return zero when an account has insufficient balance
- Maybe use a different gas cost estimate when one isn't supplied in the RPC
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-03 06:58:45 +00:00
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.subBalance(call.source, gasCost)
|
|
|
|
|
|
|
|
execComputation(c)
|
|
|
|
|
|
|
|
if c.shouldBurnGas:
|
|
|
|
return gasLimit
|
|
|
|
let maxRefund = (gasLimit - c.gasMeter.gasRemaining) div 2
|
|
|
|
let refund = min(c.getGasRefund(), maxRefund)
|
|
|
|
return gasLimit - c.gasMeter.gasRemaining - refund
|
2021-05-03 16:50:47 +00:00
|
|
|
|
2021-05-03 18:14:54 +00:00
|
|
|
proc txSetupComputation(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork): Computation =
|
2021-05-03 16:50:47 +00:00
|
|
|
var gas = tx.gasLimit - tx.intrinsicGas(fork)
|
|
|
|
assert gas >= 0
|
2021-05-17 09:36:34 +00:00
|
|
|
return setupComputation(CallParams(
|
|
|
|
vmState: vmState,
|
|
|
|
forkOverride: some(fork),
|
|
|
|
gasPrice: tx.gasPrice,
|
|
|
|
gasLimit: gas,
|
|
|
|
sender: sender,
|
|
|
|
to: tx.to,
|
|
|
|
isCreate: tx.isContractCreation,
|
|
|
|
value: tx.value,
|
|
|
|
input: tx.payload
|
|
|
|
))
|
2021-05-03 18:19:34 +00:00
|
|
|
|
2021-05-03 18:14:54 +00:00
|
|
|
proc txRefundgas(tx: Transaction, sender: EthAddress, c: Computation) =
|
2021-05-03 18:19:34 +00:00
|
|
|
let maxRefund = (tx.gasLimit - c.gasMeter.gasRemaining) div 2
|
|
|
|
let refund = min(c.getGasRefund(), maxRefund)
|
|
|
|
c.gasMeter.returnGas(refund)
|
|
|
|
c.vmState.mutateStateDB:
|
|
|
|
db.addBalance(sender, c.gasMeter.gasRemaining.u256 * tx.gasPrice.u256)
|
2021-05-03 18:14:54 +00:00
|
|
|
|
|
|
|
proc txInitialAccessListEIP2929(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork) =
|
|
|
|
# EIP2929 initial access list.
|
|
|
|
if fork >= FkBerlin:
|
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.accessList(sender)
|
|
|
|
# For contract creations the EVM will add the contract address to the
|
|
|
|
# access list itself, after calculating the new contract address.
|
|
|
|
if not tx.isContractCreation:
|
2021-05-04 06:27:18 +00:00
|
|
|
db.accessList(tx.getRecipient(sender))
|
2021-05-03 18:14:54 +00:00
|
|
|
for c in activePrecompiles():
|
|
|
|
db.accessList(c)
|
|
|
|
|
2021-05-15 13:54:34 +00:00
|
|
|
# EIP2930 optional access list
|
|
|
|
if tx.txType == AccessListTxType:
|
|
|
|
for n in tx.accessListTx.accessList:
|
|
|
|
db.accessList(n.address)
|
|
|
|
for x in n.storageKeys:
|
|
|
|
db.accessList(n.address, UInt256.fromBytesBE(x))
|
|
|
|
|
2021-05-03 18:14:54 +00:00
|
|
|
proc txCallEvm*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork): GasInt =
|
|
|
|
txInitialAccessListEIP2929(tx, sender, vmState, fork)
|
|
|
|
|
|
|
|
var c = txSetupComputation(tx, sender, vmState, fork)
|
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.subBalance(sender, tx.gasLimit.u256 * tx.gasPrice.u256)
|
|
|
|
|
|
|
|
execComputation(c)
|
|
|
|
|
|
|
|
if c.shouldBurnGas:
|
|
|
|
return tx.gasLimit
|
|
|
|
txRefundGas(tx, sender, c)
|
|
|
|
return tx.gasLimit - c.gasMeter.gasRemaining
|
2021-05-03 19:35:29 +00:00
|
|
|
|
2021-05-04 23:55:04 +00:00
|
|
|
proc asmSetupComputation(blockNumber: Uint256, chainDB: BaseChainDB, code,
|
|
|
|
data: seq[byte], forkOverride = none(Fork)): Computation =
|
2021-05-03 20:31:45 +00:00
|
|
|
let
|
|
|
|
parentNumber = blockNumber - 1
|
|
|
|
parent = chainDB.getBlockHeader(parentNumber)
|
|
|
|
header = chainDB.getBlockHeader(blockNumber)
|
|
|
|
headerHash = header.blockHash
|
|
|
|
body = chainDB.getBlockBody(headerHash)
|
|
|
|
vmState = newBaseVMState(parent.stateRoot, header, chainDB)
|
|
|
|
tx = body.transactions[0]
|
|
|
|
sender = transaction.getSender(tx)
|
2021-05-17 10:02:22 +00:00
|
|
|
gasLimit = 500000000
|
|
|
|
gasUsed = 0 #tx.payload.intrinsicGas.GasInt + gasFees[fork][GasTXCreate]
|
|
|
|
|
|
|
|
# This is an odd sort of test, where some fields are taken from
|
|
|
|
# `body.transactions[0]` but other fields (like `gasLimit`) are not. Also it
|
|
|
|
# creates the new contract using `code` like `CREATE`, but then executes the
|
|
|
|
# contract like it's `CALL`.
|
|
|
|
|
|
|
|
doAssert tx.isContractCreation
|
|
|
|
let contractAddress = generateAddress(sender, vmState.readOnlyStateDB.getNonce(sender))
|
|
|
|
vmState.mutateStateDB:
|
|
|
|
db.setCode(contractAddress, code)
|
2021-05-03 20:31:45 +00:00
|
|
|
|
2021-05-17 10:02:22 +00:00
|
|
|
return setupComputation(CallParams(
|
|
|
|
vmState: vmState,
|
|
|
|
forkOverride: forkOverride,
|
|
|
|
gasPrice: tx.gasPrice,
|
|
|
|
gasLimit: gasLimit - gasUsed,
|
|
|
|
sender: sender,
|
|
|
|
to: contractAddress,
|
|
|
|
isCreate: false,
|
|
|
|
value: tx.value,
|
|
|
|
input: data
|
|
|
|
))
|
2021-05-03 23:34:51 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
AsmResult* = object
|
|
|
|
isSuccess*: bool
|
|
|
|
gasUsed*: GasInt
|
|
|
|
output*: seq[byte]
|
|
|
|
stack*: Stack
|
|
|
|
memory*: Memory
|
|
|
|
vmState*: BaseVMState
|
|
|
|
contractAddress*: EthAddress
|
|
|
|
|
|
|
|
proc asmCallEvm*(blockNumber: Uint256, chainDB: BaseChainDB, code, data: seq[byte], fork: Fork): AsmResult =
|
2021-05-04 23:55:04 +00:00
|
|
|
var c = asmSetupComputation(blockNumber, chainDB, code, data, some(fork))
|
2021-05-03 23:34:51 +00:00
|
|
|
let gas = c.gasMeter.gasRemaining
|
|
|
|
c.execComputation()
|
|
|
|
|
|
|
|
# Some of these are extra returned state, for testing, that a normal EVMC API
|
|
|
|
# computation doesn't return. We'll have to obtain them outside EVMC.
|
|
|
|
result.isSuccess = c.isSuccess
|
|
|
|
result.gasUsed = gas - c.gasMeter.gasRemaining
|
|
|
|
result.output = c.output
|
|
|
|
result.stack = c.stack
|
|
|
|
result.memory = c.memory
|
|
|
|
result.vmState = c.vmState
|
|
|
|
result.contractAddress = c.msg.contractAddress
|
2021-05-04 04:54:39 +00:00
|
|
|
|
2021-05-04 23:55:04 +00:00
|
|
|
proc fixtureSetupComputation(vmState: BaseVMState, call: RpcCallData,
|
|
|
|
origin: EthAddress, forkOverride = none(Fork)): Computation =
|
2021-05-17 10:04:38 +00:00
|
|
|
return setupComputation(CallParams(
|
|
|
|
vmState: vmState,
|
|
|
|
forkOverride: forkOverride,
|
|
|
|
origin: some(origin),
|
|
|
|
gasPrice: call.gasPrice,
|
|
|
|
gasLimit: call.gas, # Differs from `rpcSetupComputation`
|
|
|
|
sender: call.source,
|
|
|
|
to: call.to,
|
|
|
|
isCreate: call.contractCreation,
|
|
|
|
value: call.value,
|
|
|
|
input: call.data
|
|
|
|
))
|
2021-05-04 14:02:16 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
FixtureResult* = object
|
|
|
|
isError*: bool
|
|
|
|
error*: Error
|
Precompiles: Change precompile tests to use fixtureCallEvm
Move the EVM setup and call in precompile tests to `fixtureCallEvm` in
`call_evm`. Extra return values needed for testing are returned specially, and
the convention for reporting gas used is changed to match `asmCallEvm`.
Although the precompile tests used `execPrecompiles` before, `executeOpcodes`
does perfectly well as a substitute, allowing `fixtureCallEvm` to be shared.
_Significantly, this patch also makes `Computation` more or less an internal
type of the EVM now._
Nothing outside the EVM (except `call_evm`) needs access any more to
`Computation`, `execComputation`, `executeOpcodes` or `execPrecompiles`.
Many imports can be trimmed, some files removed, and EVMC is much closer.
(As a bonus, the functions in `call_evm` reveal what capabilities parts of the
program have needed over time, makes certain bugs and inconsistencies clearer,
and suggests how to refactor into a more useful shared entry point.)
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-05 00:55:49 +00:00
|
|
|
gasUsed*: GasInt
|
2021-05-04 14:02:16 +00:00
|
|
|
output*: seq[byte]
|
|
|
|
vmState*: BaseVMState
|
|
|
|
logEntries*: seq[Log]
|
|
|
|
|
2021-05-04 23:55:04 +00:00
|
|
|
proc fixtureCallEvm*(vmState: BaseVMState, call: RpcCallData,
|
|
|
|
origin: EthAddress, forkOverride = none(Fork)): FixtureResult =
|
|
|
|
var c = fixtureSetupComputation(vmState, call, origin, forkOverride)
|
Precompiles: Change precompile tests to use fixtureCallEvm
Move the EVM setup and call in precompile tests to `fixtureCallEvm` in
`call_evm`. Extra return values needed for testing are returned specially, and
the convention for reporting gas used is changed to match `asmCallEvm`.
Although the precompile tests used `execPrecompiles` before, `executeOpcodes`
does perfectly well as a substitute, allowing `fixtureCallEvm` to be shared.
_Significantly, this patch also makes `Computation` more or less an internal
type of the EVM now._
Nothing outside the EVM (except `call_evm`) needs access any more to
`Computation`, `execComputation`, `executeOpcodes` or `execPrecompiles`.
Many imports can be trimmed, some files removed, and EVMC is much closer.
(As a bonus, the functions in `call_evm` reveal what capabilities parts of the
program have needed over time, makes certain bugs and inconsistencies clearer,
and suggests how to refactor into a more useful shared entry point.)
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-05 00:55:49 +00:00
|
|
|
let gas = c.gasMeter.gasRemaining
|
2021-05-04 14:02:16 +00:00
|
|
|
|
|
|
|
# Next line differs from all the other EVM calls. With `execComputation`,
|
|
|
|
# most "vm json tests" fail with either `balanceDiff` or `nonceDiff` errors.
|
|
|
|
c.executeOpcodes()
|
2021-05-14 14:35:23 +00:00
|
|
|
doAssert c.continuation.isNil
|
|
|
|
doAssert c.child.isNil
|
2021-05-04 14:02:16 +00:00
|
|
|
|
|
|
|
# Some of these are extra returned state, for testing, that a normal EVMC API
|
|
|
|
# computation doesn't return. We'll have to obtain them outside EVMC.
|
|
|
|
result.isError = c.isError
|
|
|
|
result.error = c.error
|
Precompiles: Change precompile tests to use fixtureCallEvm
Move the EVM setup and call in precompile tests to `fixtureCallEvm` in
`call_evm`. Extra return values needed for testing are returned specially, and
the convention for reporting gas used is changed to match `asmCallEvm`.
Although the precompile tests used `execPrecompiles` before, `executeOpcodes`
does perfectly well as a substitute, allowing `fixtureCallEvm` to be shared.
_Significantly, this patch also makes `Computation` more or less an internal
type of the EVM now._
Nothing outside the EVM (except `call_evm`) needs access any more to
`Computation`, `execComputation`, `executeOpcodes` or `execPrecompiles`.
Many imports can be trimmed, some files removed, and EVMC is much closer.
(As a bonus, the functions in `call_evm` reveal what capabilities parts of the
program have needed over time, makes certain bugs and inconsistencies clearer,
and suggests how to refactor into a more useful shared entry point.)
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-05 00:55:49 +00:00
|
|
|
result.gasUsed = gas - c.gasMeter.gasRemaining
|
2021-05-04 14:02:16 +00:00
|
|
|
result.output = c.output
|
|
|
|
result.vmState = c.vmState
|
|
|
|
shallowCopy(result.logEntries, c.logEntries)
|