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-05-20 19:08:36 +00:00
|
|
|
# Nimbus - Services available to EVM code that is run for a transaction
|
|
|
|
#
|
2024-02-16 09:08:07 +00:00
|
|
|
# Copyright (c) 2019-2024 Status Research & Development GmbH
|
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-05-20 19:08:36 +00:00
|
|
|
# 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.
|
|
|
|
|
2023-05-10 16:04:35 +00:00
|
|
|
#{.push raises: [].}
|
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-05-20 19:08:36 +00:00
|
|
|
|
|
|
|
import
|
2024-06-14 07:31:08 +00:00
|
|
|
std/typetraits,
|
2024-02-16 09:08:07 +00:00
|
|
|
stint, chronicles,
|
2023-12-12 19:12:56 +00:00
|
|
|
eth/common/eth_types, ../db/ledger,
|
2022-12-02 04:39:12 +00:00
|
|
|
../common/[evmforks, common],
|
2024-06-17 07:56:39 +00:00
|
|
|
../evm/[state, internals],
|
2024-06-14 07:31:08 +00:00
|
|
|
./host_types, ./host_trace, ./host_call_nested,
|
|
|
|
stew/saturation_arith
|
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-05-20 19:08:36 +00:00
|
|
|
|
2024-06-17 07:56:39 +00:00
|
|
|
import ../evm/computation except fromEvmc, toEvmc
|
|
|
|
|
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-05-20 19:08:36 +00:00
|
|
|
proc setupTxContext(host: TransactionHost) =
|
|
|
|
# Conversion issues:
|
|
|
|
#
|
|
|
|
# `txContext.tx_gas_price` is 256-bit, but `vmState.txGasPrice` is 64-bit
|
|
|
|
# signed (`GasInt`), and in reality it tends to be a fairly small integer,
|
|
|
|
# usually < 16 bits. Our EVM truncates whatever it gets blindly to 64-bit
|
|
|
|
# anyway. Largest ever so far may be 100,000,000.
|
|
|
|
# https://medium.com/amberdata/most-expensive-transaction-in-ethereum-blockchain-history-99d9a30d8e02
|
|
|
|
#
|
2024-06-14 07:31:08 +00:00
|
|
|
# `txContext.block_number` is 64-bit signed. Nimbus `BlockNumber` is
|
|
|
|
# 64-bit unsigned, so we use int64.saturate to avoid overflow assertion.
|
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-05-20 19:08:36 +00:00
|
|
|
#
|
|
|
|
# `txContext.chain_id` is 256-bit, but `vmState.chaindb.config.chainId` is
|
|
|
|
# 64-bit or 32-bit depending on the target CPU architecture (Nim `uint`).
|
|
|
|
# Our EVM truncates whatever it gets blindly to 64-bit or 32-bit.
|
|
|
|
#
|
|
|
|
# No conversion required with the other fields:
|
|
|
|
#
|
|
|
|
# `txContext.tx_origin` and `txContext.block_coinbase` are 20-byte Ethereum
|
|
|
|
# addresses, no issues with these.
|
|
|
|
#
|
2024-06-14 07:31:08 +00:00
|
|
|
# `txContext.block_timestamp` is 64-bit signed. Nimbus `EthTime` is
|
|
|
|
# `distinct uint64`, but the wrapped value comes from std/times
|
|
|
|
# `getTime().utc.toTime.toUnix` when EthTime.now() called.
|
|
|
|
# So the wrapped value is actually in int64 range.
|
|
|
|
# Value from other sources e.g. test vectors can overflow this int64.
|
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-05-20 19:08:36 +00:00
|
|
|
#
|
|
|
|
# `txContext.block_gas_limit` is 64-bit signed (EVMC assumes
|
|
|
|
# [EIP-1985](https://eips.ethereum.org/EIPS/eip-1985) although it's not
|
|
|
|
# officially accepted), and `vmState.gasLimit` is too (`GasInt`).
|
|
|
|
#
|
2022-09-26 12:23:54 +00:00
|
|
|
# `txContext.block_prev_randao` is 256-bit, and this one can genuinely take
|
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-05-20 19:08:36 +00:00
|
|
|
# values over much of the 256-bit range.
|
|
|
|
|
|
|
|
let vmState = host.vmState
|
2023-10-01 07:24:15 +00:00
|
|
|
host.txContext.tx_gas_price = vmState.txCtx.gasPrice.u256.toEvmc
|
|
|
|
host.txContext.tx_origin = vmState.txCtx.origin.toEvmc
|
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-05-20 19:08:36 +00:00
|
|
|
# vmState.coinbase now unused
|
2023-09-24 15:25:41 +00:00
|
|
|
host.txContext.block_coinbase = vmState.blockCtx.coinbase.toEvmc
|
2024-06-14 07:31:08 +00:00
|
|
|
# vmState.number now unused
|
|
|
|
host.txContext.block_number = int64.saturate(vmState.blockNumber)
|
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-05-20 19:08:36 +00:00
|
|
|
# vmState.timestamp now unused
|
2024-06-14 07:31:08 +00:00
|
|
|
|
|
|
|
# TODO: do not use int64.saturate for timestamp for the moment
|
|
|
|
# while the choice of using int64 in evmc will not affect the evm/evmc operations
|
|
|
|
# but some of the tests will fail if the value from test vector overflow
|
|
|
|
# see getTimestamp of computation.nim too.
|
|
|
|
# probably block timestamp should be checked before entering EVM
|
|
|
|
# problematic test vectors:
|
|
|
|
# - BlockchainTests/GeneralStateTests/Pyspecs/cancun/eip4788_beacon_root/beacon_root_contract_timestamps.json
|
|
|
|
# - BlockchainTests/GeneralStateTests/Pyspecs/cancun/eip4788_beacon_root/beacon_root_equal_to_timestamp.json
|
2023-10-18 02:16:11 +00:00
|
|
|
host.txContext.block_timestamp = cast[int64](vmState.blockCtx.timestamp)
|
2024-06-14 07:31:08 +00:00
|
|
|
|
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-05-20 19:08:36 +00:00
|
|
|
# vmState.gasLimit now unused
|
2024-07-07 06:52:11 +00:00
|
|
|
host.txContext.block_gas_limit = int64.saturate(vmState.blockCtx.gasLimit)
|
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-05-20 19:08:36 +00:00
|
|
|
# vmState.difficulty now unused
|
2022-12-02 04:39:12 +00:00
|
|
|
host.txContext.chain_id = vmState.com.chainId.uint.u256.toEvmc
|
2024-06-14 07:31:08 +00:00
|
|
|
host.txContext.block_base_fee = vmState.blockCtx.baseFeePerGas.get(0.u256).toEvmc
|
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-05-20 19:08:36 +00:00
|
|
|
|
2023-10-01 07:24:15 +00:00
|
|
|
if vmState.txCtx.versionedHashes.len > 0:
|
2023-08-04 12:43:30 +00:00
|
|
|
type
|
|
|
|
BlobHashPtr = typeof host.txContext.blob_hashes
|
2023-10-01 07:24:15 +00:00
|
|
|
host.txContext.blob_hashes = cast[BlobHashPtr](vmState.txCtx.versionedHashes[0].addr)
|
2023-08-04 12:43:30 +00:00
|
|
|
else:
|
|
|
|
host.txContext.blob_hashes = nil
|
|
|
|
|
2023-10-01 07:24:15 +00:00
|
|
|
host.txContext.blob_hashes_count= vmState.txCtx.versionedHashes.len.csize_t
|
|
|
|
host.txContext.blob_base_fee = vmState.txCtx.blobBaseFee.toEvmc
|
2023-08-04 12:43:30 +00:00
|
|
|
|
EVMC: Byte-endian conversions for 256-bit numeric values
Perform byte-endian conversion for 256-bit numeric values, but not 256-bit
hashes. These conversions are necessary for EVMC binary compatibility.
In new EVMC, all host-side conversions are explicit, calling `flip256`.
These conversions are performed in the EVMC "glue" code, which deals with the
binary interface, so the host services aren't aware of conversions.
We intend to skip these conversions when Nimbus host calls Nimbus EVM, even
when it's a shared library, using a negotiated EVMC extension. But for now
we're focused on correctness and cross-validation with third party EVMs.
The overhead of endian conversion is not too high because most EVMC host calls
access the database anyway. `getTxContext` does not, so the conversions from
that are cached here. Also, well-optimised EVMs don't call it often.
It is arguable whether endian conversion should occur for storage slots (`key`).
In favour of no conversion: Slot keys are 32-byte blobs, and this is clear in
the EVMC definition where slot keys are `evmc_bytes32` (not `evmc_uint256be`),
meaning treating as a number is _not_ expected by EVMC. Although they are
often small numbers, sometimes they are a hash from the contract code plus a
number. Slot keys are hashed on the host side with Keccak256 before any
database calls, so the host side does not look at them numerically.
In favour of conversion: They are often small numbers and it is helpful to log
them as such, rather than a long string of zero digits with 1-2 non-zero. The
representation in JSON has leading zeros removed, like a number rather than a
32-byte blob. There is also an interesting space optimisation when the keys
are used unhashed in storage.
Nimbus currently treats slot keys on the host side as numbers, and the tests
pass when endian conversion is done. So to remain consistent with other parts
of Nimbus we convert slot keys.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-11-24 15:54:59 +00:00
|
|
|
# Most host functions do `flip256` in `evmc_host_glue`, but due to this
|
|
|
|
# result being cached, it's better to do `flip256` when filling the cache.
|
|
|
|
host.txContext.tx_gas_price = flip256(host.txContext.tx_gas_price)
|
|
|
|
host.txContext.chain_id = flip256(host.txContext.chain_id)
|
|
|
|
host.txContext.block_base_fee = flip256(host.txContext.block_base_fee)
|
2023-10-01 07:24:15 +00:00
|
|
|
host.txContext.blob_base_fee = flip256(host.txContext.blob_base_fee)
|
EVMC: Byte-endian conversions for 256-bit numeric values
Perform byte-endian conversion for 256-bit numeric values, but not 256-bit
hashes. These conversions are necessary for EVMC binary compatibility.
In new EVMC, all host-side conversions are explicit, calling `flip256`.
These conversions are performed in the EVMC "glue" code, which deals with the
binary interface, so the host services aren't aware of conversions.
We intend to skip these conversions when Nimbus host calls Nimbus EVM, even
when it's a shared library, using a negotiated EVMC extension. But for now
we're focused on correctness and cross-validation with third party EVMs.
The overhead of endian conversion is not too high because most EVMC host calls
access the database anyway. `getTxContext` does not, so the conversions from
that are cached here. Also, well-optimised EVMs don't call it often.
It is arguable whether endian conversion should occur for storage slots (`key`).
In favour of no conversion: Slot keys are 32-byte blobs, and this is clear in
the EVMC definition where slot keys are `evmc_bytes32` (not `evmc_uint256be`),
meaning treating as a number is _not_ expected by EVMC. Although they are
often small numbers, sometimes they are a hash from the contract code plus a
number. Slot keys are hashed on the host side with Keccak256 before any
database calls, so the host side does not look at them numerically.
In favour of conversion: They are often small numbers and it is helpful to log
them as such, rather than a long string of zero digits with 1-2 non-zero. The
representation in JSON has leading zeros removed, like a number rather than a
32-byte blob. There is also an interesting space optimisation when the keys
are used unhashed in storage.
Nimbus currently treats slot keys on the host side as numbers, and the tests
pass when endian conversion is done. So to remain consistent with other parts
of Nimbus we convert slot keys.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-11-24 15:54:59 +00:00
|
|
|
|
2022-02-01 08:35:30 +00:00
|
|
|
# EIP-4399
|
|
|
|
# Transfer block randomness to difficulty OPCODE
|
2023-09-24 15:25:41 +00:00
|
|
|
let difficulty = vmState.difficultyOrPrevRandao.toEvmc
|
2022-09-26 12:23:54 +00:00
|
|
|
host.txContext.block_prev_randao = flip256(difficulty)
|
2022-02-01 08:35:30 +00:00
|
|
|
|
EVMC: Byte-endian conversions for 256-bit numeric values
Perform byte-endian conversion for 256-bit numeric values, but not 256-bit
hashes. These conversions are necessary for EVMC binary compatibility.
In new EVMC, all host-side conversions are explicit, calling `flip256`.
These conversions are performed in the EVMC "glue" code, which deals with the
binary interface, so the host services aren't aware of conversions.
We intend to skip these conversions when Nimbus host calls Nimbus EVM, even
when it's a shared library, using a negotiated EVMC extension. But for now
we're focused on correctness and cross-validation with third party EVMs.
The overhead of endian conversion is not too high because most EVMC host calls
access the database anyway. `getTxContext` does not, so the conversions from
that are cached here. Also, well-optimised EVMs don't call it often.
It is arguable whether endian conversion should occur for storage slots (`key`).
In favour of no conversion: Slot keys are 32-byte blobs, and this is clear in
the EVMC definition where slot keys are `evmc_bytes32` (not `evmc_uint256be`),
meaning treating as a number is _not_ expected by EVMC. Although they are
often small numbers, sometimes they are a hash from the contract code plus a
number. Slot keys are hashed on the host side with Keccak256 before any
database calls, so the host side does not look at them numerically.
In favour of conversion: They are often small numbers and it is helpful to log
them as such, rather than a long string of zero digits with 1-2 non-zero. The
representation in JSON has leading zeros removed, like a number rather than a
32-byte blob. There is also an interesting space optimisation when the keys
are used unhashed in storage.
Nimbus currently treats slot keys on the host side as numbers, and the tests
pass when endian conversion is done. So to remain consistent with other parts
of Nimbus we convert slot keys.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-11-24 15:54:59 +00:00
|
|
|
host.cachedTxContext = true
|
|
|
|
|
2021-05-20 09:15:00 +00:00
|
|
|
const use_evmc_glue = defined(evmc_enabled)
|
|
|
|
|
|
|
|
# When using the EVMC binary interface, each of the functions below is wrapped
|
|
|
|
# in another function that converts types to be compatible with the binary
|
|
|
|
# interface, and the functions below are not called directly. The conversions
|
|
|
|
# mostly just cast between byte-compatible types, so to avoid a redundant call
|
|
|
|
# layer, make the functions below `{.inline.}` when wrapped in this way.
|
|
|
|
when use_evmc_glue:
|
|
|
|
{.push inline.}
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc accountExists(host: TransactionHost, address: HostAddress): bool {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
if host.vmState.fork >= FkSpurious:
|
|
|
|
not host.vmState.readOnlyStateDB.isDeadAccount(address)
|
|
|
|
else:
|
|
|
|
host.vmState.readOnlyStateDB.accountExists(address)
|
|
|
|
|
|
|
|
# TODO: Why is `address` an argument in `getStorage`, `setStorage` and
|
|
|
|
# `selfDestruct`, if an EVM is only allowed to do these things to its own
|
|
|
|
# contract account and the host always knows which account?
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getStorage(host: TransactionHost, address: HostAddress, key: HostKey): HostValue {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
host.vmState.readOnlyStateDB.getStorage(address, key)
|
|
|
|
|
2024-07-05 00:00:03 +00:00
|
|
|
proc setStorage(host: TransactionHost, address: HostAddress,
|
|
|
|
key: HostKey, newVal: HostValue): EvmcStorageStatus {.show.} =
|
2022-09-26 12:23:54 +00:00
|
|
|
let
|
|
|
|
db = host.vmState.readOnlyStateDB
|
|
|
|
currentVal = db.getStorage(address, key)
|
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-05-24 08:53:53 +00:00
|
|
|
|
2022-09-26 12:23:54 +00:00
|
|
|
if currentVal == newVal:
|
|
|
|
return EVMC_STORAGE_ASSIGNED
|
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-05-20 19:08:36 +00:00
|
|
|
|
|
|
|
host.vmState.mutateStateDB:
|
2022-09-26 12:23:54 +00:00
|
|
|
db.setStorage(address, key, newVal)
|
|
|
|
|
|
|
|
# https://eips.ethereum.org/EIPS/eip-1283
|
|
|
|
let originalVal = db.getCommittedStorage(address, key)
|
|
|
|
if originalVal == currentVal:
|
|
|
|
if originalVal.isZero:
|
|
|
|
return EVMC_STORAGE_ADDED
|
|
|
|
|
|
|
|
# !is_zero(original_val)
|
|
|
|
if newVal.isZero:
|
|
|
|
return EVMC_STORAGE_DELETED
|
|
|
|
else:
|
|
|
|
return EVMC_STORAGE_MODIFIED
|
|
|
|
|
|
|
|
# originalVal != currentVal
|
|
|
|
if originalVal.isZero.not:
|
|
|
|
if currentVal.isZero:
|
|
|
|
if originalVal == newVal:
|
|
|
|
return EVMC_STORAGE_DELETED_RESTORED
|
|
|
|
else:
|
|
|
|
return EVMC_STORAGE_DELETED_ADDED
|
|
|
|
|
|
|
|
# !is_zero(current_val)
|
|
|
|
if newVal.isZero:
|
|
|
|
return EVMC_STORAGE_MODIFIED_DELETED
|
|
|
|
|
|
|
|
# !is_zero(new_val)
|
|
|
|
if originalVal == newVal:
|
|
|
|
return EVMC_STORAGE_MODIFIED_RESTORED
|
|
|
|
else:
|
|
|
|
return EVMC_STORAGE_ASSIGNED
|
|
|
|
|
|
|
|
# is_zero(original_val)
|
|
|
|
if originalVal == newVal:
|
|
|
|
return EVMC_STORAGE_ADDED_DELETED
|
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-05-20 19:08:36 +00:00
|
|
|
else:
|
2022-09-26 12:23:54 +00:00
|
|
|
return EVMC_STORAGE_ASSIGNED
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getBalance(host: TransactionHost, address: HostAddress): HostBalance {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
host.vmState.readOnlyStateDB.getBalance(address)
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getCodeSize(host: TransactionHost, address: HostAddress): HostSize {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
# TODO: Check this `HostSize`, it was copied as `uint` from other code.
|
|
|
|
# Note: Old `evmc_host` uses `getCode(address).len` instead.
|
|
|
|
host.vmState.readOnlyStateDB.getCodeSize(address).HostSize
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getCodeHash(host: TransactionHost, address: HostAddress): HostHash {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
let db = host.vmState.readOnlyStateDB
|
|
|
|
# TODO: Copied from `Computation`, but check if that code is wrong with
|
|
|
|
# `FkSpurious`, as it has different calls from `accountExists` above.
|
|
|
|
if not db.accountExists(address) or db.isEmptyAccount(address):
|
|
|
|
default(HostHash)
|
|
|
|
else:
|
|
|
|
db.getCodeHash(address)
|
|
|
|
|
|
|
|
proc copyCode(host: TransactionHost, address: HostAddress,
|
|
|
|
code_offset: HostSize, buffer_data: ptr byte,
|
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-05-21 05:24:09 +00:00
|
|
|
buffer_size: HostSize): HostSize {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
# We must handle edge cases carefully to prevent overflows. `len` is signed
|
|
|
|
# type `int`, but `code_offset` and `buffer_size` are _unsigned_, and may
|
|
|
|
# have large values (deliberately if attacked) that exceed the range of `int`.
|
|
|
|
#
|
|
|
|
# Comparing signed and unsigned types is _unsafe_: A type-conversion will
|
|
|
|
# take place which breaks the comparison for some values. So here we use
|
|
|
|
# explicit type-conversions, always compare the same types, and always
|
|
|
|
# convert towards the type that cannot truncate because preceding checks have
|
|
|
|
# been used to reduce the possible value range.
|
|
|
|
#
|
|
|
|
# Note, when there is no code, `getCode` result is empty `seq`. It was `nil`
|
|
|
|
# when the DB was first implemented, due to Nim language changes since then.
|
2024-06-21 07:44:10 +00:00
|
|
|
let code = host.vmState.readOnlyStateDB.getCode(address)
|
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-05-20 19:08:36 +00:00
|
|
|
var safe_len: int = code.len # It's safe to assume >= 0.
|
|
|
|
|
|
|
|
if code_offset >= safe_len.HostSize:
|
|
|
|
return 0
|
|
|
|
let safe_offset = code_offset.int
|
|
|
|
safe_len = safe_len - safe_offset
|
|
|
|
|
|
|
|
if buffer_size < safe_len.HostSize:
|
|
|
|
safe_len = buffer_size.int
|
|
|
|
|
|
|
|
if safe_len > 0:
|
2024-06-21 07:44:10 +00:00
|
|
|
copyMem(buffer_data, code.bytes()[safe_offset].addr, safe_len)
|
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-05-20 19:08:36 +00:00
|
|
|
return safe_len.HostSize
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc selfDestruct(host: TransactionHost, address, beneficiary: HostAddress) {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
host.vmState.mutateStateDB:
|
2023-09-26 02:03:21 +00:00
|
|
|
let localBalance = db.getBalance(address)
|
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-05-20 19:08:36 +00:00
|
|
|
|
2023-08-18 03:12:18 +00:00
|
|
|
if host.vmState.fork >= FkCancun:
|
2023-09-26 02:03:21 +00:00
|
|
|
# Zeroing contract balance except beneficiary
|
|
|
|
# is the same address
|
|
|
|
db.subBalance(address, localBalance)
|
|
|
|
|
|
|
|
# Transfer to beneficiary
|
|
|
|
db.addBalance(beneficiary, localBalance)
|
|
|
|
|
2023-08-18 03:12:18 +00:00
|
|
|
db.selfDestruct6780(address)
|
|
|
|
else:
|
2023-09-26 02:03:21 +00:00
|
|
|
# Transfer to beneficiary
|
|
|
|
db.addBalance(beneficiary, localBalance)
|
2023-08-18 03:12:18 +00:00
|
|
|
db.selfDestruct(address)
|
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-05-20 19:08:36 +00:00
|
|
|
|
EVMC: Improve host call tracing and fix nested call C stack usage
This combines two things, a C stack usage change with EVM nested calls
via EVMC, and changes to host call tracing.
Feature-wise, the tracing is improved:
- Storage keys and values are make more sense.
- The message/result/context objects are shown with all relevant fields.
- `call` trace is split into entry/exit, so these can be shown around the
called contract's operations, instead of only showing the `call` parameters
after the nested call is finished.
- Nested calls are indented, which helps to highlight the flow.
- C stack usage considerably reduced in nested calls when more functionality
is enabled (either tracing here, or other things to come).
This will seem like a minor patch, but C stack usage was the real motivation,
after plenty of time in the debugger.
Nobody cares about stack when `showTxCalls` (you can just use a big stack when
debugging). But these subtle changes around the `call` path were found to be
necessary for passing all tests when the EVMC nested call code is completed,
and that's a prerequisite for many things: async EVM, dynamic EVM, Beam Sync,
and to fix https://github.com/status-im/nimbus-eth1/issues/345.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-08-09 14:54:38 +00:00
|
|
|
template call(host: TransactionHost, msg: EvmcMessage): EvmcResult =
|
|
|
|
# `call` is special. The C stack usage must be kept small for deeply nested
|
|
|
|
# EVM calls. To ensure small stack, `{.show.}` must be handled at
|
|
|
|
# `host_call_nested`, not here, and this function must use `template` to
|
|
|
|
# inline at Nim level (same for `callEvmcNested`). `{.inline.}` is not good
|
|
|
|
# enough. Due to object return it ends up using a lot more stack.
|
|
|
|
host.callEvmcNested(msg)
|
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-05-20 19:08:36 +00:00
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getTxContext(host: TransactionHost): EvmcTxContext {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
if not host.cachedTxContext:
|
|
|
|
host.setupTxContext()
|
|
|
|
return host.txContext
|
|
|
|
|
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-05-21 05:24:09 +00:00
|
|
|
proc getBlockHash(host: TransactionHost, number: HostBlockNumber): HostHash {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
# TODO: Clean up the different messy block number types.
|
2024-06-14 07:31:08 +00:00
|
|
|
host.vmState.getAncestorHash(number.BlockNumber)
|
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-05-20 19:08:36 +00:00
|
|
|
|
|
|
|
proc emitLog(host: TransactionHost, address: HostAddress,
|
|
|
|
data: ptr byte, data_size: HostSize,
|
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-05-21 05:24:09 +00:00
|
|
|
topics: ptr HostTopic, topics_count: HostSize) {.show.} =
|
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-05-20 19:08:36 +00:00
|
|
|
var log: Log
|
|
|
|
# Note, this assumes the EVM ensures `data_size` and `topics_count` cannot be
|
|
|
|
# unreasonably large values. Largest `topics_count` should be 4 according to
|
|
|
|
# EVMC documentation, but we won't restrict it here.
|
|
|
|
if topics_count > 0:
|
|
|
|
let topicsArray = cast[ptr UncheckedArray[HostTopic]](topics)
|
|
|
|
let count = topics_count.int
|
|
|
|
log.topics = newSeq[Topic](count)
|
|
|
|
for i in 0 ..< count:
|
|
|
|
log.topics[i] = topicsArray[i]
|
|
|
|
|
2021-05-24 08:53:53 +00:00
|
|
|
if (data_size > 0):
|
|
|
|
log.data = newSeq[byte](data_size.int)
|
|
|
|
copyMem(log.data[0].addr, data, data_size.int)
|
|
|
|
|
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-05-20 19:08:36 +00:00
|
|
|
log.address = address
|
2024-02-20 07:16:12 +00:00
|
|
|
host.vmState.stateDB.addLogEntry(log)
|
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-05-20 19:08:36 +00:00
|
|
|
|
2021-08-05 01:52:40 +00:00
|
|
|
proc accessAccount(host: TransactionHost, address: HostAddress): EvmcAccessStatus {.show.} =
|
|
|
|
host.vmState.mutateStateDB:
|
|
|
|
if not db.inAccessList(address):
|
|
|
|
db.accessList(address)
|
|
|
|
return EVMC_ACCESS_COLD
|
|
|
|
else:
|
|
|
|
return EVMC_ACCESS_WARM
|
|
|
|
|
|
|
|
proc accessStorage(host: TransactionHost, address: HostAddress,
|
|
|
|
key: HostKey): EvmcAccessStatus {.show.} =
|
|
|
|
host.vmState.mutateStateDB:
|
|
|
|
if not db.inAccessList(address, key):
|
|
|
|
db.accessList(address, key)
|
|
|
|
return EVMC_ACCESS_COLD
|
|
|
|
else:
|
|
|
|
return EVMC_ACCESS_WARM
|
|
|
|
|
2023-08-18 02:27:44 +00:00
|
|
|
proc getTransientStorage(host: TransactionHost,
|
|
|
|
address: HostAddress, key: HostKey): HostValue {.show.} =
|
|
|
|
host.vmState.readOnlyStateDB.getTransientStorage(address, key)
|
|
|
|
|
|
|
|
proc setTransientStorage(host: TransactionHost, address: HostAddress,
|
|
|
|
key: HostKey, newVal: HostValue) {.show.} =
|
|
|
|
host.vmState.mutateStateDB:
|
|
|
|
db.setTransientStorage(address, key, newVal)
|
|
|
|
|
2021-05-20 09:15:00 +00:00
|
|
|
when use_evmc_glue:
|
|
|
|
{.pop: inline.}
|
2024-02-20 07:16:12 +00:00
|
|
|
const included_from_host_services {.used.} = true
|
2021-05-20 09:15:00 +00:00
|
|
|
include ./evmc_host_glue
|
|
|
|
else:
|
|
|
|
export
|
|
|
|
accountExists, getStorage, storage, getBalance, getCodeSize, getCodeHash,
|
|
|
|
copyCode, selfDestruct, getTxContext, call, getBlockHash, emitLog
|