nimbus-eth1/nimbus/vm/evmc_api.nim
Jamie Lokier 74f53c7761
EVMC: Add missing EIP-2929 (Berlin) functions to EVMC host
The update for London (EIP-1559) in 1cdb30df ("bump nim-emvc with evmc revision
8.0.0 to 9.0.0") really bumped EVMC ABI version from 7.5 up to 9.

In other words, it skipped Berlin, going direct from Istanbul to London.

That was accompanied by EVMC changes in 05e9b891 ("EIP-3198: add baseFee op
code in nim-evm"), which added the API changes needed for London.

But the missing Berlin functions weren't added in the move to London.

As a result, our EVMC host became incompatible with Berlin, London, and really
all revisions of the ABI, and if a third party EVM was loaded, it crashed.

This commit adds the missing Berlin host support, and makes our ABI
binary-compatible with real EVMC again.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-08-11 19:47:34 +07:00

162 lines
7.4 KiB
Nim

# Nimbus
# Copyright (c) 2019 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.
import evmc/evmc, ./evmc_helpers, eth/common, ../constants
type
# we are not using EVMC original signature here
# because we want to trick the compiler
# and reduce unnecessary conversion/typecast
# TODO: move this type definition to nim-evmc
# after we have implemented ABI compatibility test
# TODO: investigate the possibility to use Big Endian VMWord
# directly if it's not involving stint computation
# and we can reduce unecessary conversion further
nimbus_tx_context* = object
tx_gas_price* : evmc_uint256be # The transaction gas price.
tx_origin* : EthAddress # The transaction origin account.
block_coinbase* : EthAddress # The miner of the block.
block_number* : int64 # The block number.
block_timestamp* : int64 # The block timestamp.
block_gas_limit* : int64 # The block gas limit.
block_difficulty*: evmc_uint256be # The block difficulty.
chain_id* : evmc_uint256be # The blockchain's ChainID.
block_base_fee* : evmc_uint256be # The block base fee.
nimbus_message* = object
kind*: evmc_call_kind
flags*: uint32
depth*: int32
gas*: int64
destination*: EthAddress
sender*: EthAddress
input_data*: ptr byte
input_size*: uint
value*: evmc_uint256be
create2_salt*: evmc_bytes32
nimbus_result* = object
status_code*: evmc_status_code
gas_left*: int64
output_data*: ptr byte
output_size*: uint
release*: proc(result: var nimbus_result) {.cdecl, gcsafe.}
create_address*: EthAddress
padding*: array[4, byte]
nimbus_host_interface* = object
account_exists*: proc(context: evmc_host_context, address: EthAddress): bool {.cdecl, gcsafe.}
get_storage*: proc(context: evmc_host_context, address: EthAddress, key: ptr evmc_uint256be): evmc_uint256be {.cdecl, gcsafe.}
set_storage*: proc(context: evmc_host_context, address: EthAddress,
key, value: ptr evmc_uint256be): evmc_storage_status {.cdecl, gcsafe.}
get_balance*: proc(context: evmc_host_context, address: EthAddress): evmc_uint256be {.cdecl, gcsafe.}
get_code_size*: proc(context: evmc_host_context, address: EthAddress): uint {.cdecl, gcsafe.}
get_code_hash*: proc(context: evmc_host_context, address: EthAddress): Hash256 {.cdecl, gcsafe.}
copy_code*: proc(context: evmc_host_context, address: EthAddress,
code_offset: int, buffer_data: ptr byte,
buffer_size: int): int {.cdecl, gcsafe.}
selfdestruct*: proc(context: evmc_host_context, address, beneficiary: EthAddress) {.cdecl, gcsafe.}
call*: proc(context: evmc_host_context, msg: ptr nimbus_message): nimbus_result {.cdecl, gcsafe.}
get_tx_context*: proc(context: evmc_host_context): nimbus_tx_context {.cdecl, gcsafe.}
get_block_hash*: proc(context: evmc_host_context, number: int64): Hash256 {.cdecl, gcsafe.}
emit_log*: proc(context: evmc_host_context, address: EthAddress,
data: ptr byte, data_size: uint,
topics: ptr evmc_bytes32, topics_count: uint) {.cdecl, gcsafe.}
access_account*: proc(context: evmc_host_context,
address: EthAddress): evmc_access_status {.cdecl, gcsafe.}
access_storage*: proc(context: evmc_host_context, address: EthAddress,
key: var evmc_bytes32): evmc_access_status {.cdecl, gcsafe.}
proc nim_host_get_interface*(): ptr nimbus_host_interface {.importc, cdecl.}
proc nim_host_create_context*(vmstate: pointer, msg: ptr evmc_message): evmc_host_context {.importc, cdecl.}
proc nim_host_destroy_context*(ctx: evmc_host_context) {.importc, cdecl.}
proc nim_create_nimbus_vm*(): ptr evmc_vm {.importc, cdecl.}
type
HostContext* = object
host*: ptr nimbus_host_interface
context*: evmc_host_context
proc init*(x: var HostContext, host: ptr nimbus_host_interface, context: evmc_host_context) =
x.host = host
x.context = context
proc init*(x: typedesc[HostContext], host: ptr nimbus_host_interface, context: evmc_host_context): HostContext =
result.init(host, context)
proc getTxContext*(ctx: HostContext): nimbus_tx_context {.inline.} =
ctx.host.get_tx_context(ctx.context)
proc getBlockHash*(ctx: HostContext, number: Uint256): Hash256 =
let
blockNumber = ctx.getTxContext().block_number.u256
ancestorDepth = blockNumber - number - 1
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH:
return
if number >= blockNumber:
return
ctx.host.get_block_hash(ctx.context, number.truncate(int64))
proc accountExists*(ctx: HostContext, address: EthAddress): bool {.inline.} =
ctx.host.account_exists(ctx.context, address)
proc getStorage*(ctx: HostContext, address: EthAddress, key: Uint256): Uint256 =
var key = toEvmc(key)
Uint256.fromEvmc ctx.host.get_storage(ctx.context, address, key.addr)
proc setStorage*(ctx: HostContext, address: EthAddress,
key, value: Uint256): evmc_storage_status {.inline.} =
var
key = toEvmc(key)
value = toEvmc(value)
ctx.host.set_storage(ctx.context, address, key.addr, value.addr)
proc getBalance*(ctx: HostContext, address: EthAddress): Uint256 {.inline.} =
Uint256.fromEvmc ctx.host.get_balance(ctx.context, address)
proc getCodeSize*(ctx: HostContext, address: EthAddress): uint {.inline.} =
ctx.host.get_code_size(ctx.context, address)
proc getCodeHash*(ctx: HostContext, address: EthAddress): Hash256 {.inline.} =
ctx.host.get_code_hash(ctx.context, address)
proc copyCode*(ctx: HostContext, address: EthAddress, codeOffset: int = 0): seq[byte] =
let size = ctx.getCodeSize(address).int
if size - codeOffset > 0:
result = newSeq[byte](size - codeOffset)
let read = ctx.host.copy_code(ctx.context, address,
codeOffset, result[0].addr, result.len)
doAssert(read == result.len)
proc selfdestruct*(ctx: HostContext, address, beneficiary: EthAddress) {.inline.} =
ctx.host.selfdestruct(ctx.context, address, beneficiary)
proc emitLog*(ctx: HostContext, address: EthAddress, data: openArray[byte],
topics: ptr evmc_bytes32, topicsCount: int) {.inline.} =
ctx.host.emit_log(ctx.context, address, if data.len > 0: data[0].unsafeAddr else: nil,
data.len.uint, topics, topicsCount.uint)
proc call*(ctx: HostContext, msg: nimbus_message): nimbus_result {.inline.} =
ctx.host.call(ctx.context, msg.unsafeAddr)
proc accessAccount*(ctx: HostContext,
address: EthAddress): evmc_access_status {.inline.} =
ctx.host.access_account(ctx.context, address)
proc accessStorage*(ctx: HostContext, address: EthAddress,
key: Uint256): evmc_access_status {.inline.} =
var key = toEvmc(key)
ctx.host.access_storage(ctx.context, address, key)
#proc vmHost*(vmState: BaseVMState, gasPrice: GasInt, origin: EthAddress): HostContext =
# let host = nim_host_get_interface()
# let context = nim_host_create_context(cast[pointer](vmState), gasPrice, toEvmc(origin))
# result.init(host, context)
#
#proc destroy*(hc: HostContext) =
# nim_host_destroy_context(hc.context)