Transaction: Run all computations via EVMC `execute`

1. Send all EVM executions through the EVMC `execute` function.

   It leads to the same place in the end as calling `Computation` before, but
   `execute` is the API function used by all EVMC implementations, and it is
   very explicit what data is passed back and forth.

2. As a consequence this starts using the new `host_services` code from EVM, so
   this is a significant change to the paths used for account state processing.

3. Because we will have to remove the `newComputation` call on the host side,
   anticipating that the contract code is now saved in `host` instead of being
   copied around.  As it's saved in `host`, there is no need to pass it
   separately to `evmcExecComputation`.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
This commit is contained in:
Jamie Lokier 2021-05-18 23:53:14 +01:00
parent df71c8bec9
commit 0e2bc8408d
No known key found for this signature in database
GPG Key ID: CBC25C68435C30A2
3 changed files with 29 additions and 11 deletions

View File

@ -45,7 +45,7 @@ type
memory*: Memory # EVM memory on return (for test only).
error*: Error # Error if `isError` (for test only).
proc hostToComputationMessage(msg: EvmcMessage): Message =
proc hostToComputationMessage*(msg: EvmcMessage): Message =
Message(
kind: CallKind(msg.kind),
depth: msg.depth,
@ -162,6 +162,7 @@ proc setupHost(call: CallParams): TransactionHost =
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, cMsg, code)
shallowCopy(host.code, code)
else:
if call.input.len > 0:
@ -176,6 +177,26 @@ proc setupHost(call: CallParams): TransactionHost =
return host
proc doExec(host: TransactionHost, call: CallParams) =
let c = host.computation
if call.noTransfer:
# TODO: This isn't doing `noTransfer` properly yet, just enough for
# fixtures tests.
executeOpcodes(c)
doAssert c.continuation.isNil
doAssert c.child.isNil
else:
execComputation(c)
when defined(evmc_enabled):
import ./host_services
proc doExecEvmc(host: TransactionHost, call: CallParams) =
if call.noTransfer:
let c = host.computation
c.setError("Unable to perform noTransfer computations in EVMC mode", true)
else:
let callResult = evmcExecComputation(host)
proc runComputation*(call: CallParams): CallResult =
let host = setupHost(call)
let c = host.computation
@ -189,14 +210,10 @@ proc runComputation*(call: CallParams): CallResult =
host.vmState.mutateStateDB:
db.subBalance(call.sender, call.gasLimit.u256 * call.gasPrice.u256)
if call.noTransfer:
# TODO: This isn't doing `noTransfer` properly yet, just enough for
# fixtures tests.
executeOpcodes(c)
doAssert c.continuation.isNil
doAssert c.child.isNil
when defined(evmc_enabled):
doExecEvmc(host, call)
else:
execComputation(c)
doExec(host, call)
# Calculated gas used, taking into account refund rules.
var gasRemaining: GasInt = 0

View File

@ -90,7 +90,7 @@ proc evmc_create_nimbus_evm(): ptr evmc_vm {.cdecl, importc.}
# a separate library yet.
import ./evmc_vm_glue
proc evmcExecComputation*(host: TransactionHost, code: seq[byte]): EvmcResult =
proc evmcExecComputation*(host: TransactionHost): EvmcResult =
let vm = evmc_create_nimbus_evm()
if vm.isNil:
echo "Warning: No EVM"
@ -119,5 +119,5 @@ proc evmcExecComputation*(host: TransactionHost, code: seq[byte]): EvmcResult =
{.gcsafe.}:
vm.execute(vm, hostInterface[].addr, hostContext,
evmc_revision(host.vmState.fork), host.msg,
if code.len > 0: code[0].unsafeAddr else: nil,
code.len.csize_t)
if host.code.len > 0: host.code[0].unsafeAddr else: nil,
host.code.len.csize_t)

View File

@ -55,6 +55,7 @@ type
computation*: Computation
msg*: EvmcMessage
input*: seq[byte]
code*: seq[byte]
cachedTxContext*: bool
txContext*: EvmcTxContext
logEntries*: seq[Log]