EVMC: Binary compatibility on the VM side for calling `execute`
This provides the functions a loadable VM must provide for a host to use it.
The main access to a loaded EVM is `evmc_create_nimbus_evm`, and this meant to
be the only exported function the caller starts with.
That provides access to other functions, also defined in this patch, to
configure the EVM and then the key interesting function is `execute`.
`execute` runs a full computation, here using Nimbus EVM `Computation`.
(Note, even though everything is EVMC binary-compatible, there is a small
dependency on `TransactionHost` in `execute` here, which prevents this being
used by a host that is not Nimbus at the moment. It is necessary for some
tests, and will eventually go away.)
Although this provides the VM-side functionality needed by the host, it does
not contain the glue functions for `Computation` to call the host, which are
already part of the Nimbus EVM in `nimbus/vm/evmc_api.nim`.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-21 05:24:09 +00:00
|
|
|
# Nimbus - Binary compatibility on the VM side of the EVMC API interface
|
|
|
|
#
|
|
|
|
# Copyright (c) 2019-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.
|
|
|
|
|
|
|
|
#{.push raises: [Defect].}
|
|
|
|
|
|
|
|
import
|
|
|
|
./host_types, evmc/evmc, stew/ranges/ptr_arith,
|
|
|
|
".."/[vm_types, vm_computation, vm_state_transactions]
|
|
|
|
|
|
|
|
proc evmcReleaseResult(result: var evmc_result) {.cdecl.} =
|
|
|
|
dealloc(result.output_data)
|
|
|
|
|
|
|
|
proc evmcExecute(vm: ptr evmc_vm, hostInterface: ptr evmc_host_interface,
|
|
|
|
hostContext: evmc_host_context, rev: evmc_revision,
|
|
|
|
msg: var evmc_message, code: ptr byte,
|
|
|
|
code_size: csize_t): evmc_result {.cdecl.} =
|
|
|
|
# TODO: Obviously we are cheating here at the moment, knowing the caller type.
|
|
|
|
# TODO: This lets the host read extra results needed for tests, but it
|
|
|
|
# means the Nimbus EVM cannot be used by a non-Nimbus host, yet.
|
|
|
|
let host = cast[TransactionHost](hostContext)
|
|
|
|
var c = host.computation
|
|
|
|
|
|
|
|
# Allocate `Computation` on demand, and leave it in `host` for that to
|
|
|
|
# extract additional results.
|
|
|
|
#if c.isNil:
|
|
|
|
# let cMsg = hostToComputationMessage(host.msg)
|
|
|
|
# # TODO: Can we avoid `seq[byte]` so we don't have to copy the code?
|
|
|
|
# let codeSeq = if code_size <= 0: @[]
|
|
|
|
# else: @(makeOpenArray(code, code_size.int))
|
|
|
|
# c = newComputation(host.vmState, cMsg, codeSeq)
|
|
|
|
# c.host.init(cast[ptr nimbus_host_interface](hostInterface), hostContext)
|
|
|
|
# host.computation = c
|
|
|
|
|
|
|
|
c.host.init(cast[ptr nimbus_host_interface](hostInterface), hostContext)
|
|
|
|
try:
|
|
|
|
execComputation(c)
|
|
|
|
except Exception as e:
|
|
|
|
c.setError("Caught error " & e.repr, true);
|
|
|
|
|
|
|
|
# When output size is zero, output data pointer may be null.
|
|
|
|
var output_data: ptr byte
|
|
|
|
var output_size: int
|
|
|
|
if c.output.len > 0:
|
|
|
|
# TODO: Don't use a copy, share the underlying data and use `GC_ref`.
|
|
|
|
# Return a copy, because reference counting is complicated across the
|
|
|
|
# shared library boundary. We could use a refcount but we don't have a
|
|
|
|
# `ref seq[byte]` to start with, so need to check `GC_ref` on a non-ref
|
|
|
|
# `seq` does what we want first.
|
|
|
|
output_size = c.output.len
|
|
|
|
# The `alloc` here matches `dealloc` in `evmcReleaseResult`.
|
|
|
|
output_data = cast[ptr byte](alloc(output_size))
|
|
|
|
copyMem(output_data, c.output[0].addr, output_size)
|
|
|
|
|
|
|
|
return evmc_result(
|
|
|
|
# Standard EVMC result, if a bit generic.
|
|
|
|
status_code: if c.isSuccess: EVMC_SUCCESS
|
|
|
|
elif not c.error.burnsGas: EVMC_REVERT
|
|
|
|
else: EVMC_FAILURE,
|
|
|
|
# Gas left is required to be zero when not `EVMC_SUCCESS` or `EVMC_REVERT`.
|
|
|
|
gas_left: if result.status_code notin {EVMC_SUCCESS, EVMC_REVERT}: 0'i64
|
|
|
|
else: c.gasMeter.gasRemaining.int64,
|
2022-09-26 12:23:54 +00:00
|
|
|
gas_refund: if result.status_code == EVMC_SUCCESS: c.gasMeter.gasRefunded.int64
|
|
|
|
else: 0'i64,
|
EVMC: Binary compatibility on the VM side for calling `execute`
This provides the functions a loadable VM must provide for a host to use it.
The main access to a loaded EVM is `evmc_create_nimbus_evm`, and this meant to
be the only exported function the caller starts with.
That provides access to other functions, also defined in this patch, to
configure the EVM and then the key interesting function is `execute`.
`execute` runs a full computation, here using Nimbus EVM `Computation`.
(Note, even though everything is EVMC binary-compatible, there is a small
dependency on `TransactionHost` in `execute` here, which prevents this being
used by a host that is not Nimbus at the moment. It is necessary for some
tests, and will eventually go away.)
Although this provides the VM-side functionality needed by the host, it does
not contain the glue functions for `Computation` to call the host, which are
already part of the Nimbus EVM in `nimbus/vm/evmc_api.nim`.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-21 05:24:09 +00:00
|
|
|
output_data: output_data,
|
|
|
|
output_size: output_size.csize_t,
|
|
|
|
release: if output_data.isNil: nil
|
|
|
|
else: evmcReleaseResult
|
|
|
|
# Nim defaults are fine for `create_address` and `padding`, zero bytes.
|
|
|
|
)
|
|
|
|
|
2022-09-26 04:56:54 +00:00
|
|
|
const evmcName = "Nimbus EVM (vm2)"
|
EVMC: Binary compatibility on the VM side for calling `execute`
This provides the functions a loadable VM must provide for a host to use it.
The main access to a loaded EVM is `evmc_create_nimbus_evm`, and this meant to
be the only exported function the caller starts with.
That provides access to other functions, also defined in this patch, to
configure the EVM and then the key interesting function is `execute`.
`execute` runs a full computation, here using Nimbus EVM `Computation`.
(Note, even though everything is EVMC binary-compatible, there is a small
dependency on `TransactionHost` in `execute` here, which prevents this being
used by a host that is not Nimbus at the moment. It is necessary for some
tests, and will eventually go away.)
Although this provides the VM-side functionality needed by the host, it does
not contain the glue functions for `Computation` to call the host, which are
already part of the Nimbus EVM in `nimbus/vm/evmc_api.nim`.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-21 05:24:09 +00:00
|
|
|
const evmcVersion = "0.0.1"
|
|
|
|
|
|
|
|
proc evmcGetCapabilities(vm: ptr evmc_vm): evmc_capabilities {.cdecl.} =
|
|
|
|
{EVMC_CAPABILITY_EVM1, EVMC_CAPABILITY_PRECOMPILES}
|
|
|
|
|
|
|
|
proc evmcSetOption(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.} =
|
|
|
|
return EVMC_SET_OPTION_INVALID_NAME
|
|
|
|
|
|
|
|
proc evmcDestroy(vm: ptr evmc_vm) {.cdecl.} =
|
|
|
|
GC_unref(cast[ref evmc_vm](vm))
|
|
|
|
|
|
|
|
proc evmc_create_nimbus_evm(): ptr evmc_vm {.cdecl, exportc.} =
|
|
|
|
## Entry point to the Nimbus EVM, using an EVMC compatible interface.
|
|
|
|
## This is an exported C function. EVMC specifies the function must
|
|
|
|
## have this name format when exported from a shared library.
|
|
|
|
let vm = (ref evmc_vm)(
|
EVMC: Option `--evm`, load third-party EVM as a shared library
This patch adds:
- Load and use a third-party EVM in a shared library, instead of Nimbus EVM.
- New option `--evm` to specify which library to load.
- The library and this loader conforms to the [EVMC]
(https://evmc.ethereum.org/) 9.x specification.
Any third-party EVM which is compatible with EVMC version 9.x and supports EVM1
contract code will be accepted. The operating system's shared library format
applies. These are `.so*` files on Linux, `.dll` files on Windows and `.dylib`
files on Mac.
The alternative EVM can be selected in two ways:
- Nimbus command line option `--evm:<path>`.
- Environment variable `NIMBUS_EVM=<path>`.
The reason for an environment variable is this allows all the test programs to
run with a third-party EVM as well. Some don't parse command line options.
There are some limitations to be aware of:
- The third-party EVM must use EVMC version 9.x, no other major version.
EVMC 9.x supports EIP-1559 / London fork and older transactions.
- Nested `*CALL` and `CREATE*` operations don't use the third-party EVM yet.
These call the built-in Nimbus EVM. This mixing of different EVMs between
levels is explicitly allowed in specs, so there is no problem doing it.
- The third-party EVM doesn't need to support precompiles, because those are
nested calls, which use the built-in Nimbus EVM.
- Third-party EVMs execute contracts correctly, but fail the final `rootHash`
match. The reason is that some account state changes, which are correct, are
currently inside the Nimbus EVM and need to be moved to EVMC host logic.
*This is a known work in progress*. The EVM execution itself is fine.
Test results using "evmone" third-party EVM:
- [evmone](https://github.com/ethereum/evmone) has been tested. Only on
Linux but it "should" work on Windows and Mac equally well.
- [Version 0.8.1](https://github.com/ethereum/evmone/releases/tag/v0.8.1) was
used because it is compatible with EVMC 9.x, which is required for the
EIP-1559 / London fork, which Nimbus supports. Version 0.8.0 could be used
but it looks like an important bug was fixed in 0.8.1.
- evmone runs fine and the trace output looks good. The calls and arguments
are the same as the built-in Nimbus EVM for tests that have been checked
manually, except evmone skips some calls that can be safely skipped.
- The final `rootHash` is incorrect, due to the *work in progress* mentioned
above which is not part of the evmone execution. Due to this, it's possible
to try evmone and verify expected behaviours, which also validates our own
EVMC implementation, but it can't be used as a full substitute yet.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-12-05 11:20:27 +00:00
|
|
|
abi_version: EVMC_ABI_VERSION,
|
EVMC: Binary compatibility on the VM side for calling `execute`
This provides the functions a loadable VM must provide for a host to use it.
The main access to a loaded EVM is `evmc_create_nimbus_evm`, and this meant to
be the only exported function the caller starts with.
That provides access to other functions, also defined in this patch, to
configure the EVM and then the key interesting function is `execute`.
`execute` runs a full computation, here using Nimbus EVM `Computation`.
(Note, even though everything is EVMC binary-compatible, there is a small
dependency on `TransactionHost` in `execute` here, which prevents this being
used by a host that is not Nimbus at the moment. It is necessary for some
tests, and will eventually go away.)
Although this provides the VM-side functionality needed by the host, it does
not contain the glue functions for `Computation` to call the host, which are
already part of the Nimbus EVM in `nimbus/vm/evmc_api.nim`.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-21 05:24:09 +00:00
|
|
|
name: evmcName,
|
|
|
|
version: evmcVersion,
|
|
|
|
destroy: evmcDestroy,
|
|
|
|
execute: evmcExecute,
|
|
|
|
get_capabilities: evmcGetCapabilities,
|
|
|
|
set_option: evmcSetOption
|
|
|
|
)
|
|
|
|
# Keep an extra reference on this, until `evmcDestroy` is called.
|
|
|
|
GC_ref(vm)
|
|
|
|
return cast[ptr evmc_vm](vm)
|
EVMC: Option `--evm`, load third-party EVM as a shared library
This patch adds:
- Load and use a third-party EVM in a shared library, instead of Nimbus EVM.
- New option `--evm` to specify which library to load.
- The library and this loader conforms to the [EVMC]
(https://evmc.ethereum.org/) 9.x specification.
Any third-party EVM which is compatible with EVMC version 9.x and supports EVM1
contract code will be accepted. The operating system's shared library format
applies. These are `.so*` files on Linux, `.dll` files on Windows and `.dylib`
files on Mac.
The alternative EVM can be selected in two ways:
- Nimbus command line option `--evm:<path>`.
- Environment variable `NIMBUS_EVM=<path>`.
The reason for an environment variable is this allows all the test programs to
run with a third-party EVM as well. Some don't parse command line options.
There are some limitations to be aware of:
- The third-party EVM must use EVMC version 9.x, no other major version.
EVMC 9.x supports EIP-1559 / London fork and older transactions.
- Nested `*CALL` and `CREATE*` operations don't use the third-party EVM yet.
These call the built-in Nimbus EVM. This mixing of different EVMs between
levels is explicitly allowed in specs, so there is no problem doing it.
- The third-party EVM doesn't need to support precompiles, because those are
nested calls, which use the built-in Nimbus EVM.
- Third-party EVMs execute contracts correctly, but fail the final `rootHash`
match. The reason is that some account state changes, which are correct, are
currently inside the Nimbus EVM and need to be moved to EVMC host logic.
*This is a known work in progress*. The EVM execution itself is fine.
Test results using "evmone" third-party EVM:
- [evmone](https://github.com/ethereum/evmone) has been tested. Only on
Linux but it "should" work on Windows and Mac equally well.
- [Version 0.8.1](https://github.com/ethereum/evmone/releases/tag/v0.8.1) was
used because it is compatible with EVMC 9.x, which is required for the
EIP-1559 / London fork, which Nimbus supports. Version 0.8.0 could be used
but it looks like an important bug was fixed in 0.8.1.
- evmone runs fine and the trace output looks good. The calls and arguments
are the same as the built-in Nimbus EVM for tests that have been checked
manually, except evmone skips some calls that can be safely skipped.
- The final `rootHash` is incorrect, due to the *work in progress* mentioned
above which is not part of the evmone execution. Due to this, it's possible
to try evmone and verify expected behaviours, which also validates our own
EVMC implementation, but it can't be used as a full substitute yet.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-12-05 11:20:27 +00:00
|
|
|
|
2022-09-26 12:23:54 +00:00
|
|
|
# This code assumes fields, methods and types of ABI version 10, and must be
|
EVMC: Option `--evm`, load third-party EVM as a shared library
This patch adds:
- Load and use a third-party EVM in a shared library, instead of Nimbus EVM.
- New option `--evm` to specify which library to load.
- The library and this loader conforms to the [EVMC]
(https://evmc.ethereum.org/) 9.x specification.
Any third-party EVM which is compatible with EVMC version 9.x and supports EVM1
contract code will be accepted. The operating system's shared library format
applies. These are `.so*` files on Linux, `.dll` files on Windows and `.dylib`
files on Mac.
The alternative EVM can be selected in two ways:
- Nimbus command line option `--evm:<path>`.
- Environment variable `NIMBUS_EVM=<path>`.
The reason for an environment variable is this allows all the test programs to
run with a third-party EVM as well. Some don't parse command line options.
There are some limitations to be aware of:
- The third-party EVM must use EVMC version 9.x, no other major version.
EVMC 9.x supports EIP-1559 / London fork and older transactions.
- Nested `*CALL` and `CREATE*` operations don't use the third-party EVM yet.
These call the built-in Nimbus EVM. This mixing of different EVMs between
levels is explicitly allowed in specs, so there is no problem doing it.
- The third-party EVM doesn't need to support precompiles, because those are
nested calls, which use the built-in Nimbus EVM.
- Third-party EVMs execute contracts correctly, but fail the final `rootHash`
match. The reason is that some account state changes, which are correct, are
currently inside the Nimbus EVM and need to be moved to EVMC host logic.
*This is a known work in progress*. The EVM execution itself is fine.
Test results using "evmone" third-party EVM:
- [evmone](https://github.com/ethereum/evmone) has been tested. Only on
Linux but it "should" work on Windows and Mac equally well.
- [Version 0.8.1](https://github.com/ethereum/evmone/releases/tag/v0.8.1) was
used because it is compatible with EVMC 9.x, which is required for the
EIP-1559 / London fork, which Nimbus supports. Version 0.8.0 could be used
but it looks like an important bug was fixed in 0.8.1.
- evmone runs fine and the trace output looks good. The calls and arguments
are the same as the built-in Nimbus EVM for tests that have been checked
manually, except evmone skips some calls that can be safely skipped.
- The final `rootHash` is incorrect, due to the *work in progress* mentioned
above which is not part of the evmone execution. Due to this, it's possible
to try evmone and verify expected behaviours, which also validates our own
EVMC implementation, but it can't be used as a full substitute yet.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-12-05 11:20:27 +00:00
|
|
|
# checked for compatibility if the `import evmc/evmc` major version is updated.
|
2022-09-26 12:23:54 +00:00
|
|
|
when EVMC_ABI_VERSION != 10:
|
|
|
|
{.error: ("This code assumes EVMC_ABI_VERSION 10;" &
|
EVMC: Option `--evm`, load third-party EVM as a shared library
This patch adds:
- Load and use a third-party EVM in a shared library, instead of Nimbus EVM.
- New option `--evm` to specify which library to load.
- The library and this loader conforms to the [EVMC]
(https://evmc.ethereum.org/) 9.x specification.
Any third-party EVM which is compatible with EVMC version 9.x and supports EVM1
contract code will be accepted. The operating system's shared library format
applies. These are `.so*` files on Linux, `.dll` files on Windows and `.dylib`
files on Mac.
The alternative EVM can be selected in two ways:
- Nimbus command line option `--evm:<path>`.
- Environment variable `NIMBUS_EVM=<path>`.
The reason for an environment variable is this allows all the test programs to
run with a third-party EVM as well. Some don't parse command line options.
There are some limitations to be aware of:
- The third-party EVM must use EVMC version 9.x, no other major version.
EVMC 9.x supports EIP-1559 / London fork and older transactions.
- Nested `*CALL` and `CREATE*` operations don't use the third-party EVM yet.
These call the built-in Nimbus EVM. This mixing of different EVMs between
levels is explicitly allowed in specs, so there is no problem doing it.
- The third-party EVM doesn't need to support precompiles, because those are
nested calls, which use the built-in Nimbus EVM.
- Third-party EVMs execute contracts correctly, but fail the final `rootHash`
match. The reason is that some account state changes, which are correct, are
currently inside the Nimbus EVM and need to be moved to EVMC host logic.
*This is a known work in progress*. The EVM execution itself is fine.
Test results using "evmone" third-party EVM:
- [evmone](https://github.com/ethereum/evmone) has been tested. Only on
Linux but it "should" work on Windows and Mac equally well.
- [Version 0.8.1](https://github.com/ethereum/evmone/releases/tag/v0.8.1) was
used because it is compatible with EVMC 9.x, which is required for the
EIP-1559 / London fork, which Nimbus supports. Version 0.8.0 could be used
but it looks like an important bug was fixed in 0.8.1.
- evmone runs fine and the trace output looks good. The calls and arguments
are the same as the built-in Nimbus EVM for tests that have been checked
manually, except evmone skips some calls that can be safely skipped.
- The final `rootHash` is incorrect, due to the *work in progress* mentioned
above which is not part of the evmone execution. Due to this, it's possible
to try evmone and verify expected behaviours, which also validates our own
EVMC implementation, but it can't be used as a full substitute yet.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-12-05 11:20:27 +00:00
|
|
|
" update the code to use EVMC_ABI_VERSION " & $EVMC_ABI_VERSION).}
|