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>
This commit is contained in:
Jamie Lokier 2021-05-05 01:55:49 +01:00
parent 751068a4d4
commit 1574136a25
No known key found for this signature in database
GPG Key ID: CBC25C68435C30A2
3 changed files with 24 additions and 36 deletions

View File

@ -302,7 +302,7 @@ type
FixtureResult* = object
isError*: bool
error*: Error
gasRemaining*: GasInt
gasUsed*: GasInt
output*: seq[byte]
vmState*: BaseVMState
logEntries*: seq[Log]
@ -310,6 +310,7 @@ type
proc fixtureCallEvm*(vmState: BaseVMState, call: RpcCallData,
origin: EthAddress, forkOverride = none(Fork)): FixtureResult =
var c = fixtureSetupComputation(vmState, call, origin, forkOverride)
let gas = c.gasMeter.gasRemaining
# Next line differs from all the other EVM calls. With `execComputation`,
# most "vm json tests" fail with either `balanceDiff` or `nonceDiff` errors.
@ -319,7 +320,7 @@ proc fixtureCallEvm*(vmState: BaseVMState, call: RpcCallData,
# computation doesn't return. We'll have to obtain them outside EVMC.
result.isError = c.isError
result.error = c.error
result.gasRemaining = c.gasMeter.gasRemaining
result.gasUsed = gas - c.gasMeter.gasRemaining
result.output = c.output
result.vmState = c.vmState
shallowCopy(result.logEntries, c.logEntries)

View File

@ -9,7 +9,8 @@ import
unittest2, ../nimbus/vm_precompiles, json, stew/byteutils, test_helpers, os, tables,
strformat, strutils, eth/trie/db, eth/common, ../nimbus/db/db_chain,
../nimbus/[vm_computation, vm_types, vm_state, vm_types2], macros,
test_allowed_to_fail
test_allowed_to_fail,
../nimbus/transaction/call_evm, options
proc initAddress(i: byte): EthAddress = result[19] = i
@ -21,46 +22,32 @@ template doTest(fixture: JsonNode, fork: Fork, address: PrecompileAddresses): un
expectedErr = test.hasKey("ExpectedError")
expected = if test.hasKey("Expected"): hexToSeqByte(test["Expected"].getStr) else: @[]
dataStr = test["Input"].getStr
data = if dataStr.len > 0: dataStr.hexToSeqByte else: @[]
vmState = newBaseVMState(header.stateRoot, header, newBaseChainDB(newMemoryDb()))
gas = 1_000_000_000.GasInt
gasPrice = 1.GasInt
sender = initAddress(0x00)
toAddress = initAddress(address.byte)
gasCost = if test.hasKey("Gas"): test["Gas"].getInt else: -1
gasExpected = if test.hasKey("Gas"): test["Gas"].getInt else: -1
vmState.setupTxContext(
origin = sender,
gasPrice = gasPrice
)
var call: RpcCallData
call.source = initAddress(0x00)
call.to = initAddress(address.byte)
call.gas = 1_000_000_000.GasInt
call.gasPrice = 1.GasInt
call.value = 0.u256
call.data = if dataStr.len > 0: dataStr.hexToSeqByte else: @[]
call.contractCreation = false
var
message = Message(
kind: evmcCall,
gas: gas,
sender: sender,
contractAddress: toAddress,
codeAddress: toAddress,
value: 0.u256,
data: data
)
comp = newComputation(vmState, message)
let initialGas = comp.gasMeter.gasRemaining
discard execPrecompiles(comp, fork)
let fixtureResult = fixtureCallEvm(vmState, call, call.source, some(fork))
if expectedErr:
check comp.isError
check fixtureResult.isError
else:
let c = comp.output == expected
if not c: echo "Output : " & comp.output.toHex & "\nExpected: " & expected.toHex
check not fixtureResult.isError
let c = fixtureResult.output == expected
if not c: echo "Output : " & fixtureResult.output.toHex & "\nExpected: " & expected.toHex
check c
if gasCost >= 0:
let gasFee = initialGas - comp.gasMeter.gasRemaining
if gasFee != gasCost:
debugEcho "GAS: ", gasFee, " ", gasCost
check gasFee == gasCost
if gasExpected >= 0:
if fixtureResult.gasUsed != gasExpected:
debugEcho "GAS: ", fixtureResult.gasUsed, " ", gasExpected
check fixtureResult.gasUsed == gasExpected
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
let

View File

@ -80,7 +80,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
check(fixtureResult.output.bytesToHex == expectedOutput)
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt
let actualGasRemaining = fixtureResult.gasRemaining
let actualGasRemaining = call.gas - fixtureResult.gasUsed
checkpoint(&"Remaining: {actualGasRemaining} - Expected: {expectedGasRemaining}")
check(actualGasRemaining == expectedGasRemaining)