Commit Graph

9 Commits

Author SHA1 Message Date
Jamie Lokier 6d4205b0b0
Transaction: Just enough support to work with nested calls
Proper nested call functionality is being skipped in this iteration of new EVMC
host code to keep it simpler, to allow testing and architecture to be built
around the less complicated non-nested cases first.

Instead, nested calls use the old `Computation` path, and bypass any
third-party EVM that may be loaded.  The results are the same, and mixing
different EVMs in this way is actually permitted in the EVMC specification.

This approach also means third-party EVMs we test don't need to support
precompiles and we don't need to specially handle those cases.
(E.g. "evmone" doesn't support precompiles, just EVM1 opcodes).

(These before/after scope actions are approximately copy-pasted from
`nimbus/vm/evmc_host.nim`, making their detailed behaviour "obviously correct".
Of course they are subject to tests as well.  The small stack property of
a3c8a5c3 "EVMC: Small stacks when using EVMC, closes #575 (segfaults)" is
carefully retained.)

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:41 +01:00
Jamie Lokier 97b4aa5619
Transaction: Calculate EIP-1283/2200/2929 gas refund in `setStorage`
Make the host service `setStorage` calculate the gas refund itself, instead of
depending on EVM internals.

In EVMC, host `setStorage` is responsible for adding the gas refund using the
rules of EIP-1283 (Constantinople), EIP-2200 (Istanbul) or EIP-2929 (Berlin).

It is not obvious that the host must do it from EVMC documentation, but it's
how it has to be.  Note, this is very different from the gas _cost_, which the
host does not calculate.

Gas refund doesn't affect computation.  It is applied after the whole
transaction is complete, so it can be tracked on the host or EVM side.  But
`setStorage` doesn't return enough information for the EVM to calculate the
refund, so the host must do it when `setStorage` is used.

For now, this continues using Nimbus `Computation` just to hold the gas refund,
to fit around existing structures and get new EVMC working.  But the host can't
keep using `Computation`, so gas refund will be moved out of it in due course.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier eb52fd3906
Transaction: Make `emitLog` use `Computation` to pass tests
When processing log operations on the EVMC host side, it causes incorrect
`rootHash` results in some tests.  This patch fixes the results.

The cause of these results is known: `Computation` is still doing parts of
contract scope entry/exit which need to be moved to the host.  For now, as a
temporary workaround, update logs in `Computation` as it did before.

This makes test pass when using Nimbus EVM.  (It breaks third-party EVMs when
`LOG*` ops are used, although most other tests pass.)

We can't keep this as it prevents complete host/EVM separation, but it's useful
in the current code, and it's fine to develop other functionality on top.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier ce0c13c4ca
Transaction: Make `selfDestruct` use `Computation` to pass tests
When processing self destructs on the EVMC host side, it causes incorrect
`rootHash` results in some tests.  This patch fixes the results.

The cause of these results is known: `Computation` is still doing parts of
contract scope entry/exit which need to be moved to the host.  For now, as a
temporary workaround, update self destructs in `Computation` as it did before.

This makes test pass when using Nimbus EVM.  (It breaks third-party EVMs when
`SELFDESTRUCT` ops are used, although most other tests pass.)

We can't keep this as it prevents complete host/EVM separation, but it's useful
in the current code, and it's fine to develop other functionality on top.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier b734240291
Transaction: Fix bounds error getting data address in empty seq
In the unusual case where log data is zero-length, `data[0].addr` is invalid
and Nim thoughtfully raises `IndexOutOfBounds`, a `Defect` so it's not even
in `CatchableError`.

This is done in the EVMC host services to handle `LOG*` ops, and it made one
of the EVM tests silently fail with no error message.  The fix is obvious.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier ffd34a69fe
EVMC: Using `{.show.}` trace all calls from EVM to host services
When `show_tx_calls` is manually set to true, show all the calls from the EVM
to the host, including name, arguments and results.

For example this shows each call to `setStorage`, the key, value and storage
result.  This output allows the externally-visible activity of an EVM to be
seen, and it's been useful for guessing what went wrong when a test fails.

In theory, if two EVMs show the same activity in this log, they should have the
same effect on account states, gas, etc. and the same final `roothash`
(which is the only value some tests check).

ps. Ideally we'd use `{.push show.}`...`{.pop.}`, just like with `inline`.
But we can't: https://github.com/nim-lang/Nim/issues/12867

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier 43b66a3a05
EVMC: `{.show.}` pragma to show EVMC host call arguments and results
New pragma `{.show.}` on a proc definition turns on tracing for that proc.
Every call to it shows the name, arguments and results, if `show_tx_calls` is
manually set to true.  This is to trace calls from EVM to host.

This started as a template which took a block expression, but the closure it
used led to illegal capture errors.  It was easier to write a macro.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:40 +01:00
Jamie Lokier 0b19f42158
EVMC: Binary compatibility glue on the host side
1. This provides the necessary type adjustments for host services to be
   (optionally) callable via the EVMC binary-compatible interface.  This layer
   is stashed away in a glue module so the host services continue to use
   appropriate Nim types, and are still callable directly.

   Inlining is used to ensure there should be no real overhead, including stack
   frame size for the `call` function.  Note, `import` must be used for
   `{.inline.}` to work effectively.

2. This also provides a key call in the other direction, the version of host to
   EVM `execute` that is called on the host side.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:39 +01:00
Jamie Lokier d49fa0bb86
Transaction: Add "host services", accessors to host state from EVM
This provides "host services", functions provided by the application to an EVM.
They are a key part of EVMC compatibility, but we will switch to using these
with "native" EVM as well.

These are functions like `getStorage`, `setStorage` and `emitLog` for accessing
the account state, because the EVM is not allowed direct access to the database.

This code is adapted from `nimbus/vm/evmc_host.nim` and other places, but there
is more emphasis on being host-side only, no dependency on the EVM or
`Computation` type.  It uses `TransactionHost` and types in `host_types`.

These host services have two goals: To be compatible with EVMC, and to be a
good way for the Nimbus EVM to access the data it needs.  In our new Nimbus
internal architecture, the EVM will only access the databases and other
application state via these host service functions.

The reason for containing the EVM like this, even "native" EVM, is that having
one good interface to the data makes it a lot easier to change how the database
works, which is on the roadmap.

These functions almost have EVMC signatures, but they are not binary compatible
with EVMC.  (Binary compatibility is provided by another module).  It would be
fine for Nimbus EVM to call these functions directly when linked directly.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-06-08 18:29:39 +01:00